AlphaClaw is the ops and setup layer around OpenClaw. It provides a browser-based setup UI, gateway lifecycle management, watchdog recovery flows, and integrations (for example Telegram, Discord, Google Workspace, and webhooks) so users can operate OpenClaw without manual server intervention.
If you need to understand the internals of OpenClaw, you can inspect the code at ~/Projects/openclaw/src
bin/alphaclaw.js: CLI entrypoint and lifecycle command surface.lib/server: Express server, authenticated setup APIs, watchdog APIs, channel integrations, and proxying to the OpenClaw gateway.lib/public: Setup UI frontend (component-driven tabs and flows for providers, envars, watchdog, webhooks, and onboarding).lib/setup: Prompt hardening templates and setup-related assets injected into agent/system behavior.
Runtime model:
- AlphaClaw server starts and manages OpenClaw as a child process.
- Setup UI calls AlphaClaw APIs for configuration and operations.
- AlphaClaw proxies gateway traffic and handles watchdog monitoring/repair.
- Node.js 22.14+ runtime.
- Express-based HTTP API server.
http-proxyfor gateway proxy behavior.- OpenClaw CLI/gateway process orchestration.
- Preact +
htmfrontend patterns for Setup UI components. - Vitest + Supertest for server and route testing.
- Keep edits targeted and production-safe; favor small, reviewable changes.
- Preserve existing behavior unless the task explicitly requires behavior changes.
- Follow existing UI conventions and shared components for consistency.
- Reuse existing server route and state patterns before introducing new abstractions.
- Update tests when behavior changes in routes, watchdog flows, or setup state.
- Before running tests in a fresh checkout, run
npm installsovitest(devDependency) is available fornpm test.
- Avoid monolithic implementation files for new features. For new UI areas and new API areas, start with a decomposed structure (focused components/hooks/utilities for UI; focused route modules/services/helpers for server) rather than building one large file first and splitting later.
- When adding a new feature area, follow the existing project patterns from day one (for example feature folders with
index.jsplususe-*hooks in UI, and route + service separation on server) so code stays maintainable as the feature grows. - When continuing to build on a file that is growing large or accumulating unrelated concerns, stop and decompose it before adding more code rather than letting it drift into a monolith.
- Prefer the shared cache primitives in
lib/public/js/lib/api-cache.jsfor backend reads:cachedFetch(...)for imperative fetch paths.getCached(...)/setCached(...)/invalidateCache(...)for cache lifecycle.
- For component-level read requests, prefer
useCachedFetchfromlib/public/js/hooks/use-cached-fetch.jsover ad-hocuseEffect(() => fetchX())mount loads. - Treat the API URL (including query params) as the canonical cache key for GET-style payloads.
- Keep cache in-memory for fast tab switches; do not add persistent storage caching unless explicitly required by product behavior.
- Do not keep route panes mounted via
display:nonejust to preserve data. Prefer conditional rendering + cache-backed remounts. - Use
usePollingfor recurring refreshes and always pass a stablecacheKeywhen poll results should hydrate remounts. - Keep
pauseWhenHiddenbehavior enabled for polling unless a specific flow requires background polling while the browser tab is hidden. - Tune polling intervals conservatively; avoid 1-2s polling unless there is a clear real-time requirement.
- For app-shell status streams, prefer SSE (
/api/events/status) where available and keep polling as fallback behavior. - After write/mutation APIs (POST/PUT/DELETE), refresh or invalidate relevant cached keys so the UI does not show stale data.
- When reading
openclaw.jsonin server code, use the shared helper inlib/server/openclaw-config.js(readOpenclawConfig) instead of ad-hocJSON.parse(fs.readFileSync(...))blocks.
- This file (
AGENTS.md): Project-level guidance for coding agents working on the AlphaClaw codebase — architecture, conventions, release flow, UI patterns, etc. lib/setup/core-prompts/AGENTS.md: Runtime prompt injected into the OpenClaw agent's system prompt. Only write there when the guidance is meant for the deployed agent's behavior, not for coding on this project.
Use this release flow when promoting tested beta builds to production:
- Ensure
mainis clean and synced, and tests pass. - Publish beta iterations as needed:
npm version prerelease --preid=betagit push && git push --tagsnpm publish --tag beta
- Immediately after each beta publish, update
~/Projects/openclaw-railway-templateon thebetabranch to pin the exact beta version inpackage.json(for example0.3.2-beta.4), then commit and push that template change. Do not leave the beta template onlatest, or Docker layer cache can reuse an older install. - When ready for production, publish a stable release version (for example
0.3.2):npm version 0.3.2git push && git push --tagsnpm publish(publishes tolatest)
- Return templates to production channel:
@chrysb/alphaclaw: "latest"
- Optionally keep beta branch/tag flows active for next release cycle.
AlphaClaw currently expects Express 4 semantics in its setup API layer. A broken container dependency tree can accidentally resolve express@5 at /app/node_modules/express, which causes subtle request handling regressions (for example body parsing behavior on certain methods).
Known root cause pattern:
- Mutating
/app/node_modulesin-place (for example copy-over installs used for emergency package swaps) can leave the runtime tree inconsistent with/app/package.json. - This can hoist
express@5to the app root, sorequire("express")inside AlphaClaw resolves the wrong major version.
Preferred fix/recovery:
- Ensure template
package.jsonpins the intended@chrysb/alphaclawversion. - Rebuild the
openclawcontainer from scratch (no cache) and recreate it:docker compose downdocker compose build --no-cache openclawdocker compose up -d openclaw
- Verify runtime resolution inside the container:
node -p "require('express/package.json').version"should be4.xnpm ls expressshould show@chrysb/alphaclawonexpress@4.x(OpenClaw can still carry its ownexpress@5subtree).
Use this format for any Telegram notices sent from AlphaClaw services (watchdog, system alerts, repair notices):
- Header line (Markdown):
🐺 *AlphaClaw Watchdog* - Headline line (simple, no
Status:prefix):🔴 Crash loop detected🔴 Crash loop detected, auto-repairing...🟡 Auto-repair started, awaiting health check🟢 Auto-repair complete, gateway healthy🟢 Gateway healthy again🔴 Auto-repair failed
- Append a markdown link to the headline when URL is available:
... - [View logs](<full-url>/#/watchdog)
- Optional context lines like
Trigger: ...,Attempt count: ... - For values with underscores or special characters (for example
crash_loop), wrap the value in backticks:Trigger: \crash_loop``
- Do not use HTML tags (
<b>,<a href>) for Telegram watchdog notices.
Use these conventions for all UI work under lib/public/js and lib/public/css.
- The browser loads the compiled bundle under
lib/public/dist/(for exampleapp.bundle.jsand chunk files), produced byscripts/build-ui.mjs(esbuild). - After any UI source change that should ship in production (
lib/public/js,lib/public/css, or other inputs to the build), runnpm run build:uisolib/public/dist/stays in sync. Verify the app in the browser against the rebuilt bundle when the change is non-trivial. npm publishrunsprepack→npm run build:ui, so published packages always include a fresh bundle. Local installs, Docker builds from a git checkout, or commits that includedist/still requirenpm run build:uiwhen you change UI sources and expect the built assets to match.
- Use arrow-function components and helpers.
- Prefer shared components over one-off markup when a pattern already exists.
- Keep constants in
kNameformat (e.g.kUiTabs,kGroupOrder,kNamePattern). - Keep component-level helpers near the top of the file, before the main export.
- Treat
index.jsas a presentational shell whenever possible: keep business logic in hooks and pass derived state/actions down as props. - Add reusable SVG icons to
lib/public/js/components/icons.jsand import them from there; avoid introducing one-off inline SVGs in feature files when a shared icon component can be used.
- Use the
htm+preactpattern:const html = htm.bind(h);- return
html\...``
- In
htmtemplates, be explicit with inline spacing around styled inline tags (<span>,<code>,<a>): use${" "}where needed, and verify rendered copy so words never collapse (eventsand) or gain double spaces. - Prefer early return for hidden states (e.g.
if (!visible) return null;). - Use
<PageHeader />for tab/page headers that need a title and right-side actions. - Use card shells consistently:
bg-surface border border-border rounded-xl. - For nested "surface on surface" blocks (content inside a
bg-surfacecard), useac-surface-insetfor the inner container treatment so inset sections match shared history/sessions styling. - For internal section dividers, use
border-t border-border(avoid opacity variants) with comfortable vertical spacing around the divider.
- Prefer semantic Tailwind color utilities backed by theme tokens (
text-body,text-fg-muted,text-fg-dim,bg-field,bg-status-error-bg,border-status-warning-border) instead of raw palette classes liketext-gray-300orbg-red-900/30. - When a new reusable UI color role is needed, add the CSS variable in
lib/public/css/theme.cssand expose it throughtailwind.config.cjsrather than introducing one-off hardcoded color classes in components. - Keep component refactors token-based so future theme changes stay centralized in the token layer instead of requiring per-component color rewrites.
- Primary actions:
ac-btn-cyan - Secondary actions:
ac-btn-secondary - Positive/success actions:
ac-btn-green - Ghost/text actions:
ac-btn-ghost(use for low-emphasis actions like "Disconnect" or "Add provider") - Destructive inline actions:
ac-btn-danger - Use consistent disabled treatment:
opacity-50 cursor-not-allowed. - Keep action sizing consistent (
text-xs px-3 py-1.5 rounded-lgfor compact controls unless there is a clear reason otherwise). - For
<PageHeader />actions, useac-btn-cyan(primary) orac-btn-secondary(secondary) by default; avoid ghost/text-only styling for main header actions. - Prefer shared action components when available (
ActionButton,UpdateActionButton,ConfirmDialog) before custom button logic. - In setup/onboarding auth flows (e.g. Codex OAuth), prefer
<ActionButton />over raw<button>for consistency in tone, sizing, and loading behavior. - In setup wizard/multi-step modal footers, use
<ActionButton />for Back/Next/Finish/Done actions (not raw<button>), so loading and tone behavior stays consistent. - In multi-step auth flows, keep the active "finish" action visually primary and demote the "start/restart" action to secondary once the flow has started.
- Use
<ConfirmDialog />for destructive/confirmation flows. - Use
<ModalShell />for non-confirm custom modals that need shared overlay and Escape handling. - Modal overlay convention:
fixed inset-0 bg-black/70 flex items-center justify-center p-4 z-50
- Modal panel convention:
bg-modal border border-border rounded-xl p-5 ...
- Support close-on-overlay click and Escape key for dialogs.
- Reuse
<SecretInput />for sensitive values and token/key inputs. - Reuse
<ToggleSwitch />for boolean on/off controls instead of ad-hoc checkbox/switch markup. - Base input look should remain consistent:
bg-field border border-border rounded-lg ... focus:border-fg-muted
- Preserve monospace for technical values (
font-mono) and codes/paths. - Prefer inline helper text under fields (
text-xs text-fg-muted/text-fg-dim) for setup guidance. - For tip/help links in helper text, use the shared
ac-tip-linkclass (token-backed via--accent-link) instead of per-file ad-hoc cyan classes.
- Use
showToast(...)for user-visible operation outcomes. - Prefer semantic toast levels (
success,error,warning,info) at callsites. Legacy color aliases are only for backwards compatibility. - Keep toast positioning relative to the active page container (not the viewport) when layout banners can shift content.
- For hover help and icon labels, use the shared portal-backed tooltip components (
Tooltip,InfoTooltip) instead of inline absolutely positioned popovers, so tooltips are not clipped by cards, rows, or scroll containers. - Keep loading/saving flags explicit in state (
saving,creating,restartingGateway, etc.). - Reuse
<LoadingSpinner />for loading indicators instead of inline spinner SVG markup. - Use
<Badge />for compact status chips (e.g. connected/not connected) instead of one-off status span styling. - Use polling via
usePollingfor frequently refreshed backend-backed data. - For restart-required flows, render the standardized yellow restart banner style used in
providers,envars, andwebhooks.
- Prefer shared formatter helpers in
lib/public/js/lib/format.jsfor reusable value formatting (formatXstyle helpers such as date/time, currency, integers, and common duration formats). - Before adding a new formatter in a component, check
lib/public/js/lib/format.jsand reuse an existing helper when possible. - Add new formatter helpers to
lib/public/js/lib/format.jswhen the behavior is cross-feature and likely to be reused; keep feature-specific transforms local to the feature folder. - Avoid wrapper pass-through helpers that only rename a global formatter without adding feature-specific behavior.
- Keep shared session-key parsing/filtering helpers in
lib/public/js/lib/session-keys.js(for example extractingagentId, destination-session matching checks, and destination payload derivation). - Before adding session-key logic in a hook/component, check
lib/public/js/lib/session-keys.jsfirst and reuse existing helpers. - When session-key behavior is reused across features, add/extend helpers in
lib/public/js/lib/session-keys.jsinstead of duplicating regex/string parsing in feature files.
- All standalone
localStoragekeys are defined inlib/public/js/lib/storage-keys.js. Import keys from this file — never define raw localStorage key strings inline in components. - Use the naming convention
alphaclaw.<area>.<purpose>for new keys (e.g.alphaclaw.doctor.lastSessionKey). - Keys that live inside the
alphaclaw.ui.settingsJSON blob (e.g.browseLastPath,doctorWarningDismissedUntilMs) are sub-keys, not standalone localStorage entries — those stay in their consuming file.