@chrysb/alphaclaw 0.5.5 → 0.5.6

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.
@@ -199,7 +199,7 @@ export const AppSidebar = ({
199
199
  `,
200
200
  )}
201
201
  <div class="sidebar-footer">
202
- ${acHasUpdate && acLatest
202
+ ${acHasUpdate && acLatest && selectedNavId === "general"
203
203
  ? html`
204
204
  <${UpdateActionButton}
205
205
  onClick=${onAcUpdate}
@@ -250,18 +250,6 @@ export const AppSidebar = ({
250
250
  onSelectFile=${onSelectBrowseFile}
251
251
  isActive=${sidebarTab === "browse"}
252
252
  />
253
- ${acHasUpdate && acLatest
254
- ? html`
255
- <${UpdateActionButton}
256
- onClick=${onAcUpdate}
257
- loading=${acUpdating}
258
- warning=${true}
259
- idleLabel=${`Update to v${acLatest}`}
260
- loadingLabel="Updating..."
261
- className="w-full justify-center"
262
- />
263
- `
264
- : null}
265
253
  </div>
266
254
  </div>
267
255
  </div>
@@ -124,18 +124,8 @@ const createGmailServeManager = ({
124
124
  stdio: ["ignore", "pipe", "pipe"],
125
125
  });
126
126
 
127
- child.stdout.on("data", (chunk) => {
128
- const line = String(chunk || "").trim();
129
- if (line) {
130
- console.log(`[alphaclaw] gmail watch serve (${email}): ${line}`);
131
- }
132
- });
133
- child.stderr.on("data", (chunk) => {
134
- const line = String(chunk || "").trim();
135
- if (line) {
136
- console.log(`[alphaclaw] gmail watch serve stderr (${email}): ${line}`);
137
- }
138
- });
127
+ child.stdout.on("data", () => {});
128
+ child.stderr.on("data", () => {});
139
129
 
140
130
  const nextEntry = {
141
131
  accountId,
@@ -154,5 +154,6 @@ const ensureOpenclawRuntimeArtifacts = ({
154
154
  module.exports = {
155
155
  ensureOpenclawRuntimeArtifacts,
156
156
  installControlUiSkill,
157
+ resolveSetupUiUrl,
157
158
  syncBootstrapPromptFiles,
158
159
  };
@@ -0,0 +1,23 @@
1
+ const runOnboardedBootSequence = ({
2
+ doSyncPromptFiles,
3
+ reloadEnv,
4
+ syncChannelConfig,
5
+ readEnvFile,
6
+ ensureGatewayProxyConfig,
7
+ resolveSetupUrl,
8
+ startGateway,
9
+ watchdog,
10
+ gmailWatchService,
11
+ }) => {
12
+ doSyncPromptFiles();
13
+ reloadEnv();
14
+ syncChannelConfig(readEnvFile());
15
+ ensureGatewayProxyConfig(resolveSetupUrl());
16
+ startGateway();
17
+ watchdog.start();
18
+ gmailWatchService.start();
19
+ };
20
+
21
+ module.exports = {
22
+ runOnboardedBootSequence,
23
+ };
@@ -12,10 +12,15 @@ const kBootstrapHealthCheckMs = 5 * 1000;
12
12
  const kExpectedRestartWindowMs = 15 * 1000;
13
13
 
14
14
  const isTruthy = (value) =>
15
- ["1", "true", "yes", "on"].includes(String(value || "").trim().toLowerCase());
15
+ ["1", "true", "yes", "on"].includes(
16
+ String(value || "")
17
+ .trim()
18
+ .toLowerCase(),
19
+ );
16
20
 
17
21
  const parseHealthResult = (result) => {
18
- if (!result?.ok) return { ok: false, reason: result?.stderr || "health command failed" };
22
+ if (!result?.ok)
23
+ return { ok: false, reason: result?.stderr || "health command failed" };
19
24
  const raw = String(result.stdout || "").trim();
20
25
  if (!raw) return { ok: true };
21
26
  try {
@@ -58,7 +63,9 @@ const createWatchdog = ({
58
63
  repairAttempts: 0,
59
64
  crashTimestamps: [],
60
65
  autoRepair: isTruthy(process.env.WATCHDOG_AUTO_REPAIR),
61
- notificationsDisabled: isTruthy(process.env.WATCHDOG_NOTIFICATIONS_DISABLED),
66
+ notificationsDisabled: isTruthy(
67
+ process.env.WATCHDOG_NOTIFICATIONS_DISABLED,
68
+ ),
62
69
  operationInProgress: false,
63
70
  gatewayStartedAt: null,
64
71
  gatewayPid: null,
@@ -92,7 +99,8 @@ const createWatchdog = ({
92
99
  scheduleDegradedHealthCheck();
93
100
  }
94
101
  }, kWatchdogDegradedCheckIntervalMs);
95
- if (typeof degradedHealthTimer.unref === "function") degradedHealthTimer.unref();
102
+ if (typeof degradedHealthTimer.unref === "function")
103
+ degradedHealthTimer.unref();
96
104
  };
97
105
 
98
106
  const clearExpectedRestartWindow = () => {
@@ -101,7 +109,10 @@ const createWatchdog = ({
101
109
  };
102
110
 
103
111
  const markExpectedRestartWindow = (durationMs = kExpectedRestartWindowMs) => {
104
- const safeDuration = Math.max(5000, Number(durationMs) || kExpectedRestartWindowMs);
112
+ const safeDuration = Math.max(
113
+ 5000,
114
+ Number(durationMs) || kExpectedRestartWindowMs,
115
+ );
105
116
  state.expectedRestartInProgress = true;
106
117
  state.expectedRestartUntilMs = Date.now() + safeDuration;
107
118
  };
@@ -142,13 +153,21 @@ const createWatchdog = ({
142
153
 
143
154
  const trimCrashWindow = () => {
144
155
  const threshold = Date.now() - kWatchdogCrashLoopWindowMs;
145
- state.crashTimestamps = state.crashTimestamps.filter((ts) => ts >= threshold);
156
+ state.crashTimestamps = state.crashTimestamps.filter(
157
+ (ts) => ts >= threshold,
158
+ );
146
159
  };
147
160
 
148
161
  const createCorrelationId = () =>
149
162
  `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
150
163
 
151
- const logEvent = (eventType, source, status, details = null, correlationId = "") => {
164
+ const logEvent = (
165
+ eventType,
166
+ source,
167
+ status,
168
+ details = null,
169
+ correlationId = "",
170
+ ) => {
152
171
  try {
153
172
  insertWatchdogEvent({
154
173
  eventType,
@@ -168,16 +187,25 @@ const createWatchdog = ({
168
187
  }
169
188
  if (!notifier?.notify) return { ok: false, reason: "notifier_unavailable" };
170
189
  const result = await notifier.notify(message);
171
- logEvent("notification", "watchdog", result.ok ? "ok" : "failed", result, correlationId);
190
+ logEvent(
191
+ "notification",
192
+ "watchdog",
193
+ result.ok ? "ok" : "failed",
194
+ result,
195
+ correlationId,
196
+ );
172
197
  return result;
173
198
  };
174
199
 
175
200
  const getWatchdogSetupUrl = () => {
176
201
  try {
177
202
  const base =
178
- typeof resolveSetupUrl === "function" ? String(resolveSetupUrl() || "") : "";
203
+ typeof resolveSetupUrl === "function"
204
+ ? String(resolveSetupUrl() || "")
205
+ : "";
179
206
  if (base) return `${base.replace(/\/+$/, "")}/#/watchdog`;
180
- const fallbackPort = Number.parseInt(String(process.env.PORT || "3000"), 10) || 3000;
207
+ const fallbackPort =
208
+ Number.parseInt(String(process.env.PORT || "3000"), 10) || 3000;
181
209
  return `http://localhost:${fallbackPort}/#/watchdog`;
182
210
  } catch {
183
211
  return "";
@@ -190,7 +218,8 @@ const createWatchdog = ({
190
218
  return `${line} - [View logs](${setupUrl})`;
191
219
  };
192
220
 
193
- const asInlineCode = (value) => `\`${String(value || "").replace(/`/g, "")}\``;
221
+ const asInlineCode = (value) =>
222
+ `\`${String(value || "").replace(/`/g, "")}\``;
194
223
 
195
224
  const notifyAutoRepairOutcome = async ({
196
225
  source,
@@ -225,11 +254,15 @@ const createWatchdog = ({
225
254
  const hasAutoRepair = typeof autoRepair === "boolean";
226
255
  const hasNotificationsEnabled = typeof notificationsEnabled === "boolean";
227
256
  if (!hasAutoRepair && !hasNotificationsEnabled) {
228
- throw new Error("Expected autoRepair and/or notificationsEnabled boolean");
257
+ throw new Error(
258
+ "Expected autoRepair and/or notificationsEnabled boolean",
259
+ );
229
260
  }
230
261
  const envVars = readEnvFile();
231
262
  if (hasAutoRepair) {
232
- const existingIdx = envVars.findIndex((item) => item.key === "WATCHDOG_AUTO_REPAIR");
263
+ const existingIdx = envVars.findIndex(
264
+ (item) => item.key === "WATCHDOG_AUTO_REPAIR",
265
+ );
233
266
  const nextValue = autoRepair ? "true" : "false";
234
267
  if (existingIdx >= 0) {
235
268
  envVars[existingIdx] = { ...envVars[existingIdx], value: nextValue };
@@ -254,7 +287,9 @@ const createWatchdog = ({
254
287
  writeEnvFile(envVars);
255
288
  reloadEnv();
256
289
  state.autoRepair = isTruthy(process.env.WATCHDOG_AUTO_REPAIR);
257
- state.notificationsDisabled = isTruthy(process.env.WATCHDOG_NOTIFICATIONS_DISABLED);
290
+ state.notificationsDisabled = isTruthy(
291
+ process.env.WATCHDOG_NOTIFICATIONS_DISABLED,
292
+ );
258
293
  return getSettings();
259
294
  };
260
295
 
@@ -277,7 +312,13 @@ const createWatchdog = ({
277
312
  const child = launchGatewayProcess();
278
313
  launchedGateway = !!child;
279
314
  if (launchedGateway) {
280
- logEvent("restart", "repair", "ok", { pid: child.pid }, correlationId);
315
+ logEvent(
316
+ "restart",
317
+ "repair",
318
+ "ok",
319
+ { pid: child.pid },
320
+ correlationId,
321
+ );
281
322
  } else {
282
323
  logEvent(
283
324
  "restart",
@@ -288,7 +329,13 @@ const createWatchdog = ({
288
329
  );
289
330
  }
290
331
  } catch (err) {
291
- logEvent("restart", "repair", "failed", { error: err.message }, correlationId);
332
+ logEvent(
333
+ "restart",
334
+ "repair",
335
+ "failed",
336
+ { error: err.message },
337
+ correlationId,
338
+ );
292
339
  }
293
340
  state.health = "unknown";
294
341
  state.lifecycle = "running";
@@ -344,7 +391,10 @@ const createWatchdog = ({
344
391
  source = "health_timer",
345
392
  allowAutoRepair = true,
346
393
  } = {}) => {
347
- if (state.expectedRestartInProgress && Date.now() >= state.expectedRestartUntilMs) {
394
+ if (
395
+ state.expectedRestartInProgress &&
396
+ Date.now() >= state.expectedRestartUntilMs
397
+ ) {
348
398
  clearExpectedRestartWindow();
349
399
  }
350
400
  if (state.operationInProgress && !allowDuringOperation) return false;
@@ -358,7 +408,8 @@ const createWatchdog = ({
358
408
  state.gatewayStartedAt != null &&
359
409
  state.gatewayStartedAt !== gatewayStartedAtAtStart;
360
410
  const restartWindowActive =
361
- state.expectedRestartInProgress && Date.now() < state.expectedRestartUntilMs;
411
+ state.expectedRestartInProgress &&
412
+ Date.now() < state.expectedRestartUntilMs;
362
413
  if (staleAfterRestart) {
363
414
  return false;
364
415
  }
@@ -370,7 +421,8 @@ const createWatchdog = ({
370
421
  clearExpectedRestartWindow();
371
422
  state.health = "healthy";
372
423
  state.lifecycle = "running";
373
- if (!state.uptimeStartedAt || wasUnhealthy) state.uptimeStartedAt = Date.now();
424
+ if (!state.uptimeStartedAt || wasUnhealthy)
425
+ state.uptimeStartedAt = Date.now();
374
426
  state.repairAttempts = 0;
375
427
  state.crashRecoveryActive = false;
376
428
  if (recoveredFromCrashLoop) {
@@ -384,6 +436,13 @@ const createWatchdog = ({
384
436
  },
385
437
  correlationId,
386
438
  );
439
+ await notify(
440
+ [
441
+ "🐺 *AlphaClaw Watchdog*",
442
+ withViewLogsSuffix("🟢 Gateway healthy again"),
443
+ ].join("\n"),
444
+ correlationId,
445
+ );
387
446
  }
388
447
  if (state.pendingRecoveryNoticeSource) {
389
448
  const recoverySource = state.pendingRecoveryNoticeSource;
@@ -392,12 +451,17 @@ const createWatchdog = ({
392
451
  [
393
452
  "🐺 *AlphaClaw Watchdog*",
394
453
  withViewLogsSuffix("🟢 Gateway healthy again"),
395
- `Trigger: ${asInlineCode(recoverySource)}`,
396
454
  ].join("\n"),
397
455
  correlationId,
398
456
  );
399
457
  }
400
- logEvent("health_check", source, "ok", parsed.details || result, correlationId);
458
+ logEvent(
459
+ "health_check",
460
+ source,
461
+ "ok",
462
+ parsed.details || result,
463
+ correlationId,
464
+ );
401
465
  return true;
402
466
  }
403
467
  if (restartWindowActive) {
@@ -445,7 +509,10 @@ const createWatchdog = ({
445
509
 
446
510
  if (state.health === "unknown" && state.lifecycle === "running") {
447
511
  state.startupConsecutiveHealthFailures += 1;
448
- if (state.startupConsecutiveHealthFailures < kWatchdogStartupFailureThreshold) {
512
+ if (
513
+ state.startupConsecutiveHealthFailures <
514
+ kWatchdogStartupFailureThreshold
515
+ ) {
449
516
  logEvent(
450
517
  "health_check",
451
518
  source,
@@ -486,7 +553,13 @@ const createWatchdog = ({
486
553
  try {
487
554
  const child = launchGatewayProcess();
488
555
  if (child) {
489
- logEvent("restart", "exit_event", "ok", { pid: child.pid }, correlationId);
556
+ logEvent(
557
+ "restart",
558
+ "exit_event",
559
+ "ok",
560
+ { pid: child.pid },
561
+ correlationId,
562
+ );
490
563
  } else {
491
564
  logEvent(
492
565
  "restart",
@@ -497,13 +570,24 @@ const createWatchdog = ({
497
570
  );
498
571
  }
499
572
  } catch (err) {
500
- logEvent("restart", "exit_event", "failed", { error: err.message }, correlationId);
573
+ logEvent(
574
+ "restart",
575
+ "exit_event",
576
+ "failed",
577
+ { error: err.message },
578
+ correlationId,
579
+ );
501
580
  } finally {
502
581
  state.operationInProgress = false;
503
582
  }
504
583
  };
505
584
 
506
- const onGatewayExit = ({ code, signal, expectedExit = false, stderrTail = [] } = {}) => {
585
+ const onGatewayExit = ({
586
+ code,
587
+ signal,
588
+ expectedExit = false,
589
+ stderrTail = [],
590
+ } = {}) => {
507
591
  const correlationId = createCorrelationId();
508
592
  clearDegradedHealthCheckTimer();
509
593
  if (expectedExit && (code == null || code === 0)) {
@@ -557,7 +641,9 @@ const createWatchdog = ({
557
641
  ),
558
642
  `Crashes: ${state.crashTimestamps.length} in the last ${Math.floor(kWatchdogCrashLoopWindowMs / 1000)}s`,
559
643
  `Last exit code: ${code ?? "unknown"}`,
560
- ...(state.autoRepair ? [] : ["Auto-restart paused; manual action required."]),
644
+ ...(state.autoRepair
645
+ ? []
646
+ : ["Auto-restart paused; manual action required."]),
561
647
  ].join("\n"),
562
648
  correlationId,
563
649
  );
package/lib/server.js CHANGED
@@ -91,6 +91,7 @@ const {
91
91
  const {
92
92
  ensureOpenclawRuntimeArtifacts,
93
93
  installControlUiSkill,
94
+ resolveSetupUiUrl,
94
95
  syncBootstrapPromptFiles,
95
96
  } = require("./server/onboarding/workspace");
96
97
  const {
@@ -105,6 +106,7 @@ const { createDiscordApi } = require("./server/discord-api");
105
106
  const { createWatchdogNotifier } = require("./server/watchdog-notify");
106
107
  const { createWatchdog } = require("./server/watchdog");
107
108
  const { createDoctorService } = require("./server/doctor/service");
109
+ const { runOnboardedBootSequence } = require("./server/startup");
108
110
 
109
111
  const { registerAuthRoutes } = require("./server/routes/auth");
110
112
  const { registerPageRoutes } = require("./server/routes/pages");
@@ -154,20 +156,13 @@ proxy.on("error", (err, req, res) => {
154
156
  const authProfiles = createAuthProfiles();
155
157
  const loginThrottle = { ...createLoginThrottle(), getClientKey };
156
158
  const { shellCmd, clawCmd, gogCmd } = createCommands({ gatewayEnv });
157
- const resolveSetupUrl = () => {
158
- const explicit =
159
+ const resolveSetupUrl = () =>
160
+ resolveSetupUiUrl(
159
161
  process.env.ALPHACLAW_SETUP_URL ||
160
- process.env.ALPHACLAW_BASE_URL ||
161
- process.env.RENDER_EXTERNAL_URL ||
162
- process.env.URL;
163
- if (explicit) return String(explicit).trim();
164
- if (process.env.RAILWAY_STATIC_URL) {
165
- const domain = String(process.env.RAILWAY_STATIC_URL).trim();
166
- if (!domain) return "";
167
- return domain.startsWith("http") ? domain : `https://${domain}`;
168
- }
169
- return "";
170
- };
162
+ process.env.ALPHACLAW_BASE_URL ||
163
+ process.env.RENDER_EXTERNAL_URL ||
164
+ process.env.URL,
165
+ );
171
166
  const restartGateway = () => restartGatewayWithReload(reloadEnv);
172
167
  const openclawVersionService = createOpenclawVersionService({
173
168
  gatewayEnv,
@@ -415,13 +410,17 @@ server.on("upgrade", (req, socket, head) => {
415
410
  server.listen(PORT, "0.0.0.0", () => {
416
411
  console.log(`[alphaclaw] Express listening on :${PORT}`);
417
412
  if (isOnboarded()) {
418
- doSyncPromptFiles();
419
- reloadEnv();
420
- syncChannelConfig(readEnvFile());
421
- ensureGatewayProxyConfig(null);
422
- startGateway();
423
- watchdog.start();
424
- gmailWatchService.start();
413
+ runOnboardedBootSequence({
414
+ doSyncPromptFiles,
415
+ reloadEnv,
416
+ syncChannelConfig,
417
+ readEnvFile,
418
+ ensureGatewayProxyConfig,
419
+ resolveSetupUrl,
420
+ startGateway,
421
+ watchdog,
422
+ gmailWatchService,
423
+ });
425
424
  } else {
426
425
  console.log("[alphaclaw] Awaiting onboarding via Setup UI");
427
426
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrysb/alphaclaw",
3
- "version": "0.5.5",
3
+ "version": "0.5.6",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },