@chrysb/alphaclaw 0.9.9 → 0.9.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 (34) hide show
  1. package/lib/public/dist/app.bundle.js +1424 -1398
  2. package/lib/public/js/components/agents-tab/agent-overview/use-model-card.js +4 -5
  3. package/lib/public/js/components/agents-tab/agent-pairing-section.js +11 -5
  4. package/lib/public/js/components/cron-tab/cron-helpers.js +13 -1
  5. package/lib/public/js/components/cron-tab/use-cron-tab.js +4 -3
  6. package/lib/public/js/components/general/index.js +6 -1
  7. package/lib/public/js/components/general/use-general-tab.js +17 -20
  8. package/lib/public/js/components/models-tab/index.js +5 -1
  9. package/lib/public/js/components/models-tab/model-picker.js +52 -0
  10. package/lib/public/js/components/onboarding/use-welcome-pairing.js +4 -1
  11. package/lib/public/js/components/pairings.js +75 -4
  12. package/lib/public/js/hooks/usePolling.js +46 -13
  13. package/lib/public/js/lib/model-config.js +4 -0
  14. package/lib/server/agents/channels.js +53 -9
  15. package/lib/server/commands.js +4 -1
  16. package/lib/server/constants.js +5 -0
  17. package/lib/server/cost-utils.js +9 -0
  18. package/lib/server/cron-service.js +12 -1
  19. package/lib/server/db/doctor/index.js +9 -0
  20. package/lib/server/db/usage/index.js +13 -0
  21. package/lib/server/db/watchdog/index.js +13 -1
  22. package/lib/server/db/webhooks/index.js +13 -1
  23. package/lib/server/init/register-server-routes.js +2 -0
  24. package/lib/server/internal-files-migration.js +11 -1
  25. package/lib/server/model-catalog-cache.js +85 -6
  26. package/lib/server/onboarding/github.js +79 -2
  27. package/lib/server/openclaw-version.js +2 -1
  28. package/lib/server/routes/models.js +26 -0
  29. package/lib/server/routes/pairings.js +106 -15
  30. package/lib/server/utils/command-output.js +11 -0
  31. package/lib/setup/gitignore +2 -0
  32. package/package.json +2 -2
  33. package/patches/openclaw+2026.4.21.patch +13 -0
  34. package/patches/openclaw+2026.4.15.patch +0 -13
@@ -2,12 +2,14 @@ const fs = require("fs");
2
2
  const path = require("path");
3
3
  const { OPENCLAW_DIR } = require("../constants");
4
4
  const { buildManagedPaths } = require("../internal-files-migration");
5
+ const { readOpenclawConfig } = require("../openclaw-config");
5
6
  const { parseJsonObjectFromNoisyOutput } = require("../utils/json");
6
7
  const { quoteShellArg } = require("../utils/shell");
7
8
 
8
9
  const kAllowedPairingChannels = new Set(["telegram", "discord", "slack", "whatsapp"]);
9
10
  const kSafePairingArgPattern = /^[\w\-:.]+$/;
10
11
  const kDevicesListCliTimeoutMs = 5000;
12
+ const kPairingRequestTtlMs = 60 * 60 * 1000;
11
13
  const quoteCliArg = (value) => quoteShellArg(value, { strategy: "single" });
12
14
 
13
15
  const resolvePairingStorePath = ({ openclawDir, channel }) =>
@@ -23,6 +25,77 @@ const readPairingStore = ({ fsModule, filePath }) => {
23
25
  }
24
26
  };
25
27
 
28
+ const normalizePairingCode = (value) =>
29
+ String(value || "")
30
+ .trim()
31
+ .toUpperCase();
32
+
33
+ const normalizePairingAccountId = (value) => String(value || "").trim() || "default";
34
+
35
+ const parseTimestampMs = (value) => {
36
+ const parsed = Date.parse(String(value || "").trim());
37
+ return Number.isFinite(parsed) ? parsed : null;
38
+ };
39
+
40
+ const mapPairingStoreEntry = ({ entry, channel, nowMs = Date.now() }) => {
41
+ const code = normalizePairingCode(entry?.code || entry?.pairingCode);
42
+ if (!code) return null;
43
+ const createdAt = String(entry?.createdAt || "").trim();
44
+ const createdAtMs = parseTimestampMs(createdAt);
45
+ if (!createdAtMs || nowMs - createdAtMs > kPairingRequestTtlMs) {
46
+ return null;
47
+ }
48
+ return {
49
+ id: code,
50
+ code,
51
+ channel: String(channel || "").trim(),
52
+ accountId: normalizePairingAccountId(entry?.meta?.accountId || entry?.accountId),
53
+ requesterId: String(entry?.id || entry?.requesterId || "").trim(),
54
+ createdAt,
55
+ };
56
+ };
57
+
58
+ const readPendingPairingsFromStore = ({ fsModule, openclawDir, channel, nowMs = Date.now() }) => {
59
+ const filePath = resolvePairingStorePath({ openclawDir, channel });
60
+ return readPairingStore({ fsModule, filePath })
61
+ .map((entry) => mapPairingStoreEntry({ entry, channel, nowMs }))
62
+ .filter(Boolean);
63
+ };
64
+
65
+ const mergePendingPairings = (...lists) => {
66
+ const merged = [];
67
+ const seen = new Map();
68
+ for (const list of lists) {
69
+ for (const entry of Array.isArray(list) ? list : []) {
70
+ const code = normalizePairingCode(entry?.code || entry?.id);
71
+ const channel = String(entry?.channel || "").trim();
72
+ if (!code || !channel) continue;
73
+ const accountId = normalizePairingAccountId(entry?.accountId);
74
+ const key = `${channel}\u0000${accountId}\u0000${code}`;
75
+ const current = seen.get(key);
76
+ if (!current) {
77
+ const nextEntry = {
78
+ ...entry,
79
+ id: code,
80
+ code,
81
+ channel,
82
+ accountId,
83
+ };
84
+ seen.set(key, nextEntry);
85
+ merged.push(nextEntry);
86
+ continue;
87
+ }
88
+ if (!current.requesterId && entry?.requesterId) {
89
+ current.requesterId = String(entry.requesterId).trim();
90
+ }
91
+ if (!current.createdAt && entry?.createdAt) {
92
+ current.createdAt = String(entry.createdAt).trim();
93
+ }
94
+ }
95
+ }
96
+ return merged;
97
+ };
98
+
26
99
  const writePairingStore = ({ fsModule, filePath, requests }) => {
27
100
  fsModule.mkdirSync(path.dirname(filePath), { recursive: true });
28
101
  fsModule.writeFileSync(filePath, JSON.stringify({ version: 1, requests }, null, 2));
@@ -64,8 +137,9 @@ const removeAccountRequestsFromPairingStore = ({ fsModule, openclawDir, channel,
64
137
  };
65
138
 
66
139
  const registerPairingRoutes = ({ app, clawCmd, isOnboarded, fsModule = fs, openclawDir = OPENCLAW_DIR }) => {
67
- let pairingCache = { pending: [], ts: 0 };
68
- const PAIRING_CACHE_TTL = 10000;
140
+ let pairingCache = { pending: [], ts: 0, ttlMs: 0 };
141
+ const kPairingCacheTtlMs = 10000;
142
+ const kEmptyPairingCacheTtlMs = 1000;
69
143
  const {
70
144
  cliDeviceAutoApprovedPath: kCliAutoApproveMarkerPath,
71
145
  internalDir: kManagedFilesDir,
@@ -107,34 +181,50 @@ const registerPairingRoutes = ({ app, clawCmd, isOnboarded, fsModule = fs, openc
107
181
  };
108
182
 
109
183
  app.get("/api/pairings", async (req, res) => {
110
- if (Date.now() - pairingCache.ts < PAIRING_CACHE_TTL) {
184
+ if (Date.now() - pairingCache.ts < Number(pairingCache.ttlMs || 0)) {
111
185
  return res.json({ pending: pairingCache.pending });
112
186
  }
113
187
 
114
188
  const pending = [];
115
189
  const channels = ["telegram", "discord", "slack", "whatsapp"];
190
+ const config = readOpenclawConfig({
191
+ fsModule,
192
+ openclawDir,
193
+ fallback: {},
194
+ });
116
195
 
117
196
  for (const ch of channels) {
118
- try {
119
- const config = JSON.parse(
120
- fsModule.readFileSync(`${openclawDir}/openclaw.json`, "utf8"),
121
- );
122
- if (!config.channels?.[ch]?.enabled) continue;
123
- } catch {
124
- continue;
125
- }
197
+ const pendingFromStore = readPendingPairingsFromStore({
198
+ fsModule,
199
+ openclawDir,
200
+ channel: ch,
201
+ });
202
+ const isEnabledInConfig = config.channels?.[ch]?.enabled === true;
203
+ if (!isEnabledInConfig && pendingFromStore.length === 0) continue;
126
204
 
127
205
  const result = await clawCmd(`pairing list --channel ${ch} --json`, { quiet: true });
128
- if (result.ok && result.stdout) {
206
+ const rawOutput = [result.stdout, result.stderr].filter(Boolean).join("\n");
207
+ if (rawOutput) {
129
208
  try {
130
- pending.push(...parsePendingPairings(result.stdout, ch));
209
+ pending.push(
210
+ ...mergePendingPairings(
211
+ parsePendingPairings(rawOutput, ch),
212
+ pendingFromStore,
213
+ ),
214
+ );
131
215
  } catch {
132
- // Ignore malformed output for a single channel and keep the rest of the response.
216
+ pending.push(...pendingFromStore);
133
217
  }
218
+ continue;
134
219
  }
220
+ pending.push(...pendingFromStore);
135
221
  }
136
222
 
137
- pairingCache = { pending, ts: Date.now() };
223
+ pairingCache = {
224
+ pending,
225
+ ts: Date.now(),
226
+ ttlMs: pending.length > 0 ? kPairingCacheTtlMs : kEmptyPairingCacheTtlMs,
227
+ };
138
228
  res.json({ pending });
139
229
  });
140
230
 
@@ -166,6 +256,7 @@ const registerPairingRoutes = ({ app, clawCmd, isOnboarded, fsModule = fs, openc
166
256
  ? `pairing approve --channel ${quoteCliArg(channel)} --account ${quoteCliArg(accountId)} ${quoteCliArg(pairingId)}`
167
257
  : `pairing approve ${quoteCliArg(channel)} ${quoteCliArg(pairingId)}`;
168
258
  const result = await clawCmd(approveCmd);
259
+ pairingCache.ts = 0;
169
260
  res.json(result);
170
261
  });
171
262
 
@@ -0,0 +1,11 @@
1
+ const getCommandOutputCandidates = (error) => {
2
+ const stdout = String(error?.stdout || "").trim();
3
+ const stderr = String(error?.stderr || "").trim();
4
+ const combined = [stdout, stderr].filter(Boolean).join("\n").trim();
5
+
6
+ return [...new Set([combined, stdout, stderr].filter(Boolean))];
7
+ };
8
+
9
+ module.exports = {
10
+ getCommandOutputCandidates,
11
+ };
@@ -18,5 +18,7 @@ db/**
18
18
  !hooks/transforms/**
19
19
  !cron/
20
20
  !cron/jobs.json
21
+ # OpenClaw 2026.4.20+: runtime execution state (job definitions stay in cron/jobs.json).
22
+ cron/jobs-state.json
21
23
  !openclaw.json
22
24
  !.gitignore
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrysb/alphaclaw",
3
- "version": "0.9.9",
3
+ "version": "0.9.10",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -36,7 +36,7 @@
36
36
  "dependencies": {
37
37
  "express": "^4.21.0",
38
38
  "http-proxy": "^1.18.1",
39
- "openclaw": "2026.4.15",
39
+ "openclaw": "2026.4.21",
40
40
  "patch-package": "^8.0.1",
41
41
  "ws": "^8.19.0"
42
42
  },
@@ -0,0 +1,13 @@
1
+ diff --git a/node_modules/openclaw/dist/server.impl-DLF59fRo.js b/node_modules/openclaw/dist/server.impl-DLF59fRo.js
2
+ index d6b4a5c..4e76dd9 100644
3
+ --- a/node_modules/openclaw/dist/server.impl-DLF59fRo.js
4
+ +++ b/node_modules/openclaw/dist/server.impl-DLF59fRo.js
5
+ @@ -22187,7 +22187,7 @@ function attachGatewayWsMessageHandler(params) {
6
+ close(1008, truncateCloseReason(authMessage));
7
+ };
8
+ const clearUnboundScopes = () => {
9
+ - if (scopes.length > 0) {
10
+ + if (scopes.length > 0 && !sharedAuthOk) {
11
+ scopes = [];
12
+ connectParams.scopes = scopes;
13
+ }
@@ -1,13 +0,0 @@
1
- diff --git a/node_modules/openclaw/dist/server.impl-GQ72oJBa.js b/node_modules/openclaw/dist/server.impl-GQ72oJBa.js
2
- index 70c273a..04a6d54 100644
3
- --- a/node_modules/openclaw/dist/server.impl-GQ72oJBa.js
4
- +++ b/node_modules/openclaw/dist/server.impl-GQ72oJBa.js
5
- @@ -22362,7 +22362,7 @@ function attachGatewayWsMessageHandler(params) {
6
- close(1008, truncateCloseReason(authMessage));
7
- };
8
- const clearUnboundScopes = () => {
9
- - if (scopes.length > 0) {
10
- + if (scopes.length > 0 && !sharedAuthOk) {
11
- scopes = [];
12
- connectParams.scopes = scopes;
13
- }