@chrysb/alphaclaw 0.8.8 → 0.8.10

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 (31) hide show
  1. package/bin/alphaclaw.js +43 -174
  2. package/lib/public/css/tailwind.generated.css +1 -1
  3. package/lib/public/dist/app.bundle.js +2089 -2109
  4. package/lib/public/js/app.js +0 -3
  5. package/lib/public/js/components/gateway.js +3 -6
  6. package/lib/public/js/components/general/index.js +0 -2
  7. package/lib/public/js/components/onboarding/welcome-form-step.js +4 -29
  8. package/lib/public/js/components/routes/general-route.js +0 -2
  9. package/lib/public/js/components/routes/watchdog-route.js +0 -2
  10. package/lib/public/js/components/sidebar.js +7 -20
  11. package/lib/public/js/components/update-modal.js +1 -2
  12. package/lib/public/js/components/watchdog-tab/index.js +0 -2
  13. package/lib/public/js/components/welcome/index.js +0 -1
  14. package/lib/public/js/components/welcome/use-welcome.js +2 -52
  15. package/lib/public/js/hooks/use-app-shell-controller.js +9 -37
  16. package/lib/public/js/lib/api.js +0 -36
  17. package/lib/server/alphaclaw-version.js +128 -37
  18. package/lib/server/gateway.js +14 -32
  19. package/lib/server/init/register-server-routes.js +1 -7
  20. package/lib/server/openclaw-version.js +136 -76
  21. package/lib/server/routes/pages.js +1 -9
  22. package/lib/server/routes/system.js +1 -6
  23. package/lib/server/usage-tracker-config.js +3 -27
  24. package/package.json +1 -2
  25. package/lib/public/js/components/update-modal-helpers.js +0 -12
  26. package/lib/release/managed-release.js +0 -180
  27. package/lib/server/alphaclaw-runtime.js +0 -294
  28. package/lib/server/openclaw-runtime.js +0 -428
  29. package/lib/server/package-fingerprint.js +0 -274
  30. package/lib/server/pending-alphaclaw-update.js +0 -85
  31. package/lib/server/pending-openclaw-update.js +0 -86
@@ -1,4 +1,6 @@
1
+ const childProcess = require("child_process");
1
2
  const fs = require("fs");
3
+ const os = require("os");
2
4
  const path = require("path");
3
5
  const https = require("https");
4
6
  const http = require("http");
@@ -6,6 +8,7 @@ const {
6
8
  kLatestVersionCacheTtlMs,
7
9
  kAlphaclawRegistryUrl,
8
10
  kNpmPackageRoot,
11
+ kOpenclawUpdateCopyTimeoutMs,
9
12
  kRootDir,
10
13
  } = require("./constants");
11
14
 
@@ -23,9 +26,6 @@ const isNewerVersion = (latest, current) => {
23
26
  return l.patch > c.patch;
24
27
  };
25
28
 
26
- const buildAlphaclawInstallSpec = (version = "latest") =>
27
- `@chrysb/alphaclaw@${String(version || "").trim() || "latest"}`;
28
-
29
29
  const createAlphaclawVersionService = () => {
30
30
  let kUpdateStatusCache = {
31
31
  latestVersion: null,
@@ -108,6 +108,120 @@ const createAlphaclawVersionService = () => {
108
108
  return { latestVersion, hasUpdate };
109
109
  };
110
110
 
111
+ const findInstallDir = () => {
112
+ // Walk up from kNpmPackageRoot to find the consuming project's directory
113
+ // (the one with node_modules/@chrysb/alphaclaw). In Docker this is /app.
114
+ let dir = kNpmPackageRoot;
115
+ while (dir !== path.dirname(dir)) {
116
+ const parent = path.dirname(dir);
117
+ if (
118
+ path.basename(parent) === "node_modules" ||
119
+ parent.includes(`${path.sep}node_modules${path.sep}`)
120
+ ) {
121
+ dir = parent;
122
+ continue;
123
+ }
124
+ const pkgPath = path.join(parent, "package.json");
125
+ if (fs.existsSync(pkgPath)) {
126
+ try {
127
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
128
+ if (
129
+ pkg.dependencies?.["@chrysb/alphaclaw"] ||
130
+ pkg.devDependencies?.["@chrysb/alphaclaw"] ||
131
+ pkg.optionalDependencies?.["@chrysb/alphaclaw"]
132
+ ) {
133
+ return parent;
134
+ }
135
+ } catch {}
136
+ }
137
+ dir = parent;
138
+ }
139
+ // Fallback: if running directly (not from node_modules), use kNpmPackageRoot
140
+ return kNpmPackageRoot;
141
+ };
142
+
143
+ const installLatestAlphaclaw = () =>
144
+ new Promise((resolve, reject) => {
145
+ const installDir = findInstallDir();
146
+ const tmpDir = fs.mkdtempSync(
147
+ path.join(os.tmpdir(), "alphaclaw-update-"),
148
+ );
149
+
150
+ const cleanup = () => {
151
+ try {
152
+ fs.rmSync(tmpDir, { recursive: true, force: true });
153
+ } catch {}
154
+ };
155
+
156
+ fs.writeFileSync(
157
+ path.join(tmpDir, "package.json"),
158
+ JSON.stringify({
159
+ private: true,
160
+ dependencies: { "@chrysb/alphaclaw": "latest" },
161
+ }),
162
+ );
163
+
164
+ const npmEnv = {
165
+ ...process.env,
166
+ npm_config_update_notifier: "false",
167
+ npm_config_fund: "false",
168
+ npm_config_audit: "false",
169
+ };
170
+
171
+ console.log(
172
+ `[alphaclaw] Running: npm install @chrysb/alphaclaw@latest in temp dir (target: ${installDir})`,
173
+ );
174
+ childProcess.exec(
175
+ "npm install --omit=dev --prefer-online --package-lock=false",
176
+ {
177
+ cwd: tmpDir,
178
+ env: npmEnv,
179
+ timeout: 180000,
180
+ },
181
+ (err, stdout, stderr) => {
182
+ if (err) {
183
+ const message = String(stderr || err.message || "").trim();
184
+ console.log(
185
+ `[alphaclaw] alphaclaw install error: ${message.slice(0, 200)}`,
186
+ );
187
+ cleanup();
188
+ return reject(
189
+ new Error(
190
+ message || "Failed to install @chrysb/alphaclaw@latest",
191
+ ),
192
+ );
193
+ }
194
+ if (stdout?.trim()) {
195
+ console.log(
196
+ `[alphaclaw] alphaclaw install stdout: ${stdout.trim().slice(0, 300)}`,
197
+ );
198
+ }
199
+
200
+ const src = path.join(tmpDir, "node_modules");
201
+ const dest = path.join(installDir, "node_modules");
202
+ childProcess.exec(
203
+ `cp -af "${src}/." "${dest}/"`,
204
+ { timeout: kOpenclawUpdateCopyTimeoutMs },
205
+ (copyErr) => {
206
+ cleanup();
207
+ if (copyErr) {
208
+ console.log(
209
+ `[alphaclaw] alphaclaw copy error: ${(copyErr.message || "").slice(0, 200)}`,
210
+ );
211
+ return reject(
212
+ new Error(
213
+ `Failed to copy updated AlphaClaw files: ${copyErr.message}`,
214
+ ),
215
+ );
216
+ }
217
+ console.log("[alphaclaw] alphaclaw install completed");
218
+ resolve({ stdout: stdout?.trim(), stderr: stderr?.trim() });
219
+ },
220
+ );
221
+ },
222
+ );
223
+ });
224
+
111
225
  const isContainer = () =>
112
226
  process.env.RAILWAY_ENVIRONMENT ||
113
227
  process.env.RENDER ||
@@ -126,15 +240,9 @@ const createAlphaclawVersionService = () => {
126
240
  // On bare metal / Mac / Linux, spawn a replacement process then exit.
127
241
  console.log("[alphaclaw] Spawning new process and exiting...");
128
242
  const { spawn } = require("child_process");
129
- const nextEnv = { ...process.env };
130
- delete nextEnv.ALPHACLAW_MANAGED_RUNTIME_ACTIVE;
131
- const bootstrapCliPath =
132
- String(process.env.ALPHACLAW_BOOTSTRAP_CLI_PATH || "").trim() ||
133
- process.argv[1];
134
- const child = spawn(process.argv[0], [bootstrapCliPath, ...process.argv.slice(2)], {
243
+ const child = spawn(process.argv[0], process.argv.slice(1), {
135
244
  detached: true,
136
245
  stdio: "inherit",
137
- env: nextEnv,
138
246
  });
139
247
  child.unref();
140
248
  process.exit(0);
@@ -169,33 +277,18 @@ const createAlphaclawVersionService = () => {
169
277
  kUpdateInProgress = true;
170
278
  const previousVersion = readAlphaclawVersion();
171
279
  try {
172
- let targetVersion = "latest";
280
+ await installLatestAlphaclaw();
281
+ // Write marker to persistent volume so the update survives container recreation
282
+ const markerPath = path.join(kRootDir, ".alphaclaw-update-pending");
173
283
  try {
174
- const updateStatus = await readAlphaclawUpdateStatus({ refresh: true });
175
- if (updateStatus.latestVersion) {
176
- targetVersion = updateStatus.latestVersion;
177
- }
178
- } catch (error) {
179
- console.log(
180
- `[alphaclaw] Could not resolve exact AlphaClaw version before restart: ${error.message || "unknown error"}`,
284
+ fs.writeFileSync(
285
+ markerPath,
286
+ JSON.stringify({ from: previousVersion, ts: Date.now() }),
181
287
  );
288
+ console.log(`[alphaclaw] Update marker written to ${markerPath}`);
289
+ } catch (e) {
290
+ console.log(`[alphaclaw] Could not write update marker: ${e.message}`);
182
291
  }
183
-
184
- const spec = buildAlphaclawInstallSpec(targetVersion);
185
- // Write marker to persistent volume so the update survives container recreation
186
- const markerPath = path.join(kRootDir, ".alphaclaw-update-pending");
187
- fs.writeFileSync(
188
- markerPath,
189
- JSON.stringify({
190
- from: previousVersion,
191
- to: targetVersion,
192
- spec,
193
- ts: Date.now(),
194
- }),
195
- );
196
- console.log(
197
- `[alphaclaw] Update marker written to ${markerPath} for ${spec}`,
198
- );
199
292
  kUpdateStatusCache = {
200
293
  latestVersion: null,
201
294
  hasUpdate: false,
@@ -206,17 +299,15 @@ const createAlphaclawVersionService = () => {
206
299
  body: {
207
300
  ok: true,
208
301
  previousVersion,
209
- targetVersion: targetVersion === "latest" ? null : targetVersion,
210
302
  restarting: true,
211
303
  },
212
304
  };
213
305
  } catch (err) {
306
+ kUpdateInProgress = false;
214
307
  return {
215
308
  status: 500,
216
309
  body: { ok: false, error: err.message || "Failed to update AlphaClaw" },
217
310
  };
218
- } finally {
219
- kUpdateInProgress = false;
220
311
  }
221
312
  };
222
313
 
@@ -2,7 +2,6 @@ const path = require("path");
2
2
  const { spawn, execSync } = require("child_process");
3
3
  const fs = require("fs");
4
4
  const net = require("net");
5
- const { getManagedOpenclawBinPath } = require("./openclaw-runtime");
6
5
  const {
7
6
  ALPHACLAW_DIR,
8
7
  OPENCLAW_DIR,
@@ -49,16 +48,6 @@ const gatewayEnv = () => ({
49
48
  XDG_CONFIG_HOME: OPENCLAW_DIR,
50
49
  });
51
50
 
52
- const shellQuote = (value) =>
53
- `'${String(value || "").replace(/'/g, `'\"'\"'`)}'`;
54
-
55
- const getOpenclawCommandPath = () => {
56
- const managedBinPath = getManagedOpenclawBinPath();
57
- return fs.existsSync(managedBinPath) ? managedBinPath : "openclaw";
58
- };
59
-
60
- const getOpenclawCommandPrefix = () => shellQuote(getOpenclawCommandPath());
61
-
62
51
  const writeOnboardingMarker = (reason) => {
63
52
  fs.mkdirSync(ALPHACLAW_DIR, { recursive: true });
64
53
  fs.writeFileSync(
@@ -130,10 +119,9 @@ const isGatewayRunning = () =>
130
119
  });
131
120
 
132
121
  const runGatewayCmd = (cmd) => {
133
- const openclawCommandPrefix = getOpenclawCommandPrefix();
134
- console.log(`[alphaclaw] Running: ${openclawCommandPrefix} gateway ${cmd}`);
122
+ console.log(`[alphaclaw] Running: openclaw gateway ${cmd}`);
135
123
  try {
136
- const out = execSync(`${openclawCommandPrefix} gateway ${cmd}`, {
124
+ const out = execSync(`openclaw gateway ${cmd}`, {
137
125
  env: gatewayEnv(),
138
126
  timeout: 15000,
139
127
  encoding: "utf8",
@@ -158,7 +146,7 @@ const launchGatewayProcess = () => {
158
146
  return gatewayChild;
159
147
  }
160
148
  gatewayStderrTail = [];
161
- const child = spawn(getOpenclawCommandPath(), ["gateway", "run"], {
149
+ const child = spawn("openclaw", ["gateway", "run"], {
162
150
  env: gatewayEnv(),
163
151
  stdio: ["pipe", "pipe", "pipe"],
164
152
  });
@@ -318,7 +306,7 @@ const syncChannelConfig = (savedVars, mode = "all") => {
318
306
  const appToken = savedMap[def.extraEnvKeys?.[0]];
319
307
  if (!appToken) continue;
320
308
  execSync(
321
- `${getOpenclawCommandPrefix()} channels add --channel slack --bot-token "${token}" --app-token "${appToken}"`,
309
+ `openclaw channels add --channel slack --bot-token "${token}" --app-token "${appToken}"`,
322
310
  { env, timeout: 15000, encoding: "utf8" },
323
311
  );
324
312
  let raw = fs.readFileSync(configPath, "utf8");
@@ -330,14 +318,11 @@ const syncChannelConfig = (savedVars, mode = "all") => {
330
318
  }
331
319
  fs.writeFileSync(configPath, raw);
332
320
  } else {
333
- execSync(
334
- `${getOpenclawCommandPrefix()} channels add --channel ${ch} --token "${token}"`,
335
- {
336
- env,
337
- timeout: 15000,
338
- encoding: "utf8",
339
- },
340
- );
321
+ execSync(`openclaw channels add --channel ${ch} --token "${token}"`, {
322
+ env,
323
+ timeout: 15000,
324
+ encoding: "utf8",
325
+ });
341
326
  const raw = fs.readFileSync(configPath, "utf8");
342
327
  if (raw.includes(token)) {
343
328
  fs.writeFileSync(
@@ -359,14 +344,11 @@ const syncChannelConfig = (savedVars, mode = "all") => {
359
344
  ) {
360
345
  console.log(`[alphaclaw] Removing channel: ${ch}`);
361
346
  try {
362
- execSync(
363
- `${getOpenclawCommandPrefix()} channels remove --channel ${ch} --delete`,
364
- {
365
- env,
366
- timeout: 15000,
367
- encoding: "utf8",
368
- },
369
- );
347
+ execSync(`openclaw channels remove --channel ${ch} --delete`, {
348
+ env,
349
+ timeout: 15000,
350
+ encoding: "utf8",
351
+ });
370
352
  console.log(`[alphaclaw] Channel ${ch} removed`);
371
353
  } catch (e) {
372
354
  console.error(
@@ -90,13 +90,7 @@ const registerServerRoutes = ({
90
90
  loginThrottle,
91
91
  });
92
92
 
93
- registerPageRoutes({
94
- app,
95
- requireAuth,
96
- isGatewayRunning,
97
- alphaclawVersionService,
98
- openclawVersionService,
99
- });
93
+ registerPageRoutes({ app, requireAuth, isGatewayRunning });
100
94
  registerModelRoutes({
101
95
  app,
102
96
  shellCmd,
@@ -1,13 +1,14 @@
1
- const { execFileSync } = require("child_process");
1
+ const { exec, execSync } = require("child_process");
2
2
  const fs = require("fs");
3
+ const os = require("os");
3
4
  const path = require("path");
4
5
  const {
5
6
  kVersionCacheTtlMs,
6
7
  kLatestVersionCacheTtlMs,
7
- kRootDir,
8
+ kNpmPackageRoot,
9
+ kOpenclawUpdateCopyTimeoutMs,
8
10
  } = require("./constants");
9
11
  const { normalizeOpenclawVersion } = require("./helpers");
10
- const { getManagedOpenclawBinPath } = require("./openclaw-runtime");
11
12
  const { parseJsonObjectFromNoisyOutput } = require("./utils/json");
12
13
 
13
14
  const createOpenclawVersionService = ({
@@ -23,14 +24,6 @@ const createOpenclawVersionService = ({
23
24
  };
24
25
  let kOpenclawUpdateInProgress = false;
25
26
 
26
- const buildOpenclawInstallSpec = (version = "latest") =>
27
- `openclaw@${String(version || "").trim() || "latest"}`;
28
-
29
- const getOpenclawCommandPath = () => {
30
- const managedBinPath = getManagedOpenclawBinPath();
31
- return fs.existsSync(managedBinPath) ? managedBinPath : "openclaw";
32
- };
33
-
34
27
  const readOpenclawVersion = () => {
35
28
  const now = Date.now();
36
29
  if (
@@ -40,9 +33,9 @@ const createOpenclawVersionService = ({
40
33
  return kOpenclawVersionCache.value;
41
34
  }
42
35
  try {
43
- const raw = execFileSync(getOpenclawCommandPath(), ["--version"], {
36
+ const raw = execSync("openclaw --version", {
44
37
  env: gatewayEnv(),
45
- timeout: 10000,
38
+ timeout: 5000,
46
39
  encoding: "utf8",
47
40
  }).trim();
48
41
  const version = normalizeOpenclawVersion(raw);
@@ -66,16 +59,11 @@ const createOpenclawVersionService = ({
66
59
  };
67
60
  }
68
61
  try {
69
- const raw = execFileSync(
70
- getOpenclawCommandPath(),
71
- ["update", "status", "--json"],
72
- {
62
+ const raw = execSync("openclaw update status --json", {
73
63
  env: gatewayEnv(),
74
- timeout: 30000,
75
- maxBuffer: 4 * 1024 * 1024,
76
- encoding: "utf8",
77
- },
78
- ).trim();
64
+ timeout: 8000,
65
+ encoding: "utf8",
66
+ }).trim();
79
67
  const parsed = parseJsonObjectFromNoisyOutput(raw);
80
68
  if (!parsed) {
81
69
  throw new Error("openclaw update status returned invalid JSON payload");
@@ -99,6 +87,118 @@ const createOpenclawVersionService = ({
99
87
  }
100
88
  };
101
89
 
90
+ const findInstallDir = () => {
91
+ // Resolve the consumer app root (for example /app in Docker), not this package directory.
92
+ let dir = kNpmPackageRoot;
93
+ while (dir !== path.dirname(dir)) {
94
+ const parent = path.dirname(dir);
95
+ if (
96
+ path.basename(parent) === "node_modules" ||
97
+ parent.includes(`${path.sep}node_modules${path.sep}`)
98
+ ) {
99
+ dir = parent;
100
+ continue;
101
+ }
102
+ const pkgPath = path.join(parent, "package.json");
103
+ if (fs.existsSync(pkgPath)) {
104
+ try {
105
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
106
+ if (
107
+ pkg.dependencies?.["@chrysb/alphaclaw"] ||
108
+ pkg.devDependencies?.["@chrysb/alphaclaw"] ||
109
+ pkg.optionalDependencies?.["@chrysb/alphaclaw"]
110
+ ) {
111
+ return parent;
112
+ }
113
+ } catch {}
114
+ }
115
+ dir = parent;
116
+ }
117
+ return kNpmPackageRoot;
118
+ };
119
+
120
+ // Install to a temp directory, then copy into the real node_modules.
121
+ // Running `npm install` directly in the app dir causes EBUSY on Docker
122
+ // because npm tries to rename directories that the running process holds open.
123
+ // Copying individual files (cp -af) avoids the rename syscall entirely.
124
+ const installLatestOpenclaw = () =>
125
+ new Promise((resolve, reject) => {
126
+ const installDir = findInstallDir();
127
+ const tmpDir = fs.mkdtempSync(
128
+ path.join(os.tmpdir(), "openclaw-update-"),
129
+ );
130
+ const cleanup = () => {
131
+ try {
132
+ fs.rmSync(tmpDir, { recursive: true, force: true });
133
+ } catch {}
134
+ };
135
+
136
+ fs.writeFileSync(
137
+ path.join(tmpDir, "package.json"),
138
+ JSON.stringify({
139
+ private: true,
140
+ dependencies: { openclaw: "latest" },
141
+ }),
142
+ );
143
+
144
+ const npmEnv = {
145
+ ...process.env,
146
+ npm_config_update_notifier: "false",
147
+ npm_config_fund: "false",
148
+ npm_config_audit: "false",
149
+ };
150
+
151
+ console.log(
152
+ `[alphaclaw] Running: npm install openclaw@latest in temp dir (target: ${installDir})`,
153
+ );
154
+ exec(
155
+ "npm install --omit=dev --prefer-online --package-lock=false",
156
+ { cwd: tmpDir, env: npmEnv, timeout: 180000 },
157
+ (installErr, stdout, stderr) => {
158
+ if (installErr) {
159
+ const message = String(stderr || installErr.message || "").trim();
160
+ console.log(
161
+ `[alphaclaw] openclaw install error: ${message.slice(0, 200)}`,
162
+ );
163
+ cleanup();
164
+ return reject(
165
+ new Error(message || "Failed to install openclaw@latest"),
166
+ );
167
+ }
168
+ if (stdout?.trim()) {
169
+ console.log(
170
+ `[alphaclaw] openclaw install stdout: ${stdout.trim().slice(0, 300)}`,
171
+ );
172
+ }
173
+
174
+ const src = path.join(tmpDir, "node_modules");
175
+ const dest = path.join(installDir, "node_modules");
176
+ exec(
177
+ `cp -af "${src}/." "${dest}/"`,
178
+ { timeout: kOpenclawUpdateCopyTimeoutMs },
179
+ (cpErr) => {
180
+ cleanup();
181
+ if (cpErr) {
182
+ console.log(
183
+ `[alphaclaw] openclaw copy error: ${(cpErr.message || "").slice(0, 200)}`,
184
+ );
185
+ return reject(
186
+ new Error(
187
+ `Failed to copy updated openclaw files: ${cpErr.message}`,
188
+ ),
189
+ );
190
+ }
191
+ console.log("[alphaclaw] openclaw install completed");
192
+ resolve({
193
+ stdout: stdout?.trim() || "",
194
+ stderr: stderr?.trim() || "",
195
+ });
196
+ },
197
+ );
198
+ },
199
+ );
200
+ });
201
+
102
202
  const getVersionStatus = async (refresh) => {
103
203
  const currentVersion = readOpenclawVersion();
104
204
  try {
@@ -128,67 +228,27 @@ const createOpenclawVersionService = ({
128
228
  kOpenclawUpdateInProgress = true;
129
229
  const previousVersion = readOpenclawVersion();
130
230
  try {
131
- let latestVersion = null;
132
- let hasUpdate = false;
133
- try {
134
- const updateStatus = readOpenclawUpdateStatus({ refresh: true });
135
- latestVersion = updateStatus.latestVersion || null;
136
- hasUpdate = !!updateStatus.hasUpdate;
137
- } catch (error) {
138
- console.log(
139
- `[alphaclaw] Could not resolve exact OpenClaw version before restart: ${error.message || "unknown error"}`,
140
- );
141
- }
142
-
143
- if (!hasUpdate && latestVersion && latestVersion === previousVersion) {
144
- return {
145
- status: 200,
146
- body: {
147
- ok: true,
148
- previousVersion,
149
- currentVersion: previousVersion,
150
- latestVersion,
151
- hasUpdate: false,
152
- restarted: false,
153
- restarting: false,
154
- updated: false,
155
- },
156
- };
231
+ await installLatestOpenclaw();
232
+ kOpenclawVersionCache = { value: null, fetchedAt: 0 };
233
+ const currentVersion = readOpenclawVersion();
234
+ const { latestVersion, hasUpdate } = readOpenclawUpdateStatus({
235
+ refresh: true,
236
+ });
237
+ let restarted = false;
238
+ if (isOnboarded()) {
239
+ restartGateway();
240
+ restarted = true;
157
241
  }
158
-
159
- const targetVersion = latestVersion || "latest";
160
- const spec = buildOpenclawInstallSpec(targetVersion);
161
- const markerPath = path.join(kRootDir, ".openclaw-update-pending");
162
- fs.writeFileSync(
163
- markerPath,
164
- JSON.stringify({
165
- from: previousVersion,
166
- to: targetVersion,
167
- spec,
168
- ts: Date.now(),
169
- }),
170
- );
171
- console.log(
172
- `[alphaclaw] OpenClaw update marker written to ${markerPath} for ${spec}`,
173
- );
174
- kOpenclawVersionCache = { value: previousVersion, fetchedAt: 0 };
175
- kOpenclawUpdateStatusCache = {
176
- latestVersion,
177
- hasUpdate,
178
- fetchedAt: 0,
179
- };
180
242
  return {
181
243
  status: 200,
182
244
  body: {
183
245
  ok: true,
184
246
  previousVersion,
185
- currentVersion: previousVersion,
186
- targetVersion: targetVersion === "latest" ? null : targetVersion,
247
+ currentVersion,
187
248
  latestVersion,
188
- hasUpdate: true,
189
- restarted: false,
190
- restarting: true,
191
- updated: previousVersion !== targetVersion,
249
+ hasUpdate,
250
+ restarted,
251
+ updated: previousVersion !== currentVersion,
192
252
  },
193
253
  };
194
254
  } catch (err) {
@@ -1,19 +1,11 @@
1
1
  const path = require("path");
2
2
 
3
- const registerPageRoutes = ({
4
- app,
5
- requireAuth,
6
- isGatewayRunning,
7
- alphaclawVersionService,
8
- openclawVersionService,
9
- }) => {
3
+ const registerPageRoutes = ({ app, requireAuth, isGatewayRunning }) => {
10
4
  app.get("/health", async (req, res) => {
11
5
  const running = await isGatewayRunning();
12
6
  res.json({
13
7
  status: running ? "healthy" : "starting",
14
8
  gateway: running ? "running" : "starting",
15
- alphaclawVersion: alphaclawVersionService?.readAlphaclawVersion?.() || null,
16
- openclawVersion: openclawVersionService?.readOpenclawVersion?.() || null,
17
9
  });
18
10
  });
19
11
 
@@ -588,12 +588,7 @@ const registerSystemRoutes = ({
588
588
  console.log(
589
589
  `[alphaclaw] /api/openclaw/update result: status=${result.status} ok=${result.body?.ok === true}`,
590
590
  );
591
- if (result.status === 200 && result.body?.ok && result.body?.restarting) {
592
- res.json(result.body);
593
- setTimeout(() => alphaclawVersionService.restartProcess(), 1000);
594
- } else {
595
- res.status(result.status).json(result.body);
596
- }
591
+ res.status(result.status).json(result.body);
597
592
  });
598
593
 
599
594
  app.get("/api/alphaclaw/version", async (req, res) => {
@@ -8,27 +8,6 @@ const kUsageTrackerPluginPath = path.resolve(
8
8
  "usage-tracker",
9
9
  );
10
10
 
11
- const normalizePluginPath = (value = "") =>
12
- String(value || "")
13
- .trim()
14
- .replace(/\\/g, "/")
15
- .replace(/\/+$/, "");
16
-
17
- const isUsageTrackerPluginPath = (value = "") => {
18
- const normalizedValue = normalizePluginPath(value);
19
- const normalizedCanonicalPath = normalizePluginPath(kUsageTrackerPluginPath);
20
- if (!normalizedValue) return false;
21
- if (
22
- normalizedValue === normalizedCanonicalPath ||
23
- normalizedValue.startsWith(`${normalizedCanonicalPath}/`)
24
- ) {
25
- return true;
26
- }
27
- return /(?:^|\/)(?:node_modules\/@chrysb\/alphaclaw\/lib|lib)\/plugin\/usage-tracker(?:\/.*)?$/.test(
28
- normalizedValue,
29
- );
30
- };
31
-
32
11
  const ensurePluginsShell = (cfg = {}) => {
33
12
  if (!cfg.plugins || typeof cfg.plugins !== "object") cfg.plugins = {};
34
13
  if (!Array.isArray(cfg.plugins.allow)) cfg.plugins.allow = [];
@@ -53,11 +32,9 @@ const ensurePluginAllowed = ({ cfg = {}, pluginKey = "" }) => {
53
32
  const ensureUsageTrackerPluginEntry = (cfg = {}) => {
54
33
  const before = JSON.stringify(cfg);
55
34
  ensurePluginAllowed({ cfg, pluginKey: "usage-tracker" });
56
- const nextPaths = cfg.plugins.load.paths.filter(
57
- (entry) => !isUsageTrackerPluginPath(entry),
58
- );
59
- nextPaths.push(kUsageTrackerPluginPath);
60
- cfg.plugins.load.paths = nextPaths;
35
+ if (!cfg.plugins.load.paths.includes(kUsageTrackerPluginPath)) {
36
+ cfg.plugins.load.paths.push(kUsageTrackerPluginPath);
37
+ }
61
38
  cfg.plugins.entries["usage-tracker"] = {
62
39
  ...(cfg.plugins.entries["usage-tracker"] &&
63
40
  typeof cfg.plugins.entries["usage-tracker"] === "object"
@@ -87,7 +64,6 @@ const ensureUsageTrackerPluginConfig = ({ fsModule, openclawDir }) => {
87
64
 
88
65
  module.exports = {
89
66
  kUsageTrackerPluginPath,
90
- isUsageTrackerPluginPath,
91
67
  ensurePluginsShell,
92
68
  ensurePluginAllowed,
93
69
  ensureUsageTrackerPluginEntry,