@chrysb/alphaclaw 0.9.8 → 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.
- package/lib/public/dist/app.bundle.js +1424 -1398
- package/lib/public/js/components/agents-tab/agent-overview/use-model-card.js +4 -5
- package/lib/public/js/components/agents-tab/agent-pairing-section.js +11 -5
- package/lib/public/js/components/cron-tab/cron-helpers.js +13 -1
- package/lib/public/js/components/cron-tab/use-cron-tab.js +4 -3
- package/lib/public/js/components/general/index.js +6 -1
- package/lib/public/js/components/general/use-general-tab.js +17 -20
- package/lib/public/js/components/models-tab/index.js +5 -1
- package/lib/public/js/components/models-tab/model-picker.js +52 -0
- package/lib/public/js/components/onboarding/use-welcome-pairing.js +4 -1
- package/lib/public/js/components/pairings.js +75 -4
- package/lib/public/js/hooks/usePolling.js +46 -13
- package/lib/public/js/lib/model-config.js +4 -0
- package/lib/server/agents/channels.js +53 -9
- package/lib/server/commands.js +4 -1
- package/lib/server/constants.js +5 -0
- package/lib/server/cost-utils.js +9 -0
- package/lib/server/cron-service.js +12 -1
- package/lib/server/db/doctor/index.js +9 -0
- package/lib/server/db/usage/index.js +13 -0
- package/lib/server/db/watchdog/index.js +13 -1
- package/lib/server/db/webhooks/index.js +13 -1
- package/lib/server/init/register-server-routes.js +2 -0
- package/lib/server/internal-files-migration.js +11 -1
- package/lib/server/model-catalog-cache.js +85 -6
- package/lib/server/onboarding/github.js +79 -2
- package/lib/server/openclaw-version.js +2 -1
- package/lib/server/routes/models.js +26 -0
- package/lib/server/routes/pairings.js +106 -15
- package/lib/server/utils/command-output.js +11 -0
- package/lib/setup/gitignore +2 -0
- package/package.json +2 -2
- package/patches/openclaw+2026.4.21.patch +13 -0
- package/patches/openclaw+2026.4.14.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
|
|
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 <
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
206
|
+
const rawOutput = [result.stdout, result.stderr].filter(Boolean).join("\n");
|
|
207
|
+
if (rawOutput) {
|
|
129
208
|
try {
|
|
130
|
-
pending.push(
|
|
209
|
+
pending.push(
|
|
210
|
+
...mergePendingPairings(
|
|
211
|
+
parsePendingPairings(rawOutput, ch),
|
|
212
|
+
pendingFromStore,
|
|
213
|
+
),
|
|
214
|
+
);
|
|
131
215
|
} catch {
|
|
132
|
-
|
|
216
|
+
pending.push(...pendingFromStore);
|
|
133
217
|
}
|
|
218
|
+
continue;
|
|
134
219
|
}
|
|
220
|
+
pending.push(...pendingFromStore);
|
|
135
221
|
}
|
|
136
222
|
|
|
137
|
-
pairingCache = {
|
|
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
|
+
};
|
package/lib/setup/gitignore
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chrysb/alphaclaw",
|
|
3
|
-
"version": "0.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.
|
|
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-BbJvXoPb.js b/node_modules/openclaw/dist/server.impl-BbJvXoPb.js
|
|
2
|
-
index a5be9f5d..72e3db5b 100644
|
|
3
|
-
--- a/node_modules/openclaw/dist/server.impl-BbJvXoPb.js
|
|
4
|
-
+++ b/node_modules/openclaw/dist/server.impl-BbJvXoPb.js
|
|
5
|
-
@@ -22272,7 +22272,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
|
-
}
|