@chrysb/alphaclaw 0.1.2 → 0.1.4

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.
@@ -12,6 +12,8 @@ import {
12
12
  fetchOnboardStatus,
13
13
  fetchDashboardUrl,
14
14
  updateSyncCron,
15
+ fetchAlphaclawVersion,
16
+ updateAlphaclaw,
15
17
  } from "./lib/api.js";
16
18
  import { usePolling } from "./hooks/usePolling.js";
17
19
  import { Gateway } from "./components/gateway.js";
@@ -240,6 +242,11 @@ const GeneralTab = ({ onSwitchTab }) => {
240
242
  function App() {
241
243
  const [onboarded, setOnboarded] = useState(null);
242
244
  const [tab, setTab] = useState("general");
245
+ const [acVersion, setAcVersion] = useState(null);
246
+ const [acLatest, setAcLatest] = useState(null);
247
+ const [acHasUpdate, setAcHasUpdate] = useState(false);
248
+ const [acUpdating, setAcUpdating] = useState(false);
249
+ const [acDismissed, setAcDismissed] = useState(false);
243
250
 
244
251
  useEffect(() => {
245
252
  fetchOnboardStatus()
@@ -247,6 +254,40 @@ function App() {
247
254
  .catch(() => setOnboarded(false));
248
255
  }, []);
249
256
 
257
+ useEffect(() => {
258
+ if (!onboarded) return;
259
+ let active = true;
260
+ const check = async () => {
261
+ try {
262
+ const data = await fetchAlphaclawVersion(false);
263
+ if (!active) return;
264
+ setAcVersion(data.currentVersion || null);
265
+ setAcLatest(data.latestVersion || null);
266
+ setAcHasUpdate(!!data.hasUpdate);
267
+ } catch {}
268
+ };
269
+ check();
270
+ const id = setInterval(check, 5 * 60 * 1000);
271
+ return () => { active = false; clearInterval(id); };
272
+ }, [onboarded]);
273
+
274
+ const handleAcUpdate = async () => {
275
+ if (acUpdating) return;
276
+ setAcUpdating(true);
277
+ try {
278
+ const data = await updateAlphaclaw();
279
+ if (data.ok) {
280
+ showToast("AlphaClaw updated — restarting...", "success");
281
+ } else {
282
+ showToast(data.error || "AlphaClaw update failed", "error");
283
+ setAcUpdating(false);
284
+ }
285
+ } catch (err) {
286
+ showToast(err.message || "Could not update AlphaClaw", "error");
287
+ setAcUpdating(false);
288
+ }
289
+ };
290
+
250
291
  // Still loading onboard status
251
292
  if (onboarded === null) {
252
293
  return html`
@@ -288,11 +329,37 @@ function App() {
288
329
  <div class="flex items-center gap-3 pb-3">
289
330
  <div class="text-4xl">🦞</div>
290
331
  <div>
291
- <h1 class="text-2xl font-semibold">OpenClaw Setup</h1>
332
+ <h1 class="text-2xl font-semibold">OpenClaw Setup${acVersion ? html`${" "}<span class="text-base font-normal text-gray-600">v${acVersion}</span>` : ""}</h1>
292
333
  <p class="text-gray-500 text-sm">This should be easy, right?</p>
293
334
  </div>
294
335
  </div>
295
336
 
337
+ ${acHasUpdate && acLatest && !acDismissed && html`
338
+ <div class="mb-3 flex items-center justify-between gap-3 rounded-lg border border-yellow-500/30 bg-yellow-500/10 px-3 py-2">
339
+ <p class="text-sm text-yellow-400">
340
+ AlphaClaw <span class="font-medium">v${acLatest}</span> is available
341
+ </p>
342
+ <div class="flex items-center gap-2 shrink-0">
343
+ <button
344
+ onclick=${handleAcUpdate}
345
+ disabled=${acUpdating}
346
+ class="text-xs font-medium px-3 py-1 rounded-lg bg-yellow-500/20 text-yellow-300 hover:bg-yellow-500/30 transition-colors ${acUpdating ? "opacity-50 cursor-not-allowed" : ""}"
347
+ >
348
+ ${acUpdating ? "Updating..." : "Update now"}
349
+ </button>
350
+ <button
351
+ onclick=${() => setAcDismissed(true)}
352
+ class="text-yellow-500/60 hover:text-yellow-400 transition-colors"
353
+ title="Dismiss"
354
+ >
355
+ <svg class="w-4 h-4" viewBox="0 0 20 20" fill="currentColor">
356
+ <path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"/>
357
+ </svg>
358
+ </button>
359
+ </div>
360
+ </div>
361
+ `}
362
+
296
363
  <div class="flex gap-1 border-b border-border">
297
364
  ${["general", "models", "envars"].map(
298
365
  (t) => html`
@@ -9,96 +9,118 @@ import {
9
9
  import { showToast } from "./toast.js";
10
10
  const html = htm.bind(h);
11
11
 
12
- export function Gateway({ status, openclawVersion }) {
13
- const [restarting, setRestarting] = useState(false);
14
- const [checkingUpdate, setCheckingUpdate] = useState(false);
15
- const [currentVersion, setCurrentVersion] = useState(openclawVersion || null);
12
+ function VersionRow({ label, currentVersion, fetchVersion, applyUpdate, tagsUrl }) {
13
+ const [checking, setChecking] = useState(false);
14
+ const [version, setVersion] = useState(currentVersion || null);
16
15
  const [latestVersion, setLatestVersion] = useState(null);
17
16
  const [hasUpdate, setHasUpdate] = useState(false);
18
- const [updateError, setUpdateError] = useState("");
19
- const isRunning = status === "running" && !restarting;
20
- const dotClass = isRunning
21
- ? "w-2 h-2 rounded-full bg-green-500"
22
- : "w-2 h-2 rounded-full bg-yellow-500 animate-pulse";
17
+ const [error, setError] = useState("");
23
18
 
24
19
  useEffect(() => {
25
- setCurrentVersion(openclawVersion || null);
26
- }, [openclawVersion]);
20
+ setVersion(currentVersion || null);
21
+ }, [currentVersion]);
27
22
 
28
23
  useEffect(() => {
29
24
  let active = true;
30
- const loadLatest = async () => {
25
+ const load = async () => {
31
26
  try {
32
- const data = await fetchOpenclawVersion(false);
27
+ const data = await fetchVersion(false);
33
28
  if (!active) return;
34
- setCurrentVersion(data.currentVersion || openclawVersion || null);
29
+ setVersion(data.currentVersion || currentVersion || null);
35
30
  setLatestVersion(data.latestVersion || null);
36
31
  setHasUpdate(!!data.hasUpdate);
37
- setUpdateError(data.ok ? "" : data.error || "");
32
+ setError(data.ok ? "" : data.error || "");
38
33
  } catch (err) {
39
34
  if (!active) return;
40
- setUpdateError(err.message || "Could not check updates");
35
+ setError(err.message || "Could not check updates");
41
36
  }
42
37
  };
43
- loadLatest();
44
- return () => {
45
- active = false;
46
- };
38
+ load();
39
+ return () => { active = false; };
47
40
  }, []);
48
41
 
49
- const handleRestart = async () => {
50
- if (restarting) return;
51
- setRestarting(true);
52
- try {
53
- await restartGateway();
54
- showToast("Gateway restarted", "success");
55
- } catch (err) {
56
- showToast("Restart failed: " + err.message, "error");
57
- }
58
- setRestarting(false);
59
- };
60
-
61
- const handleUpdate = async () => {
62
- if (checkingUpdate) return;
63
- setCheckingUpdate(true);
64
- setUpdateError("");
42
+ const handleAction = async () => {
43
+ if (checking) return;
44
+ setChecking(true);
45
+ setError("");
65
46
  try {
66
47
  const data = hasUpdate
67
- ? await updateOpenclaw()
68
- : await fetchOpenclawVersion(true);
69
- setCurrentVersion(data.currentVersion || currentVersion);
48
+ ? await applyUpdate()
49
+ : await fetchVersion(true);
50
+ setVersion(data.currentVersion || version);
70
51
  setLatestVersion(data.latestVersion || null);
71
52
  setHasUpdate(!!data.hasUpdate);
72
- setUpdateError(data.ok ? "" : data.error || "");
53
+ setError(data.ok ? "" : data.error || "");
73
54
  if (hasUpdate) {
74
55
  if (!data.ok) {
75
- showToast(data.error || "OpenClaw update failed", "error");
76
- } else if (data.updated) {
56
+ showToast(data.error || `${label} update failed`, "error");
57
+ } else if (data.updated || data.restarting) {
77
58
  showToast(
78
- data.restarted
79
- ? `Updated to ${data.currentVersion} and restarted gateway`
80
- : `Updated to ${data.currentVersion}`,
59
+ data.restarting
60
+ ? `${label} updated restarting...`
61
+ : `Updated ${label} to ${data.currentVersion}`,
81
62
  "success",
82
63
  );
83
64
  } else {
84
- showToast("Already at latest OpenClaw version", "success");
65
+ showToast(`Already at latest ${label} version`, "success");
85
66
  }
86
67
  } else if (data.hasUpdate && data.latestVersion) {
87
- showToast(`Update available: ${data.latestVersion}`, "warning");
68
+ showToast(`${label} update available: ${data.latestVersion}`, "warning");
88
69
  } else {
89
- showToast("OpenClaw is up to date", "success");
70
+ showToast(`${label} is up to date`, "success");
90
71
  }
91
72
  } catch (err) {
92
- setUpdateError(
93
- err.message ||
94
- (hasUpdate ? "Could not update OpenClaw" : "Could not check updates"),
95
- );
96
- showToast(
97
- hasUpdate ? "Could not update OpenClaw" : "Could not check updates",
98
- "error",
99
- );
73
+ setError(err.message || (hasUpdate ? `Could not update ${label}` : "Could not check updates"));
74
+ showToast(hasUpdate ? `Could not update ${label}` : "Could not check updates", "error");
75
+ }
76
+ setChecking(false);
77
+ };
78
+
79
+ return html`
80
+ <div class="flex items-center justify-between gap-3">
81
+ <div class="min-w-0">
82
+ <p class="text-sm text-gray-300 truncate">
83
+ <span class="text-gray-500">${label}</span>${" "}v${version || "unknown"}
84
+ </p>
85
+ ${error && html`<p class="text-xs text-yellow-500 mt-1">${error}</p>`}
86
+ </div>
87
+ <div class="flex items-center gap-2 shrink-0">
88
+ ${hasUpdate && latestVersion && tagsUrl && html`
89
+ <a href=${tagsUrl} target="_blank"
90
+ class="text-xs text-yellow-500 hover:text-yellow-300 transition-colors"
91
+ >${latestVersion} available</a>
92
+ `}
93
+ <button
94
+ onclick=${handleAction}
95
+ disabled=${checking}
96
+ class="text-xs px-2.5 py-1 rounded-lg border border-border text-gray-500 hover:text-gray-300 hover:border-gray-500 transition-colors ${checking ? "opacity-50 cursor-not-allowed" : ""}"
97
+ >
98
+ ${checking
99
+ ? hasUpdate ? "Updating..." : "Checking..."
100
+ : hasUpdate ? "Update" : "Check updates"}
101
+ </button>
102
+ </div>
103
+ </div>
104
+ `;
105
+ }
106
+
107
+ export function Gateway({ status, openclawVersion }) {
108
+ const [restarting, setRestarting] = useState(false);
109
+ const isRunning = status === "running" && !restarting;
110
+ const dotClass = isRunning
111
+ ? "w-2 h-2 rounded-full bg-green-500"
112
+ : "w-2 h-2 rounded-full bg-yellow-500 animate-pulse";
113
+
114
+ const handleRestart = async () => {
115
+ if (restarting) return;
116
+ setRestarting(true);
117
+ try {
118
+ await restartGateway();
119
+ showToast("Gateway restarted", "success");
120
+ } catch (err) {
121
+ showToast("Restart failed: " + err.message, "error");
100
122
  }
101
- setCheckingUpdate(false);
123
+ setRestarting(false);
102
124
  };
103
125
 
104
126
  return html` <div class="bg-surface border border-border rounded-xl p-4">
@@ -124,40 +146,13 @@ export function Gateway({ status, openclawVersion }) {
124
146
  </button>
125
147
  </div>
126
148
  <div class="mt-3 pt-3 border-t border-border">
127
- <div class="flex items-center justify-between gap-3">
128
- <div class="min-w-0">
129
- <p class="text-sm text-gray-300 truncate">
130
- v${currentVersion || openclawVersion || "unknown"}
131
- </p>
132
- ${updateError &&
133
- html`<p class="text-xs text-yellow-500 mt-1">${updateError}</p>`}
134
- </div>
135
- <div class="flex items-center gap-2 shrink-0">
136
- ${hasUpdate &&
137
- latestVersion &&
138
- html`<a
139
- href="https://github.com/openclaw/openclaw/tags"
140
- target="_blank"
141
- class="text-xs text-yellow-500 hover:text-yellow-300 transition-colors"
142
- >${latestVersion} available</a
143
- >`}
144
- <button
145
- onclick=${handleUpdate}
146
- disabled=${checkingUpdate}
147
- class="text-xs px-2.5 py-1 rounded-lg border border-border text-gray-500 hover:text-gray-300 hover:border-gray-500 transition-colors ${checkingUpdate
148
- ? "opacity-50 cursor-not-allowed"
149
- : ""}"
150
- >
151
- ${checkingUpdate
152
- ? hasUpdate
153
- ? "Updating..."
154
- : "Checking..."
155
- : hasUpdate
156
- ? "Update"
157
- : "Check updates"}
158
- </button>
159
- </div>
160
- </div>
149
+ <${VersionRow}
150
+ label="OpenClaw"
151
+ currentVersion=${openclawVersion}
152
+ fetchVersion=${fetchOpenclawVersion}
153
+ applyUpdate=${updateOpenclaw}
154
+ tagsUrl="https://github.com/openclaw/openclaw/tags"
155
+ />
161
156
  </div>
162
157
  </div>`;
163
158
  }
@@ -80,6 +80,17 @@ export async function updateOpenclaw() {
80
80
  return res.json();
81
81
  }
82
82
 
83
+ export async function fetchAlphaclawVersion(refresh = false) {
84
+ const query = refresh ? '?refresh=1' : '';
85
+ const res = await authFetch(`/api/alphaclaw/version${query}`);
86
+ return res.json();
87
+ }
88
+
89
+ export async function updateAlphaclaw() {
90
+ const res = await authFetch('/api/alphaclaw/update', { method: 'POST' });
91
+ return res.json();
92
+ }
93
+
83
94
  export async function fetchSyncCron() {
84
95
  const res = await authFetch('/api/sync-cron');
85
96
  const text = await res.text();
@@ -0,0 +1,190 @@
1
+ const { exec } = require("child_process");
2
+ const { spawn } = require("child_process");
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const https = require("https");
6
+ const http = require("http");
7
+ const {
8
+ kLatestVersionCacheTtlMs,
9
+ kAlphaclawRegistryUrl,
10
+ kPackageRoot,
11
+ } = require("./constants");
12
+
13
+ // kPackageRoot is lib/ — the actual npm package root (with package.json) is one level up
14
+ const kNpmPackageRoot = path.resolve(kPackageRoot, "..");
15
+
16
+ const createAlphaclawVersionService = () => {
17
+ let kUpdateStatusCache = { latestVersion: null, hasUpdate: false, fetchedAt: 0 };
18
+ let kUpdateInProgress = false;
19
+
20
+ const readAlphaclawVersion = () => {
21
+ try {
22
+ const pkg = JSON.parse(
23
+ fs.readFileSync(path.join(kNpmPackageRoot, "package.json"), "utf8"),
24
+ );
25
+ return pkg.version || null;
26
+ } catch {
27
+ return null;
28
+ }
29
+ };
30
+
31
+ const fetchLatestVersionFromRegistry = () =>
32
+ new Promise((resolve, reject) => {
33
+ const get = kAlphaclawRegistryUrl.startsWith("https") ? https.get : http.get;
34
+ get(kAlphaclawRegistryUrl, { headers: { Accept: "application/json" } }, (res) => {
35
+ let data = "";
36
+ res.on("data", (chunk) => { data += chunk; });
37
+ res.on("end", () => {
38
+ try {
39
+ const parsed = JSON.parse(data);
40
+ resolve(parsed["dist-tags"]?.latest || null);
41
+ } catch (e) {
42
+ reject(new Error("Failed to parse registry response"));
43
+ }
44
+ });
45
+ }).on("error", reject);
46
+ });
47
+
48
+ const readAlphaclawUpdateStatus = async ({ refresh = false } = {}) => {
49
+ const now = Date.now();
50
+ if (
51
+ !refresh &&
52
+ kUpdateStatusCache.fetchedAt &&
53
+ now - kUpdateStatusCache.fetchedAt < kLatestVersionCacheTtlMs
54
+ ) {
55
+ return {
56
+ latestVersion: kUpdateStatusCache.latestVersion,
57
+ hasUpdate: kUpdateStatusCache.hasUpdate,
58
+ };
59
+ }
60
+ const currentVersion = readAlphaclawVersion();
61
+ const latestVersion = await fetchLatestVersionFromRegistry();
62
+ const hasUpdate = !!(currentVersion && latestVersion && latestVersion !== currentVersion);
63
+ kUpdateStatusCache = { latestVersion, hasUpdate, fetchedAt: Date.now() };
64
+ console.log(
65
+ `[alphaclaw] alphaclaw update status: hasUpdate=${hasUpdate} current=${currentVersion} latest=${latestVersion || "unknown"}`,
66
+ );
67
+ return { latestVersion, hasUpdate };
68
+ };
69
+
70
+ const findInstallDir = () => {
71
+ // Walk up from kNpmPackageRoot to find the consuming project's directory
72
+ // (the one with node_modules/@chrysb/alphaclaw). In Docker this is /app.
73
+ let dir = kNpmPackageRoot;
74
+ while (dir !== path.dirname(dir)) {
75
+ const parent = path.dirname(dir);
76
+ if (path.basename(parent) === "node_modules" || parent.includes("node_modules")) {
77
+ dir = parent;
78
+ continue;
79
+ }
80
+ const pkgPath = path.join(parent, "package.json");
81
+ if (fs.existsSync(pkgPath)) {
82
+ try {
83
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
84
+ if (pkg.dependencies?.["@chrysb/alphaclaw"]) {
85
+ return parent;
86
+ }
87
+ } catch {}
88
+ }
89
+ dir = parent;
90
+ }
91
+ // Fallback: if running directly (not from node_modules), use kNpmPackageRoot
92
+ return kNpmPackageRoot;
93
+ };
94
+
95
+ const installLatestAlphaclaw = () =>
96
+ new Promise((resolve, reject) => {
97
+ const installDir = findInstallDir();
98
+ console.log(`[alphaclaw] Running: npm install @chrysb/alphaclaw@latest (cwd: ${installDir})`);
99
+ exec(
100
+ "npm install @chrysb/alphaclaw@latest --omit=dev --no-save --package-lock=false",
101
+ {
102
+ cwd: installDir,
103
+ env: {
104
+ ...process.env,
105
+ npm_config_update_notifier: "false",
106
+ npm_config_fund: "false",
107
+ npm_config_audit: "false",
108
+ },
109
+ timeout: 180000,
110
+ },
111
+ (err, stdout, stderr) => {
112
+ if (err) {
113
+ const message = String(stderr || err.message || "").trim();
114
+ console.log(`[alphaclaw] alphaclaw install error: ${message.slice(0, 200)}`);
115
+ return reject(new Error(message || "Failed to install @chrysb/alphaclaw@latest"));
116
+ }
117
+ if (stdout?.trim()) {
118
+ console.log(`[alphaclaw] alphaclaw install stdout: ${stdout.trim().slice(0, 300)}`);
119
+ }
120
+ console.log("[alphaclaw] alphaclaw install completed");
121
+ resolve({ stdout: stdout?.trim(), stderr: stderr?.trim() });
122
+ },
123
+ );
124
+ });
125
+
126
+ const restartProcess = () => {
127
+ console.log("[alphaclaw] Restarting process...");
128
+ const child = spawn(process.argv[0], process.argv.slice(1), {
129
+ detached: true,
130
+ stdio: "inherit",
131
+ });
132
+ child.unref();
133
+ process.exit(0);
134
+ };
135
+
136
+ const getVersionStatus = async (refresh) => {
137
+ const currentVersion = readAlphaclawVersion();
138
+ try {
139
+ const { latestVersion, hasUpdate } = await readAlphaclawUpdateStatus({ refresh });
140
+ return { ok: true, currentVersion, latestVersion, hasUpdate };
141
+ } catch (err) {
142
+ return {
143
+ ok: false,
144
+ currentVersion,
145
+ latestVersion: kUpdateStatusCache.latestVersion,
146
+ hasUpdate: kUpdateStatusCache.hasUpdate,
147
+ error: err.message || "Failed to fetch latest AlphaClaw version",
148
+ };
149
+ }
150
+ };
151
+
152
+ const updateAlphaclaw = async () => {
153
+ if (kUpdateInProgress) {
154
+ return {
155
+ status: 409,
156
+ body: { ok: false, error: "AlphaClaw update already in progress" },
157
+ };
158
+ }
159
+
160
+ kUpdateInProgress = true;
161
+ const previousVersion = readAlphaclawVersion();
162
+ try {
163
+ await installLatestAlphaclaw();
164
+ kUpdateStatusCache = { latestVersion: null, hasUpdate: false, fetchedAt: 0 };
165
+ return {
166
+ status: 200,
167
+ body: {
168
+ ok: true,
169
+ previousVersion,
170
+ restarting: true,
171
+ },
172
+ };
173
+ } catch (err) {
174
+ kUpdateInProgress = false;
175
+ return {
176
+ status: 500,
177
+ body: { ok: false, error: err.message || "Failed to update AlphaClaw" },
178
+ };
179
+ }
180
+ };
181
+
182
+ return {
183
+ readAlphaclawVersion,
184
+ getVersionStatus,
185
+ updateAlphaclaw,
186
+ restartProcess,
187
+ };
188
+ };
189
+
190
+ module.exports = { createAlphaclawVersionService };
@@ -106,6 +106,7 @@ const kFallbackOnboardingModels = [
106
106
  const kVersionCacheTtlMs = 60 * 1000;
107
107
  const kLatestVersionCacheTtlMs = 10 * 60 * 1000;
108
108
  const kOpenclawRegistryUrl = "https://registry.npmjs.org/openclaw";
109
+ const kAlphaclawRegistryUrl = "https://registry.npmjs.org/@chrysb%2falphaclaw";
109
110
  const kAppDir = kPackageRoot;
110
111
 
111
112
  const kSystemVars = new Set([
@@ -265,6 +266,7 @@ module.exports = {
265
266
  kVersionCacheTtlMs,
266
267
  kLatestVersionCacheTtlMs,
267
268
  kOpenclawRegistryUrl,
269
+ kAlphaclawRegistryUrl,
268
270
  kAppDir,
269
271
  kSystemVars,
270
272
  kKnownVars,
@@ -12,6 +12,7 @@ const registerSystemRoutes = ({
12
12
  isOnboarded,
13
13
  getChannelStatus,
14
14
  openclawVersionService,
15
+ alphaclawVersionService,
15
16
  clawCmd,
16
17
  restartGateway,
17
18
  OPENCLAW_DIR,
@@ -183,6 +184,26 @@ const registerSystemRoutes = ({
183
184
  res.status(result.status).json(result.body);
184
185
  });
185
186
 
187
+ app.get("/api/alphaclaw/version", async (req, res) => {
188
+ const refresh = String(req.query.refresh || "") === "1";
189
+ const status = await alphaclawVersionService.getVersionStatus(refresh);
190
+ res.json(status);
191
+ });
192
+
193
+ app.post("/api/alphaclaw/update", async (req, res) => {
194
+ console.log("[alphaclaw] /api/alphaclaw/update requested");
195
+ const result = await alphaclawVersionService.updateAlphaclaw();
196
+ console.log(
197
+ `[alphaclaw] /api/alphaclaw/update result: status=${result.status} ok=${result.body?.ok === true}`,
198
+ );
199
+ if (result.status === 200 && result.body?.ok) {
200
+ res.json(result.body);
201
+ setTimeout(() => alphaclawVersionService.restartProcess(), 1000);
202
+ } else {
203
+ res.status(result.status).json(result.body);
204
+ }
205
+ });
206
+
186
207
  app.get("/api/gateway-status", async (req, res) => {
187
208
  const result = await clawCmd("status");
188
209
  res.json(result);
package/lib/server.js CHANGED
@@ -34,6 +34,7 @@ const { createCommands } = require("./server/commands");
34
34
  const { createAuthProfiles } = require("./server/auth-profiles");
35
35
  const { createLoginThrottle } = require("./server/login-throttle");
36
36
  const { createOpenclawVersionService } = require("./server/openclaw-version");
37
+ const { createAlphaclawVersionService } = require("./server/alphaclaw-version");
37
38
  const { syncBootstrapPromptFiles } = require("./server/onboarding/workspace");
38
39
 
39
40
  const { registerAuthRoutes } = require("./server/routes/auth");
@@ -76,6 +77,7 @@ const openclawVersionService = createOpenclawVersionService({
76
77
  restartGateway,
77
78
  isOnboarded,
78
79
  });
80
+ const alphaclawVersionService = createAlphaclawVersionService();
79
81
 
80
82
  const { requireAuth } = registerAuthRoutes({ app, loginThrottle });
81
83
  app.use(express.static(path.join(__dirname, "public")));
@@ -118,6 +120,7 @@ registerSystemRoutes({
118
120
  isOnboarded,
119
121
  getChannelStatus,
120
122
  openclawVersionService,
123
+ alphaclawVersionService,
121
124
  clawCmd,
122
125
  restartGateway,
123
126
  OPENCLAW_DIR: constants.OPENCLAW_DIR,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrysb/alphaclaw",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },