@chrysb/alphaclaw 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/bin/alphaclaw.js +79 -0
  2. package/lib/public/css/shell.css +57 -2
  3. package/lib/public/css/theme.css +184 -0
  4. package/lib/public/js/app.js +330 -89
  5. package/lib/public/js/components/action-button.js +92 -0
  6. package/lib/public/js/components/channels.js +16 -7
  7. package/lib/public/js/components/confirm-dialog.js +25 -19
  8. package/lib/public/js/components/credentials-modal.js +32 -23
  9. package/lib/public/js/components/device-pairings.js +15 -2
  10. package/lib/public/js/components/envars.js +22 -65
  11. package/lib/public/js/components/features.js +1 -1
  12. package/lib/public/js/components/gateway.js +139 -32
  13. package/lib/public/js/components/global-restart-banner.js +31 -0
  14. package/lib/public/js/components/google.js +9 -9
  15. package/lib/public/js/components/icons.js +19 -0
  16. package/lib/public/js/components/info-tooltip.js +18 -0
  17. package/lib/public/js/components/loading-spinner.js +32 -0
  18. package/lib/public/js/components/modal-shell.js +42 -0
  19. package/lib/public/js/components/models.js +34 -29
  20. package/lib/public/js/components/onboarding/welcome-form-step.js +45 -32
  21. package/lib/public/js/components/onboarding/welcome-pairing-step.js +2 -2
  22. package/lib/public/js/components/onboarding/welcome-setup-step.js +7 -24
  23. package/lib/public/js/components/page-header.js +13 -0
  24. package/lib/public/js/components/pairings.js +15 -2
  25. package/lib/public/js/components/providers.js +216 -142
  26. package/lib/public/js/components/scope-picker.js +1 -1
  27. package/lib/public/js/components/secret-input.js +1 -0
  28. package/lib/public/js/components/telegram-workspace.js +37 -49
  29. package/lib/public/js/components/toast.js +34 -5
  30. package/lib/public/js/components/toggle-switch.js +25 -0
  31. package/lib/public/js/components/update-action-button.js +13 -53
  32. package/lib/public/js/components/watchdog-tab.js +312 -0
  33. package/lib/public/js/components/webhooks.js +981 -0
  34. package/lib/public/js/components/welcome.js +2 -1
  35. package/lib/public/js/lib/api.js +102 -1
  36. package/lib/public/js/lib/model-config.js +0 -5
  37. package/lib/server/alphaclaw-version.js +5 -3
  38. package/lib/server/constants.js +33 -0
  39. package/lib/server/discord-api.js +48 -0
  40. package/lib/server/gateway.js +64 -4
  41. package/lib/server/log-writer.js +102 -0
  42. package/lib/server/onboarding/github.js +21 -1
  43. package/lib/server/openclaw-version.js +2 -6
  44. package/lib/server/restart-required-state.js +86 -0
  45. package/lib/server/routes/auth.js +9 -4
  46. package/lib/server/routes/proxy.js +12 -14
  47. package/lib/server/routes/system.js +61 -15
  48. package/lib/server/routes/telegram.js +17 -48
  49. package/lib/server/routes/watchdog.js +68 -0
  50. package/lib/server/routes/webhooks.js +214 -0
  51. package/lib/server/telegram-api.js +11 -0
  52. package/lib/server/watchdog-db.js +148 -0
  53. package/lib/server/watchdog-notify.js +93 -0
  54. package/lib/server/watchdog.js +585 -0
  55. package/lib/server/webhook-middleware.js +195 -0
  56. package/lib/server/webhooks-db.js +265 -0
  57. package/lib/server/webhooks.js +238 -0
  58. package/lib/server.js +119 -4
  59. package/lib/setup/core-prompts/AGENTS.md +84 -0
  60. package/lib/setup/core-prompts/TOOLS.md +13 -0
  61. package/lib/setup/core-prompts/UI-DRY-OPPORTUNITIES.md +50 -0
  62. package/lib/setup/gitignore +2 -0
  63. package/package.json +2 -1
package/lib/server.js CHANGED
@@ -5,6 +5,11 @@ const path = require("path");
5
5
  const fs = require("fs");
6
6
 
7
7
  const constants = require("./server/constants");
8
+ const { initLogWriter, readLogTail } = require("./server/log-writer");
9
+ initLogWriter({
10
+ rootDir: constants.kRootDir,
11
+ maxBytes: constants.kLogMaxBytes,
12
+ });
8
13
  const {
9
14
  parseJsonFromNoisyOutput,
10
15
  normalizeOnboardingModels,
@@ -18,7 +23,26 @@ const {
18
23
  readGoogleCredentials,
19
24
  getClientKey,
20
25
  } = require("./server/helpers");
21
- const { readEnvFile, writeEnvFile, reloadEnv, startEnvWatcher } = require("./server/env");
26
+ const {
27
+ initWebhooksDb,
28
+ insertRequest,
29
+ getRequests,
30
+ getRequestById,
31
+ getHookSummaries,
32
+ deleteRequestsByHook,
33
+ } = require("./server/webhooks-db");
34
+ const {
35
+ initWatchdogDb,
36
+ insertWatchdogEvent,
37
+ getRecentEvents,
38
+ } = require("./server/watchdog-db");
39
+ const { createWebhookMiddleware } = require("./server/webhook-middleware");
40
+ const {
41
+ readEnvFile,
42
+ writeEnvFile,
43
+ reloadEnv,
44
+ startEnvWatcher,
45
+ } = require("./server/env");
22
46
  const {
23
47
  gatewayEnv,
24
48
  isOnboarded,
@@ -29,14 +53,23 @@ const {
29
53
  ensureGatewayProxyConfig,
30
54
  syncChannelConfig,
31
55
  getChannelStatus,
56
+ launchGatewayProcess,
57
+ setGatewayExitHandler,
58
+ setGatewayLaunchHandler,
32
59
  } = require("./server/gateway");
33
60
  const { createCommands } = require("./server/commands");
34
61
  const { createAuthProfiles } = require("./server/auth-profiles");
35
62
  const { createLoginThrottle } = require("./server/login-throttle");
36
63
  const { createOpenclawVersionService } = require("./server/openclaw-version");
37
64
  const { createAlphaclawVersionService } = require("./server/alphaclaw-version");
65
+ const {
66
+ createRestartRequiredState,
67
+ } = require("./server/restart-required-state");
38
68
  const { syncBootstrapPromptFiles } = require("./server/onboarding/workspace");
39
69
  const { createTelegramApi } = require("./server/telegram-api");
70
+ const { createDiscordApi } = require("./server/discord-api");
71
+ const { createWatchdogNotifier } = require("./server/watchdog-notify");
72
+ const { createWatchdog } = require("./server/watchdog");
40
73
 
41
74
  const { registerAuthRoutes } = require("./server/routes/auth");
42
75
  const { registerPageRoutes } = require("./server/routes/pages");
@@ -48,6 +81,8 @@ const { registerCodexRoutes } = require("./server/routes/codex");
48
81
  const { registerGoogleRoutes } = require("./server/routes/google");
49
82
  const { registerProxyRoutes } = require("./server/routes/proxy");
50
83
  const { registerTelegramRoutes } = require("./server/routes/telegram");
84
+ const { registerWebhookRoutes } = require("./server/routes/webhooks");
85
+ const { registerWatchdogRoutes } = require("./server/routes/watchdog");
51
86
 
52
87
  const { PORT, GATEWAY_URL, kTrustProxyHops, SETUP_API_PREFIXES } = constants;
53
88
 
@@ -56,6 +91,7 @@ attachGatewaySignalHandlers();
56
91
 
57
92
  const app = express();
58
93
  app.set("trust proxy", kTrustProxyHops);
94
+ app.use(["/webhook", "/hooks"], express.raw({ type: "*/*", limit: "5mb" }));
59
95
  app.use(express.json());
60
96
 
61
97
  const proxy = httpProxy.createProxyServer({
@@ -73,6 +109,20 @@ proxy.on("error", (err, req, res) => {
73
109
  const authProfiles = createAuthProfiles();
74
110
  const loginThrottle = { ...createLoginThrottle(), getClientKey };
75
111
  const { shellCmd, clawCmd, gogCmd } = createCommands({ gatewayEnv });
112
+ const resolveSetupUrl = () => {
113
+ const explicit =
114
+ process.env.ALPHACLAW_SETUP_URL ||
115
+ process.env.ALPHACLAW_BASE_URL ||
116
+ process.env.RENDER_EXTERNAL_URL ||
117
+ process.env.URL;
118
+ if (explicit) return String(explicit).trim();
119
+ if (process.env.RAILWAY_STATIC_URL) {
120
+ const domain = String(process.env.RAILWAY_STATIC_URL).trim();
121
+ if (!domain) return "";
122
+ return domain.startsWith("http") ? domain : `https://${domain}`;
123
+ }
124
+ return "";
125
+ };
76
126
  const restartGateway = () => restartGatewayWithReload(reloadEnv);
77
127
  const openclawVersionService = createOpenclawVersionService({
78
128
  gatewayEnv,
@@ -80,12 +130,26 @@ const openclawVersionService = createOpenclawVersionService({
80
130
  isOnboarded,
81
131
  });
82
132
  const alphaclawVersionService = createAlphaclawVersionService();
133
+ const restartRequiredState = createRestartRequiredState({ isGatewayRunning });
83
134
 
84
135
  const { requireAuth, isAuthorizedRequest } = registerAuthRoutes({
85
136
  app,
86
137
  loginThrottle,
87
138
  });
88
139
  app.use(express.static(path.join(__dirname, "public")));
140
+ initWebhooksDb({
141
+ rootDir: constants.kRootDir,
142
+ pruneDays: constants.kWebhookPruneDays,
143
+ });
144
+ initWatchdogDb({
145
+ rootDir: constants.kRootDir,
146
+ pruneDays: constants.kWatchdogLogRetentionDays,
147
+ });
148
+ const webhookMiddleware = createWebhookMiddleware({
149
+ gatewayUrl: constants.GATEWAY_URL,
150
+ insertRequest,
151
+ maxPayloadBytes: constants.kMaxPayloadBytes,
152
+ });
89
153
 
90
154
  registerPageRoutes({ app, requireAuth, isGatewayRunning });
91
155
  registerModelRoutes({
@@ -128,7 +192,9 @@ registerSystemRoutes({
128
192
  alphaclawVersionService,
129
193
  clawCmd,
130
194
  restartGateway,
195
+ onExpectedGatewayRestart: () => watchdog.onExpectedRestart(),
131
196
  OPENCLAW_DIR: constants.OPENCLAW_DIR,
197
+ restartRequiredState,
132
198
  });
133
199
  registerPairingRoutes({ app, clawCmd, isOnboarded });
134
200
  registerCodexRoutes({
@@ -149,14 +215,62 @@ registerGoogleRoutes({
149
215
  constants,
150
216
  });
151
217
  const telegramApi = createTelegramApi(() => process.env.TELEGRAM_BOT_TOKEN);
218
+ const discordApi = createDiscordApi(() => process.env.DISCORD_BOT_TOKEN);
219
+ const watchdogNotifier = createWatchdogNotifier({ telegramApi, discordApi });
220
+ const watchdog = createWatchdog({
221
+ clawCmd,
222
+ launchGatewayProcess,
223
+ insertWatchdogEvent,
224
+ notifier: watchdogNotifier,
225
+ readEnvFile,
226
+ writeEnvFile,
227
+ reloadEnv,
228
+ resolveSetupUrl,
229
+ });
230
+ setGatewayExitHandler((payload) => watchdog.onGatewayExit(payload));
231
+ setGatewayLaunchHandler((payload) => watchdog.onGatewayLaunch(payload));
152
232
  const doSyncPromptFiles = () =>
153
233
  syncBootstrapPromptFiles({ fs, workspaceDir: constants.WORKSPACE_DIR });
154
- registerTelegramRoutes({ app, telegramApi, syncPromptFiles: doSyncPromptFiles });
155
- registerProxyRoutes({ app, proxy, SETUP_API_PREFIXES, requireAuth });
234
+ registerTelegramRoutes({
235
+ app,
236
+ telegramApi,
237
+ syncPromptFiles: doSyncPromptFiles,
238
+ });
239
+ registerWebhookRoutes({
240
+ app,
241
+ fs,
242
+ constants,
243
+ getBaseUrl,
244
+ shellCmd,
245
+ webhooksDb: {
246
+ getRequests,
247
+ getRequestById,
248
+ getHookSummaries,
249
+ deleteRequestsByHook,
250
+ },
251
+ restartRequiredState,
252
+ });
253
+ registerWatchdogRoutes({
254
+ app,
255
+ requireAuth,
256
+ watchdog,
257
+ getRecentEvents,
258
+ readLogTail,
259
+ });
260
+ registerProxyRoutes({
261
+ app,
262
+ proxy,
263
+ SETUP_API_PREFIXES,
264
+ requireAuth,
265
+ webhookMiddleware,
266
+ });
156
267
 
157
268
  const server = http.createServer(app);
158
269
  server.on("upgrade", (req, socket, head) => {
159
- const requestUrl = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
270
+ const requestUrl = new URL(
271
+ req.url || "/",
272
+ `http://${req.headers.host || "localhost"}`,
273
+ );
160
274
  if (requestUrl.pathname.startsWith("/openclaw")) {
161
275
  const upgradeReq = {
162
276
  ...req,
@@ -182,6 +296,7 @@ server.listen(PORT, "0.0.0.0", () => {
182
296
  syncChannelConfig(readEnvFile());
183
297
  ensureGatewayProxyConfig(null);
184
298
  startGateway();
299
+ watchdog.start();
185
300
  } else {
186
301
  console.log("[alphaclaw] Awaiting onboarding via Setup UI");
187
302
  }
@@ -31,3 +31,87 @@ Use workspace-relative paths only for local files (no absolute paths). Include a
31
31
  Changes committed ([abc1234](commit url)): <-- linked commit hash
32
32
  • path/or/resource (new|edit|delete) — brief description
33
33
  ```
34
+
35
+ ### Telegram Notice Format (AlphaClaw)
36
+
37
+ Use this format for any Telegram notices sent from AlphaClaw services (watchdog, system alerts, repair notices):
38
+
39
+ 1. Header line (Markdown): `🐺 *AlphaClaw Watchdog*`
40
+ 2. Headline line (simple, no `Status:` prefix):
41
+ - `🔴 Crash loop detected`
42
+ - `🔴 Crash loop detected, auto-repairing...`
43
+ - `🟡 Auto-repair started, awaiting health check`
44
+ - `🟢 Auto-repair complete, gateway healthy`
45
+ - `🟢 Gateway healthy again`
46
+ - `🔴 Auto-repair failed`
47
+ 3. Append a markdown link to the headline when URL is available:
48
+ - `... - [View logs](<full-url>/#/watchdog)`
49
+ 4. Optional context lines like `Trigger: ...`, `Attempt count: ...`
50
+ 5. For values with underscores or special characters (for example `crash_loop`), wrap the value in backticks:
51
+ - `Trigger: \`crash_loop\``
52
+ 6. Do not use HTML tags (`<b>`, `<a href>`) for Telegram watchdog notices.
53
+
54
+ ### UI Conventions
55
+
56
+ Use these conventions for all UI work under `lib/public/js` and `lib/public/css`.
57
+
58
+ #### Component structure
59
+
60
+ - Use arrow-function components and helpers.
61
+ - Prefer shared components over one-off markup when a pattern already exists.
62
+ - Keep constants in `kName` format (e.g. `kUiTabs`, `kGroupOrder`, `kNamePattern`).
63
+ - Keep component-level helpers near the top of the file, before the main export.
64
+
65
+ #### Rendering and composition
66
+
67
+ - Use the `htm` + `preact` pattern:
68
+ - `const html = htm.bind(h);`
69
+ - return `html\`...\``
70
+ - Prefer early return for hidden states (e.g. `if (!visible) return null;`).
71
+ - Use `<PageHeader />` for tab/page headers that need a title and right-side actions.
72
+ - Use card shells consistently: `bg-surface border border-border rounded-xl`.
73
+
74
+ #### Buttons
75
+
76
+ - Primary actions: `ac-btn-cyan`
77
+ - Secondary actions: `ac-btn-secondary`
78
+ - Positive/success actions: `ac-btn-green`
79
+ - Ghost/text actions: `ac-btn-ghost` (use for low-emphasis actions like "Disconnect" or "Add provider")
80
+ - Destructive inline actions: `ac-btn-danger`
81
+ - Use consistent disabled treatment: `opacity-50 cursor-not-allowed`.
82
+ - Keep action sizing consistent (`text-xs px-3 py-1.5 rounded-lg` for compact controls unless there is a clear reason otherwise).
83
+ - For `<PageHeader />` actions, use `ac-btn-cyan` (primary) or `ac-btn-secondary` (secondary) by default; avoid ghost/text-only styling for main header actions.
84
+ - Prefer shared action components when available (`ActionButton`, `UpdateActionButton`, `ConfirmDialog`) before custom button logic.
85
+ - In setup/onboarding auth flows (e.g. Codex OAuth), prefer `<ActionButton />` over raw `<button>` for consistency in tone, sizing, and loading behavior.
86
+ - 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.
87
+
88
+ #### Dialogs and modals
89
+
90
+ - Use `<ConfirmDialog />` for destructive/confirmation flows.
91
+ - Use `<ModalShell />` for non-confirm custom modals that need shared overlay and Escape handling.
92
+ - Modal overlay convention:
93
+ - `fixed inset-0 bg-black/70 flex items-center justify-center p-4 z-50`
94
+ - Modal panel convention:
95
+ - `bg-modal border border-border rounded-xl p-5 ...`
96
+ - Support close-on-overlay click and Escape key for dialogs.
97
+
98
+ #### Inputs and forms
99
+
100
+ - Reuse `<SecretInput />` for sensitive values and token/key inputs.
101
+ - Base input look should remain consistent:
102
+ - `bg-black/30 border border-border rounded-lg ... focus:border-gray-500`
103
+ - Preserve monospace for technical values (`font-mono`) and codes/paths.
104
+ - Prefer inline helper text under fields (`text-xs text-gray-500/600`) for setup guidance.
105
+
106
+ #### Feedback and state
107
+
108
+ - Use `showToast(...)` for user-visible operation outcomes.
109
+ - Prefer semantic toast levels (`success`, `error`, `warning`, `info`) at callsites. Legacy color aliases are only for backwards compatibility.
110
+ - Keep toast positioning relative to the active page container (not the viewport) when layout banners can shift content.
111
+ - Keep loading/saving flags explicit in state (`saving`, `creating`, `restartingGateway`, etc.).
112
+ - Reuse `<LoadingSpinner />` for loading indicators instead of inline spinner SVG markup.
113
+ - Use `<Badge />` for compact status chips (e.g. connected/not connected) instead of one-off status span styling.
114
+ - Use polling via `usePolling` for frequently refreshed backend-backed data.
115
+ - For restart-required flows, render the standardized yellow restart banner style used in `providers`, `envars`, and `webhooks`.
116
+
117
+ For inconsistencies tracking and DRY opportunities, see `lib/setup/core-prompts/UI-DRY-OPPORTUNITIES.md`.
@@ -11,6 +11,7 @@ AlphaClaw UI: `{{SETUP_UI_URL}}`
11
11
  | General | `{{SETUP_UI_URL}}#general` | Gateway status & restart, channel health (Telegram/Discord), pending pairings, feature health (Embeddings/Audio), Google Workspace connection, repo auto-sync schedule, OpenClaw dashboard |
12
12
  | Providers | `{{SETUP_UI_URL}}#providers` | AI provider credentials (Anthropic, OpenAI, Gemini, Mistral, Voyage, Groq, Deepgram), feature capabilities, Codex OAuth |
13
13
  | Envars | `{{SETUP_UI_URL}}#envars` | View/edit/add environment variables (saved to `/data/.env`), gateway restart to apply changes |
14
+ | Webhooks | `{{SETUP_UI_URL}}#webhooks` | Webhook endpoint visibility, create flow, request history, and gateway delivery debugging |
14
15
 
15
16
  ### Environment variables
16
17
 
@@ -34,3 +35,15 @@ After pushing, include a link to the commit using the abbreviated hash: [abc1234
34
35
  ## Telegram Formatting
35
36
 
36
37
  - **Links:** Use markdown syntax `[text](URL)` — HTML `<a href>` does NOT render
38
+
39
+ ## Webhooks
40
+
41
+ You can create webhooks yourself or the user can create them through the Setup UI.
42
+
43
+ Webhook transform files must follow this convention:
44
+
45
+ - Path: hooks/transforms/{hook-name}/{hook-name}-transform.mjs
46
+ - Signature: export default async function transform(payload, context)
47
+ - Webhook data is at payload.payload (nested)
48
+ - Never create transform files outside of hooks/transforms/
49
+ - When modifying a transform, read the existing file first
@@ -0,0 +1,50 @@
1
+ ### Prioritized Implementation Order
2
+
3
+ 1. **[P0 | S] Unify toast type mapping in one place so callers consistently use semantic levels.**
4
+ - [x] Update `lib/public/js/components/toast.js` (`showToast`, `ToastContainer`) to normalize legacy values.
5
+ - [x] Normalize highest-impact callers first: `lib/public/js/components/providers.js`, `lib/public/js/components/models.js`, `lib/public/js/components/google.js`, `lib/public/js/components/gateway.js`, `lib/public/js/components/envars.js`, `lib/public/js/components/webhooks.js`.
6
+ - Current inconsistency snapshot:
7
+ - Legacy color callers: `lib/public/js/components/providers.js`, `lib/public/js/components/models.js`, `lib/public/js/components/google.js`.
8
+ - Semantic callers: `lib/public/js/components/envars.js`, `lib/public/js/components/webhooks.js`, `lib/public/js/components/gateway.js`, `lib/public/js/components/telegram-workspace.js`.
9
+
10
+ 2. **[P0 | M] Add a shared restart banner component (and/or restart hook) for restart-required flows.**
11
+ - [x] Extract shared banner + handler state from: `lib/public/js/components/envars.js`, `lib/public/js/components/providers.js`, `lib/public/js/components/webhooks.js`.
12
+ - [x] Keep `restartGateway` integration aligned with existing gateway action UI in `lib/public/js/components/gateway.js`.
13
+ - Current inconsistency snapshot:
14
+ - Duplicate banner + state + handler logic in the same 3 files above.
15
+
16
+ 3. **[P1 | S] Add a small shared loading spinner component to replace repeated inline SVG.**
17
+ - [x] Replace inline spinners in: `lib/public/js/app.js`, `lib/public/js/components/providers.js`, `lib/public/js/components/models.js`, `lib/public/js/components/onboarding/welcome-setup-step.js`.
18
+ - [x] Evaluate whether `lib/public/js/components/update-action-button.js` should consume the shared spinner as well.
19
+ - Current inconsistency snapshot:
20
+ - Spinner markup shape and sizing differ slightly by file.
21
+
22
+ 4. **[P1 | M] Standardize button styles around `ac-btn-*` + common disabled treatment.**
23
+ - [x] Convert ad-hoc utility button patterns in: `lib/public/js/components/pairings.js` (Reject), `lib/public/js/components/device-pairings.js` (Reject), `lib/public/js/components/webhooks.js` (Create/Delete), `lib/public/js/components/google.js` (disconnect action), `lib/public/js/components/providers.js` (Codex reconnect/disconnect/restart).
24
+ - [x] Align onboarding variants in: `lib/public/js/components/onboarding/welcome-setup-step.js`, `lib/public/js/components/onboarding/welcome-form-step.js`, `lib/public/js/components/onboarding/welcome-pairing-step.js`.
25
+ - Current inconsistency snapshot:
26
+ - Mixed custom classes vs ad-hoc border utility buttons.
27
+
28
+ 5. **[P1 | S] Normalize disabled opacity usage to one convention.**
29
+ - [x] Standardize `opacity-50` vs `opacity-60` in:
30
+ - `lib/public/js/components/envars.js`
31
+ - `lib/public/js/components/providers.js`
32
+ - `lib/public/js/components/webhooks.js`
33
+ - `lib/public/js/components/telegram-workspace.js`
34
+ - `lib/public/js/components/onboarding/welcome-setup-step.js`
35
+ - `lib/public/js/components/onboarding/welcome-pairing-step.js`
36
+ - Current inconsistency snapshot:
37
+ - `opacity-50` and `opacity-60` both used for disabled states.
38
+
39
+ 6. **[P2 | M] Consider a shared action button wrapper for common save/create/delete states.**
40
+ - [x] Evaluate extraction points in: `lib/public/js/components/envars.js`, `lib/public/js/components/providers.js`, `lib/public/js/components/webhooks.js`, `lib/public/js/components/models.js`, `lib/public/js/components/pairings.js`, `lib/public/js/components/device-pairings.js`.
41
+ - Current inconsistency snapshot:
42
+ - Repeated action-label + loading-label + disabled-guard patterns.
43
+
44
+ 7. **[P2 | M] Extract a shared modal shell wrapper for non-confirm custom modals.**
45
+ - [x] Factor shared overlay/panel shell from:
46
+ - `lib/public/js/components/webhooks.js` (`CreateWebhookModal`)
47
+ - `lib/public/js/components/credentials-modal.js`
48
+ - [x] Keep parity with `lib/public/js/components/confirm-dialog.js` behavior (overlay click + Escape semantics).
49
+ - Current inconsistency snapshot:
50
+ - Same modal shell classes/structure repeated with minor differences.
@@ -6,6 +6,8 @@
6
6
  !workspace/**
7
7
  workspace/.openclaw/
8
8
  workspace/.openclaw/**
9
+ db/
10
+ db/**
9
11
  !skills/
10
12
  !skills/**
11
13
  !cron/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrysb/alphaclaw",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -16,6 +16,7 @@
16
16
  "start": "node bin/alphaclaw.js start",
17
17
  "test": "vitest run",
18
18
  "test:watch": "vitest",
19
+ "test:watchdog": "vitest run tests/server/watchdog.test.js tests/server/watchdog-db.test.js tests/server/routes-watchdog.test.js",
19
20
  "test:coverage": "vitest run --coverage"
20
21
  },
21
22
  "dependencies": {