@chrysb/alphaclaw 0.9.14 → 0.9.15
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.
- package/README.md +2 -1
- package/bin/alphaclaw.js +53 -135
- package/lib/cli/openclaw-config-restore.js +162 -0
- package/lib/public/css/explorer.css +3 -1
- package/lib/public/dist/app.bundle.js +474 -474
- package/lib/public/js/components/agents-tab/agent-identity-section.js +1 -1
- package/lib/public/js/components/general/use-general-tab.js +25 -8
- package/lib/public/js/components/sidebar.js +2 -1
- package/lib/public/js/lib/agent-identity.js +8 -0
- package/lib/public/js/lib/api.js +7 -5
- package/lib/server/routes/pairings.js +120 -8
- package/lib/server/routes/system.js +104 -4
- package/package.json +2 -2
|
@@ -116,7 +116,7 @@ export const AgentIdentitySection = ({
|
|
|
116
116
|
value=${form.emoji}
|
|
117
117
|
onInput=${(event) => updateField("emoji", event.target.value)}
|
|
118
118
|
class="w-full bg-field border border-border rounded-lg px-3 py-2 text-sm text-body outline-none focus:border-fg-muted"
|
|
119
|
-
placeholder="
|
|
119
|
+
placeholder="Single emoji, e.g. ✨"
|
|
120
120
|
/>
|
|
121
121
|
</label>
|
|
122
122
|
<label class="block space-y-1">
|
|
@@ -218,15 +218,27 @@ export const useGeneralTab = ({
|
|
|
218
218
|
};
|
|
219
219
|
|
|
220
220
|
const handleDeviceApprove = async (id) => {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
221
|
+
try {
|
|
222
|
+
await approveDevice(id);
|
|
223
|
+
showToast("Device pairing approved", "success");
|
|
224
|
+
setTimeout(devicePoll.refresh, 500);
|
|
225
|
+
setTimeout(devicePoll.refresh, 2000);
|
|
226
|
+
} catch (err) {
|
|
227
|
+
showToast(err.message || "Could not approve device pairing", "error");
|
|
228
|
+
throw err;
|
|
229
|
+
}
|
|
224
230
|
};
|
|
225
231
|
|
|
226
232
|
const handleDeviceReject = async (id) => {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
233
|
+
try {
|
|
234
|
+
await rejectDevice(id);
|
|
235
|
+
showToast("Device pairing rejected", "info");
|
|
236
|
+
setTimeout(devicePoll.refresh, 500);
|
|
237
|
+
setTimeout(devicePoll.refresh, 2000);
|
|
238
|
+
} catch (err) {
|
|
239
|
+
showToast(err.message || "Could not reject device pairing", "error");
|
|
240
|
+
throw err;
|
|
241
|
+
}
|
|
230
242
|
};
|
|
231
243
|
|
|
232
244
|
const handleWatchdogRepair = async () => {
|
|
@@ -252,10 +264,15 @@ export const useGeneralTab = ({
|
|
|
252
264
|
setDashboardLoading(true);
|
|
253
265
|
try {
|
|
254
266
|
const data = await fetchDashboardUrl();
|
|
255
|
-
|
|
267
|
+
if (data.needsAuth) {
|
|
268
|
+
showToast(
|
|
269
|
+
"OpenClaw dashboard token is missing from the AlphaClaw server environment",
|
|
270
|
+
"warning",
|
|
271
|
+
);
|
|
272
|
+
}
|
|
256
273
|
window.open(data.url || "/openclaw", "_blank");
|
|
257
274
|
} catch (err) {
|
|
258
|
-
|
|
275
|
+
showToast(err.message || "Could not open OpenClaw dashboard", "error");
|
|
259
276
|
window.open("/openclaw", "_blank");
|
|
260
277
|
} finally {
|
|
261
278
|
setDashboardLoading(false);
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
getSessionDisplayLabel,
|
|
34
34
|
getSessionRowKey,
|
|
35
35
|
} from "../lib/session-keys.js";
|
|
36
|
+
import { sanitizeAgentEmoji } from "../lib/agent-identity.js";
|
|
36
37
|
import { ThemeToggle } from "./theme-toggle.js";
|
|
37
38
|
|
|
38
39
|
const html = htm.bind(h);
|
|
@@ -91,7 +92,7 @@ const renderNavItem = ({ item, selectedNavId, onSelectNavItem }) => {
|
|
|
91
92
|
`;
|
|
92
93
|
};
|
|
93
94
|
|
|
94
|
-
const getAgentIdentityEmoji = (agent) =>
|
|
95
|
+
const getAgentIdentityEmoji = (agent) => sanitizeAgentEmoji(agent?.identity?.emoji);
|
|
95
96
|
|
|
96
97
|
export const AppSidebar = ({
|
|
97
98
|
mobileSidebarOpen = false,
|
package/lib/public/js/lib/api.js
CHANGED
|
@@ -466,7 +466,7 @@ export async function updateWatchdogSettings(settings) {
|
|
|
466
466
|
|
|
467
467
|
export async function fetchDashboardUrl() {
|
|
468
468
|
const res = await authFetch("/api/gateway/dashboard");
|
|
469
|
-
return res
|
|
469
|
+
return parseJsonOrThrow(res, "Could not load dashboard URL");
|
|
470
470
|
}
|
|
471
471
|
|
|
472
472
|
export async function fetchAlphaclawVersion(refresh = false) {
|
|
@@ -682,13 +682,15 @@ export async function fetchDevicePairings() {
|
|
|
682
682
|
}
|
|
683
683
|
|
|
684
684
|
export async function approveDevice(id) {
|
|
685
|
-
const
|
|
686
|
-
|
|
685
|
+
const safeId = encodeURIComponent(String(id || ""));
|
|
686
|
+
const res = await authFetch(`/api/devices/${safeId}/approve`, { method: "POST" });
|
|
687
|
+
return parseJsonOrThrow(res, "Could not approve device");
|
|
687
688
|
}
|
|
688
689
|
|
|
689
690
|
export async function rejectDevice(id) {
|
|
690
|
-
const
|
|
691
|
-
|
|
691
|
+
const safeId = encodeURIComponent(String(id || ""));
|
|
692
|
+
const res = await authFetch(`/api/devices/${safeId}/reject`, { method: "POST" });
|
|
693
|
+
return parseJsonOrThrow(res, "Could not reject device");
|
|
692
694
|
}
|
|
693
695
|
|
|
694
696
|
export const fetchNodesStatus = async () => {
|
|
@@ -10,8 +10,88 @@ const kAllowedPairingChannels = new Set(["telegram", "discord", "slack", "whatsa
|
|
|
10
10
|
const kSafePairingArgPattern = /^[\w\-:.]+$/;
|
|
11
11
|
const kDevicesListCliTimeoutMs = 5000;
|
|
12
12
|
const kPairingRequestTtlMs = 60 * 60 * 1000;
|
|
13
|
+
const kDeviceApprovalCallerScopes = [
|
|
14
|
+
"operator.admin",
|
|
15
|
+
"operator.read",
|
|
16
|
+
"operator.write",
|
|
17
|
+
"operator.approvals",
|
|
18
|
+
"operator.pairing",
|
|
19
|
+
"operator.talk.secrets",
|
|
20
|
+
];
|
|
13
21
|
const quoteCliArg = (value) => quoteShellArg(value, { strategy: "single" });
|
|
14
22
|
|
|
23
|
+
let deviceBootstrapModulePromise = null;
|
|
24
|
+
|
|
25
|
+
const loadDeviceBootstrapModule = async () => {
|
|
26
|
+
deviceBootstrapModulePromise ||= import("openclaw/plugin-sdk/device-bootstrap");
|
|
27
|
+
return deviceBootstrapModulePromise;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const defaultApproveDevicePairingDirect = async (requestId, options, baseDir) => {
|
|
31
|
+
const mod = await loadDeviceBootstrapModule();
|
|
32
|
+
if (typeof mod.approveDevicePairing !== "function") {
|
|
33
|
+
throw new Error("OpenClaw device approval helper is unavailable");
|
|
34
|
+
}
|
|
35
|
+
return mod.approveDevicePairing(requestId, options, baseDir);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const formatDevicePairingForbiddenMessage = (result) => {
|
|
39
|
+
switch (result?.reason) {
|
|
40
|
+
case "caller-scopes-required":
|
|
41
|
+
return `missing scope: ${result.scope || "callerScopes-required"}`;
|
|
42
|
+
case "caller-missing-scope":
|
|
43
|
+
return `missing scope: ${result.scope || "unknown"}`;
|
|
44
|
+
case "scope-outside-requested-roles":
|
|
45
|
+
return `invalid scope for requested roles: ${result.scope || "unknown"}`;
|
|
46
|
+
case "bootstrap-role-not-allowed":
|
|
47
|
+
return `bootstrap profile does not allow role: ${result.role || "unknown"}`;
|
|
48
|
+
case "bootstrap-scope-not-allowed":
|
|
49
|
+
return `bootstrap profile does not allow scope: ${result.scope || "unknown"}`;
|
|
50
|
+
default:
|
|
51
|
+
return "Device pairing approval forbidden";
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const redactApprovedDevice = (device) => {
|
|
56
|
+
if (!device || typeof device !== "object") return null;
|
|
57
|
+
const safeDevice = { ...device };
|
|
58
|
+
delete safeDevice.publicKey;
|
|
59
|
+
delete safeDevice.tokens;
|
|
60
|
+
return safeDevice;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const normalizeDeviceApprovalResult = (approval, requestId) => {
|
|
64
|
+
if (approval?.status === "approved") {
|
|
65
|
+
return {
|
|
66
|
+
ok: true,
|
|
67
|
+
requestId: approval.requestId || requestId,
|
|
68
|
+
device: redactApprovedDevice(approval.device),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (approval?.status === "forbidden") {
|
|
72
|
+
return {
|
|
73
|
+
ok: false,
|
|
74
|
+
statusCode: 403,
|
|
75
|
+
error: formatDevicePairingForbiddenMessage(approval),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
ok: false,
|
|
80
|
+
statusCode: 404,
|
|
81
|
+
error: "Device pairing request not found",
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const toHttpDeviceApprovalPayload = (result) => {
|
|
86
|
+
const { statusCode, ...payload } = result || {};
|
|
87
|
+
return payload;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const isValidDeviceRequestId = (value) => {
|
|
91
|
+
const requestId = String(value || "").trim();
|
|
92
|
+
return Boolean(requestId && kSafePairingArgPattern.test(requestId));
|
|
93
|
+
};
|
|
94
|
+
|
|
15
95
|
const resolvePairingStorePath = ({ openclawDir, channel }) =>
|
|
16
96
|
path.join(openclawDir, "credentials", `${String(channel).trim().toLowerCase()}-pairing.json`);
|
|
17
97
|
|
|
@@ -136,7 +216,14 @@ const removeAccountRequestsFromPairingStore = ({ fsModule, openclawDir, channel,
|
|
|
136
216
|
}
|
|
137
217
|
};
|
|
138
218
|
|
|
139
|
-
const registerPairingRoutes = ({
|
|
219
|
+
const registerPairingRoutes = ({
|
|
220
|
+
app,
|
|
221
|
+
clawCmd,
|
|
222
|
+
isOnboarded,
|
|
223
|
+
fsModule = fs,
|
|
224
|
+
openclawDir = OPENCLAW_DIR,
|
|
225
|
+
approveDevicePairingDirect = defaultApproveDevicePairingDirect,
|
|
226
|
+
}) => {
|
|
140
227
|
let pairingCache = { pending: [], ts: 0, ttlMs: 0 };
|
|
141
228
|
const kPairingCacheTtlMs = 10000;
|
|
142
229
|
const kEmptyPairingCacheTtlMs = 1000;
|
|
@@ -157,6 +244,23 @@ const registerPairingRoutes = ({ app, clawCmd, isOnboarded, fsModule = fs, openc
|
|
|
157
244
|
);
|
|
158
245
|
};
|
|
159
246
|
|
|
247
|
+
const approveDeviceRequestWithAdminScope = async (requestId) => {
|
|
248
|
+
try {
|
|
249
|
+
const approval = await approveDevicePairingDirect(
|
|
250
|
+
requestId,
|
|
251
|
+
{ callerScopes: kDeviceApprovalCallerScopes },
|
|
252
|
+
openclawDir,
|
|
253
|
+
);
|
|
254
|
+
return normalizeDeviceApprovalResult(approval, requestId);
|
|
255
|
+
} catch (error) {
|
|
256
|
+
return {
|
|
257
|
+
ok: false,
|
|
258
|
+
statusCode: 500,
|
|
259
|
+
error: error?.message || "Could not approve device pairing",
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
|
|
160
264
|
const parsePendingPairings = (stdout, channel) => {
|
|
161
265
|
const parsed = parseJsonObjectFromNoisyOutput(stdout) || {};
|
|
162
266
|
const requestLists = [
|
|
@@ -320,15 +424,13 @@ const registerPairingRoutes = ({ app, clawCmd, isOnboarded, fsModule = fs, openc
|
|
|
320
424
|
const firstCliPendingId = firstCliPending?.requestId || firstCliPending?.id;
|
|
321
425
|
if (firstCliPendingId) {
|
|
322
426
|
console.log(`[alphaclaw] Auto-approving first CLI device request: ${firstCliPendingId}`);
|
|
323
|
-
const approveResult = await
|
|
324
|
-
quiet: true,
|
|
325
|
-
});
|
|
427
|
+
const approveResult = await approveDeviceRequestWithAdminScope(firstCliPendingId);
|
|
326
428
|
if (approveResult.ok) {
|
|
327
429
|
writeCliAutoApproveMarker();
|
|
328
430
|
autoApprovedRequestId = String(firstCliPendingId);
|
|
329
431
|
} else {
|
|
330
432
|
console.log(
|
|
331
|
-
`[alphaclaw] CLI auto-approve failed: ${(approveResult.
|
|
433
|
+
`[alphaclaw] CLI auto-approve failed: ${(approveResult.error || "").slice(0, 200)}`,
|
|
332
434
|
);
|
|
333
435
|
}
|
|
334
436
|
}
|
|
@@ -353,13 +455,23 @@ const registerPairingRoutes = ({ app, clawCmd, isOnboarded, fsModule = fs, openc
|
|
|
353
455
|
});
|
|
354
456
|
|
|
355
457
|
app.post("/api/devices/:id/approve", async (req, res) => {
|
|
356
|
-
const
|
|
458
|
+
const requestId = String(req.params.id || "").trim();
|
|
459
|
+
if (!isValidDeviceRequestId(requestId)) {
|
|
460
|
+
return res.status(400).json({ ok: false, error: "Invalid device request id" });
|
|
461
|
+
}
|
|
462
|
+
const result = await approveDeviceRequestWithAdminScope(requestId);
|
|
357
463
|
devicePairingCache.ts = 0;
|
|
358
|
-
res
|
|
464
|
+
res
|
|
465
|
+
.status(result.ok ? 200 : result.statusCode || 500)
|
|
466
|
+
.json(toHttpDeviceApprovalPayload(result));
|
|
359
467
|
});
|
|
360
468
|
|
|
361
469
|
app.post("/api/devices/:id/reject", async (req, res) => {
|
|
362
|
-
const
|
|
470
|
+
const requestId = String(req.params.id || "").trim();
|
|
471
|
+
if (!isValidDeviceRequestId(requestId)) {
|
|
472
|
+
return res.status(400).json({ ok: false, error: "Invalid device request id" });
|
|
473
|
+
}
|
|
474
|
+
const result = await clawCmd(`devices reject ${quoteCliArg(requestId)}`);
|
|
363
475
|
devicePairingCache.ts = 0;
|
|
364
476
|
res.json(result);
|
|
365
477
|
});
|
|
@@ -28,6 +28,7 @@ const registerSystemRoutes = ({
|
|
|
28
28
|
doctorService,
|
|
29
29
|
}) => {
|
|
30
30
|
let envRestartPending = false;
|
|
31
|
+
let openclawSecretRuntimePromise = null;
|
|
31
32
|
const kManagedChannelTokenPattern =
|
|
32
33
|
/^(?:TELEGRAM_BOT_TOKEN|DISCORD_BOT_TOKEN|SLACK_BOT_TOKEN|SLACK_APP_TOKEN)(?:_[A-Z0-9_]+)?$/;
|
|
33
34
|
const kEnvVarsReservedForUserInput = new Set([
|
|
@@ -92,6 +93,97 @@ const registerSystemRoutes = ({
|
|
|
92
93
|
}
|
|
93
94
|
return null;
|
|
94
95
|
};
|
|
96
|
+
const getEnvFileValue = (key) =>
|
|
97
|
+
(typeof readEnvFile === "function" ? readEnvFile() : []).find(
|
|
98
|
+
(entry) => entry?.key === key,
|
|
99
|
+
)?.value;
|
|
100
|
+
const normalizeSecretValue = (value) => {
|
|
101
|
+
if (typeof value !== "string") return "";
|
|
102
|
+
const trimmed = String(value || "").trim();
|
|
103
|
+
if (trimmed.length >= 2) {
|
|
104
|
+
const first = trimmed[0];
|
|
105
|
+
const last = trimmed[trimmed.length - 1];
|
|
106
|
+
if ((first === '"' && last === '"') || (first === "'" && last === "'")) {
|
|
107
|
+
return trimmed.slice(1, -1).trim();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return trimmed;
|
|
111
|
+
};
|
|
112
|
+
const getEnvObject = () => {
|
|
113
|
+
const env = { ...process.env };
|
|
114
|
+
for (const entry of typeof readEnvFile === "function" ? readEnvFile() : []) {
|
|
115
|
+
const key = String(entry?.key || "").trim();
|
|
116
|
+
if (!key) continue;
|
|
117
|
+
if (!normalizeSecretValue(env[key])) {
|
|
118
|
+
env[key] = normalizeSecretValue(entry?.value);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return env;
|
|
122
|
+
};
|
|
123
|
+
const loadOpenclawSecretRuntime = async () => {
|
|
124
|
+
openclawSecretRuntimePromise ||= Promise.all([
|
|
125
|
+
import("openclaw/plugin-sdk/secret-input"),
|
|
126
|
+
import("openclaw/plugin-sdk/runtime-secret-resolution"),
|
|
127
|
+
]).then(([secretInput, runtimeSecretResolution]) => ({
|
|
128
|
+
coerceSecretRef: secretInput.coerceSecretRef,
|
|
129
|
+
resolveSecretRefValues: runtimeSecretResolution.resolveSecretRefValues,
|
|
130
|
+
}));
|
|
131
|
+
return openclawSecretRuntimePromise;
|
|
132
|
+
};
|
|
133
|
+
const resolveSecretRefToken = async ({ config, value, env }) => {
|
|
134
|
+
try {
|
|
135
|
+
const { coerceSecretRef, resolveSecretRefValues } =
|
|
136
|
+
await loadOpenclawSecretRuntime();
|
|
137
|
+
const ref = coerceSecretRef(value, config?.secrets?.defaults);
|
|
138
|
+
if (!ref) return "";
|
|
139
|
+
const resolved = await resolveSecretRefValues([ref], { config, env });
|
|
140
|
+
const refKey = `${ref.source}:${ref.provider}:${ref.id}`;
|
|
141
|
+
return normalizeSecretValue(resolved.get(refKey));
|
|
142
|
+
} catch {
|
|
143
|
+
return "";
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
const resolveEnvReference = (value) => {
|
|
147
|
+
const match = String(value || "").trim().match(/^\$\{([A-Z_][A-Z0-9_]*)\}$/);
|
|
148
|
+
if (!match) return "";
|
|
149
|
+
const envKey = match[1];
|
|
150
|
+
const envValue = process.env[envKey] || getEnvFileValue(envKey);
|
|
151
|
+
return normalizeSecretValue(envValue);
|
|
152
|
+
};
|
|
153
|
+
const getDashboardTokenFromConfig = async () => {
|
|
154
|
+
const config = readOpenclawConfig({
|
|
155
|
+
fsModule: fs,
|
|
156
|
+
openclawDir: OPENCLAW_DIR,
|
|
157
|
+
fallback: {},
|
|
158
|
+
});
|
|
159
|
+
const env = getEnvObject();
|
|
160
|
+
const configuredToken = config?.gateway?.auth?.token;
|
|
161
|
+
const resolvedSecretRefToken = await resolveSecretRefToken({
|
|
162
|
+
config,
|
|
163
|
+
value: configuredToken,
|
|
164
|
+
env,
|
|
165
|
+
});
|
|
166
|
+
if (resolvedSecretRefToken) return resolvedSecretRefToken;
|
|
167
|
+
if (typeof configuredToken === "string" && configuredToken.trim()) {
|
|
168
|
+
const trimmedToken = normalizeSecretValue(configuredToken);
|
|
169
|
+
if (/^\$\{[A-Z_][A-Z0-9_]*\}$/.test(trimmedToken)) {
|
|
170
|
+
return resolveEnvReference(trimmedToken);
|
|
171
|
+
}
|
|
172
|
+
return trimmedToken;
|
|
173
|
+
}
|
|
174
|
+
return normalizeSecretValue(env.OPENCLAW_GATEWAY_TOKEN);
|
|
175
|
+
};
|
|
176
|
+
const buildDashboardUrl = (token) =>
|
|
177
|
+
token ? `/openclaw/#token=${encodeURIComponent(token)}` : "/openclaw";
|
|
178
|
+
const extractDashboardTokenFromOutput = (stdout) => {
|
|
179
|
+
const tokenMatch = String(stdout || "").match(/[#?&]token=([^\s&#]+)/);
|
|
180
|
+
if (!tokenMatch) return "";
|
|
181
|
+
try {
|
|
182
|
+
return decodeURIComponent(tokenMatch[1]);
|
|
183
|
+
} catch {
|
|
184
|
+
return tokenMatch[1];
|
|
185
|
+
}
|
|
186
|
+
};
|
|
95
187
|
const getRawSessionKey = (sessionRow = {}) =>
|
|
96
188
|
String(sessionRow?.key || sessionRow?.sessionKey || sessionRow?.id || "").trim();
|
|
97
189
|
const getRawSessionsFromPayload = (payload) => {
|
|
@@ -692,14 +784,22 @@ const registerSystemRoutes = ({
|
|
|
692
784
|
|
|
693
785
|
app.get("/api/gateway/dashboard", async (req, res) => {
|
|
694
786
|
if (!isOnboarded()) return res.json({ ok: false, url: "/openclaw" });
|
|
787
|
+
const token = await getDashboardTokenFromConfig();
|
|
788
|
+
if (token) {
|
|
789
|
+
return res.json({
|
|
790
|
+
ok: true,
|
|
791
|
+
url: buildDashboardUrl(token),
|
|
792
|
+
source: "config",
|
|
793
|
+
});
|
|
794
|
+
}
|
|
695
795
|
const result = await clawCmd("dashboard --no-open");
|
|
696
796
|
if (result.ok && result.stdout) {
|
|
697
|
-
const
|
|
698
|
-
if (
|
|
699
|
-
return res.json({ ok: true, url:
|
|
797
|
+
const cliToken = extractDashboardTokenFromOutput(result.stdout);
|
|
798
|
+
if (cliToken) {
|
|
799
|
+
return res.json({ ok: true, url: buildDashboardUrl(cliToken) });
|
|
700
800
|
}
|
|
701
801
|
}
|
|
702
|
-
res.json({ ok: true, url: "/openclaw" });
|
|
802
|
+
res.json({ ok: true, url: "/openclaw", needsAuth: true });
|
|
703
803
|
});
|
|
704
804
|
|
|
705
805
|
app.get("/api/restart-status", async (req, res) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chrysb/alphaclaw",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.15",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"express": "^4.21.0",
|
|
35
35
|
"http-proxy": "^1.18.1",
|
|
36
|
-
"openclaw": "2026.5.
|
|
36
|
+
"openclaw": "2026.5.6",
|
|
37
37
|
"ws": "^8.19.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|