@chrysb/alphaclaw 0.5.6 → 0.5.7-beta.0
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/bin/alphaclaw.js +6 -1
- package/lib/public/css/agents.css +92 -0
- package/lib/public/css/explorer.css +101 -0
- package/lib/public/css/shell.css +15 -4
- package/lib/public/js/app.js +69 -3
- package/lib/public/js/components/action-button.js +5 -0
- package/lib/public/js/components/agents-tab/agent-bindings-section/helpers.js +76 -0
- package/lib/public/js/components/agents-tab/agent-bindings-section/index.js +490 -0
- package/lib/public/js/components/agents-tab/agent-bindings-section/use-agent-bindings.js +256 -0
- package/lib/public/js/components/agents-tab/agent-detail-panel.js +74 -0
- package/lib/public/js/components/agents-tab/agent-identity-section.js +175 -0
- package/lib/public/js/components/agents-tab/agent-overview/index.js +53 -0
- package/lib/public/js/components/agents-tab/agent-overview/manage-card.js +44 -0
- package/lib/public/js/components/agents-tab/agent-overview/model-card.js +158 -0
- package/lib/public/js/components/agents-tab/agent-overview/use-model-card.js +169 -0
- package/lib/public/js/components/agents-tab/agent-overview/use-workspace-card.js +45 -0
- package/lib/public/js/components/agents-tab/agent-overview/workspace-card.js +47 -0
- package/lib/public/js/components/agents-tab/agent-pairing-section.js +265 -0
- package/lib/public/js/components/agents-tab/create-agent-modal.js +189 -0
- package/lib/public/js/components/agents-tab/create-channel-modal.js +323 -0
- package/lib/public/js/components/agents-tab/delete-agent-dialog.js +50 -0
- package/lib/public/js/components/agents-tab/edit-agent-modal.js +109 -0
- package/lib/public/js/components/agents-tab/index.js +148 -0
- package/lib/public/js/components/agents-tab/use-agents.js +89 -0
- package/lib/public/js/components/channel-account-status-badge.js +35 -0
- package/lib/public/js/components/channel-operations-panel.js +33 -0
- package/lib/public/js/components/channels.js +545 -60
- package/lib/public/js/components/envars.js +25 -4
- package/lib/public/js/components/general/index.js +21 -11
- package/lib/public/js/components/general/use-general-tab.js +78 -16
- package/lib/public/js/components/google/gmail-setup-wizard.js +1 -3
- package/lib/public/js/components/google/index.js +28 -30
- package/lib/public/js/components/icons.js +37 -0
- package/lib/public/js/components/models-tab/index.js +58 -224
- package/lib/public/js/components/models-tab/model-picker.js +212 -0
- package/lib/public/js/components/models-tab/use-models.js +17 -14
- package/lib/public/js/components/onboarding/use-welcome-pairing.js +4 -4
- package/lib/public/js/components/onboarding/welcome-pairing-step.js +2 -2
- package/lib/public/js/components/overflow-menu.js +122 -0
- package/lib/public/js/components/pairings.js +36 -8
- package/lib/public/js/components/routes/agents-route.js +27 -0
- package/lib/public/js/components/routes/general-route.js +2 -0
- package/lib/public/js/components/routes/index.js +1 -0
- package/lib/public/js/components/routes/telegram-route.js +2 -2
- package/lib/public/js/components/secret-input.js +8 -1
- package/lib/public/js/components/sidebar.js +64 -26
- package/lib/public/js/components/telegram-workspace/index.js +175 -74
- package/lib/public/js/components/telegram-workspace/manage.js +83 -10
- package/lib/public/js/components/telegram-workspace/onboarding.js +9 -8
- package/lib/public/js/components/webhooks.js +43 -18
- package/lib/public/js/hooks/use-app-shell-controller.js +7 -0
- package/lib/public/js/hooks/use-browse-navigation.js +8 -5
- package/lib/public/js/hooks/use-destination-session-selection.js +8 -1
- package/lib/public/js/lib/api.js +163 -9
- package/lib/public/js/lib/app-navigation.js +2 -1
- package/lib/public/js/lib/channel-create-operation.js +102 -0
- package/lib/public/js/lib/format.js +14 -0
- package/lib/public/js/lib/sse.js +51 -0
- package/lib/public/js/lib/telegram-api.js +38 -18
- package/lib/public/setup.html +1 -0
- package/lib/public/shared/browse-file-policies.json +0 -1
- package/lib/server/agents/service.js +1478 -0
- package/lib/server/constants.js +2 -2
- package/lib/server/env.js +3 -1
- package/lib/server/gateway.js +104 -20
- package/lib/server/gmail-watch.js +29 -2
- package/lib/server/onboarding/import/import-applier.js +0 -1
- package/lib/server/onboarding/index.js +0 -6
- package/lib/server/onboarding/workspace.js +73 -38
- package/lib/server/openclaw-config.js +23 -0
- package/lib/server/operation-events.js +141 -0
- package/lib/server/routes/agents.js +266 -0
- package/lib/server/routes/pairings.js +135 -25
- package/lib/server/routes/system.js +90 -10
- package/lib/server/routes/telegram.js +247 -51
- package/lib/server/telegram-workspace.js +61 -10
- package/lib/server/topic-registry.js +66 -7
- package/lib/server/watchdog.js +39 -1
- package/lib/server/webhooks.js +60 -12
- package/lib/server.js +21 -7
- package/lib/setup/core-prompts/AGENTS.md +6 -5
- package/lib/setup/core-prompts/TOOLS.md +1 -8
- package/package.json +1 -1
- package/lib/setup/skills/control-ui/SKILL.md +0 -62
|
@@ -51,6 +51,78 @@ const buildTelegramGitSyncCommand = (action, target = "") => {
|
|
|
51
51
|
return `alphaclaw git-sync -m ${quoteShellArg(message, { strategy: "single" })}`;
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
+
const { createTelegramApi } = require("../telegram-api");
|
|
55
|
+
|
|
56
|
+
const kTelegramEnvKeyBase = "TELEGRAM_BOT_TOKEN";
|
|
57
|
+
const normalizeAccountId = (value) => String(value || "").trim() || "default";
|
|
58
|
+
|
|
59
|
+
const deriveAccountEnvKey = (accountId) => {
|
|
60
|
+
const normalized = normalizeAccountId(accountId);
|
|
61
|
+
if (normalized === "default") return kTelegramEnvKeyBase;
|
|
62
|
+
return `${kTelegramEnvKeyBase}_${normalized.replace(/-/g, "_").toUpperCase()}`;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const resolveAccountTelegramApi = (accountId, defaultApi) => {
|
|
66
|
+
const normalized = normalizeAccountId(accountId);
|
|
67
|
+
if (normalized === "default") return defaultApi;
|
|
68
|
+
const envKey = deriveAccountEnvKey(normalized);
|
|
69
|
+
const token = process.env[envKey];
|
|
70
|
+
if (!token) {
|
|
71
|
+
console.log(
|
|
72
|
+
`[alphaclaw] Telegram account "${normalized}": env var ${envKey} not found, falling back to default token`,
|
|
73
|
+
);
|
|
74
|
+
return defaultApi;
|
|
75
|
+
}
|
|
76
|
+
return createTelegramApi(() => process.env[envKey]);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const resolveAccountId = (req) =>
|
|
80
|
+
normalizeAccountId(req.query?.accountId || req.body?.accountId || "");
|
|
81
|
+
|
|
82
|
+
const resolveTelegramConfigForAccount = ({ telegramConfig, accountId }) => {
|
|
83
|
+
const normalizedAccountId = normalizeAccountId(accountId);
|
|
84
|
+
const accounts =
|
|
85
|
+
telegramConfig?.accounts && typeof telegramConfig.accounts === "object"
|
|
86
|
+
? telegramConfig.accounts
|
|
87
|
+
: null;
|
|
88
|
+
const hasAccounts = !!accounts && Object.keys(accounts).length > 0;
|
|
89
|
+
if (hasAccounts) {
|
|
90
|
+
const accountConfig =
|
|
91
|
+
accounts[normalizedAccountId] &&
|
|
92
|
+
typeof accounts[normalizedAccountId] === "object"
|
|
93
|
+
? accounts[normalizedAccountId]
|
|
94
|
+
: {};
|
|
95
|
+
return { normalizedAccountId, hasAccounts, accountConfig };
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
normalizedAccountId,
|
|
99
|
+
hasAccounts: false,
|
|
100
|
+
accountConfig: telegramConfig || {},
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const hasScopedBindingFields = (match) =>
|
|
105
|
+
!!match.peer ||
|
|
106
|
+
!!match.parentPeer ||
|
|
107
|
+
!!String(match.guildId || "").trim() ||
|
|
108
|
+
!!String(match.teamId || "").trim() ||
|
|
109
|
+
(Array.isArray(match.roles) && match.roles.length > 0);
|
|
110
|
+
|
|
111
|
+
const resolveBoundAgentIdForAccount = ({ cfg, accountId }) => {
|
|
112
|
+
const bindings = Array.isArray(cfg?.bindings) ? cfg.bindings : [];
|
|
113
|
+
const normalizedAccountId = normalizeAccountId(accountId);
|
|
114
|
+
for (const binding of bindings) {
|
|
115
|
+
const match = binding?.match || {};
|
|
116
|
+
if (hasScopedBindingFields(match)) continue;
|
|
117
|
+
if (String(match.channel || "").trim() !== "telegram") continue;
|
|
118
|
+
const bindingAccountId = normalizeAccountId(match.accountId);
|
|
119
|
+
if (bindingAccountId !== normalizedAccountId) continue;
|
|
120
|
+
const boundAgentId = String(binding?.agentId || "").trim();
|
|
121
|
+
if (boundAgentId) return boundAgentId;
|
|
122
|
+
}
|
|
123
|
+
return normalizedAccountId === "default" ? "default" : "";
|
|
124
|
+
};
|
|
125
|
+
|
|
54
126
|
const registerTelegramRoutes = ({
|
|
55
127
|
app,
|
|
56
128
|
telegramApi,
|
|
@@ -59,18 +131,24 @@ const registerTelegramRoutes = ({
|
|
|
59
131
|
}) => {
|
|
60
132
|
const repairGroupAllowFromIfMissing = async ({
|
|
61
133
|
cfg,
|
|
134
|
+
accountId = "default",
|
|
62
135
|
groupId,
|
|
63
136
|
requireMention = false,
|
|
137
|
+
tgApi = telegramApi,
|
|
64
138
|
}) => {
|
|
65
139
|
const telegramConfig = cfg?.channels?.telegram || {};
|
|
140
|
+
const { accountConfig } = resolveTelegramConfigForAccount({
|
|
141
|
+
telegramConfig,
|
|
142
|
+
accountId,
|
|
143
|
+
});
|
|
66
144
|
if (
|
|
67
|
-
Array.isArray(
|
|
68
|
-
|
|
145
|
+
Array.isArray(accountConfig.groupAllowFrom) &&
|
|
146
|
+
accountConfig.groupAllowFrom.length > 0
|
|
69
147
|
) {
|
|
70
148
|
return { repaired: false, resolvedUserId: "", syncWarning: null };
|
|
71
149
|
}
|
|
72
150
|
const resolvedUserId = await resolveAllowUserId({
|
|
73
|
-
telegramApi,
|
|
151
|
+
telegramApi: tgApi,
|
|
74
152
|
groupId,
|
|
75
153
|
preferredUserId: "",
|
|
76
154
|
});
|
|
@@ -79,6 +157,7 @@ const registerTelegramRoutes = ({
|
|
|
79
157
|
openclawDir: OPENCLAW_DIR,
|
|
80
158
|
topicRegistry,
|
|
81
159
|
groupId,
|
|
160
|
+
accountId,
|
|
82
161
|
requireMention,
|
|
83
162
|
resolvedUserId,
|
|
84
163
|
});
|
|
@@ -104,8 +183,10 @@ const registerTelegramRoutes = ({
|
|
|
104
183
|
// Verify bot token
|
|
105
184
|
app.get("/api/telegram/bot", async (req, res) => {
|
|
106
185
|
try {
|
|
107
|
-
const
|
|
108
|
-
|
|
186
|
+
const reqAccountId = resolveAccountId(req);
|
|
187
|
+
const tgApi = resolveAccountTelegramApi(reqAccountId, telegramApi);
|
|
188
|
+
const me = await tgApi.getMe();
|
|
189
|
+
res.json({ ok: true, bot: me, accountId: reqAccountId || "default" });
|
|
109
190
|
} catch (e) {
|
|
110
191
|
res.json({ ok: false, error: e.message });
|
|
111
192
|
}
|
|
@@ -118,11 +199,15 @@ const registerTelegramRoutes = ({
|
|
|
118
199
|
return res.status(400).json({ ok: false, error: "groupId is required" });
|
|
119
200
|
|
|
120
201
|
try {
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
const member = await telegramApi.getChatMember(groupId, me.id);
|
|
124
|
-
const suggestedUserId = await resolveAllowUserId({
|
|
202
|
+
const tgApi = resolveAccountTelegramApi(
|
|
203
|
+
resolveAccountId(req),
|
|
125
204
|
telegramApi,
|
|
205
|
+
);
|
|
206
|
+
const chat = await tgApi.getChat(groupId);
|
|
207
|
+
const me = await tgApi.getMe();
|
|
208
|
+
const member = await tgApi.getChatMember(groupId, me.id);
|
|
209
|
+
const suggestedUserId = await resolveAllowUserId({
|
|
210
|
+
telegramApi: tgApi,
|
|
126
211
|
groupId,
|
|
127
212
|
preferredUserId: "",
|
|
128
213
|
});
|
|
@@ -166,6 +251,8 @@ const registerTelegramRoutes = ({
|
|
|
166
251
|
const systemInstructions = String(
|
|
167
252
|
body.systemInstructions ?? body.systemPrompt ?? "",
|
|
168
253
|
).trim();
|
|
254
|
+
const hasAgentId = Object.prototype.hasOwnProperty.call(body, "agentId");
|
|
255
|
+
const agentId = String(body.agentId ?? "").trim();
|
|
169
256
|
const iconColorValue =
|
|
170
257
|
rawIconColor == null ? null : Number.parseInt(String(rawIconColor), 10);
|
|
171
258
|
const iconColor = Number.isFinite(iconColorValue)
|
|
@@ -175,7 +262,11 @@ const registerTelegramRoutes = ({
|
|
|
175
262
|
return res.status(400).json({ ok: false, error: "name is required" });
|
|
176
263
|
|
|
177
264
|
try {
|
|
178
|
-
const
|
|
265
|
+
const tgApi = resolveAccountTelegramApi(
|
|
266
|
+
resolveAccountId(req),
|
|
267
|
+
telegramApi,
|
|
268
|
+
);
|
|
269
|
+
const result = await tgApi.createForumTopic(groupId, name, {
|
|
179
270
|
iconColor,
|
|
180
271
|
});
|
|
181
272
|
const threadId = result.message_thread_id;
|
|
@@ -183,12 +274,14 @@ const registerTelegramRoutes = ({
|
|
|
183
274
|
name: result.name,
|
|
184
275
|
iconColor: result.icon_color,
|
|
185
276
|
...(systemInstructions ? { systemInstructions } : {}),
|
|
277
|
+
...(hasAgentId ? { agentId: agentId || undefined } : {}),
|
|
186
278
|
});
|
|
187
279
|
syncConfigForTelegram({
|
|
188
280
|
fs,
|
|
189
281
|
openclawDir: OPENCLAW_DIR,
|
|
190
282
|
topicRegistry,
|
|
191
283
|
groupId,
|
|
284
|
+
accountId: resolveAccountId(req),
|
|
192
285
|
requireMention: false,
|
|
193
286
|
resolvedUserId: "",
|
|
194
287
|
});
|
|
@@ -200,6 +293,7 @@ const registerTelegramRoutes = ({
|
|
|
200
293
|
threadId,
|
|
201
294
|
name: result.name,
|
|
202
295
|
iconColor: result.icon_color,
|
|
296
|
+
...(hasAgentId ? { agentId } : {}),
|
|
203
297
|
},
|
|
204
298
|
syncWarning,
|
|
205
299
|
});
|
|
@@ -219,6 +313,7 @@ const registerTelegramRoutes = ({
|
|
|
219
313
|
.json({ ok: false, error: "topics array is required" });
|
|
220
314
|
}
|
|
221
315
|
|
|
316
|
+
const tgApi = resolveAccountTelegramApi(resolveAccountId(req), telegramApi);
|
|
222
317
|
const results = [];
|
|
223
318
|
for (const t of topics) {
|
|
224
319
|
if (!t.name) {
|
|
@@ -226,17 +321,20 @@ const registerTelegramRoutes = ({
|
|
|
226
321
|
continue;
|
|
227
322
|
}
|
|
228
323
|
try {
|
|
229
|
-
const result = await
|
|
324
|
+
const result = await tgApi.createForumTopic(groupId, t.name, {
|
|
230
325
|
iconColor: t.iconColor || undefined,
|
|
231
326
|
});
|
|
232
327
|
const threadId = result.message_thread_id;
|
|
233
328
|
const systemInstructions = String(
|
|
234
329
|
t.systemInstructions ?? t.systemPrompt ?? "",
|
|
235
330
|
).trim();
|
|
331
|
+
const hasAgentId = Object.prototype.hasOwnProperty.call(t, "agentId");
|
|
332
|
+
const agentId = String(t.agentId ?? "").trim();
|
|
236
333
|
topicRegistry.addTopic(groupId, threadId, {
|
|
237
334
|
name: result.name,
|
|
238
335
|
iconColor: result.icon_color,
|
|
239
336
|
...(systemInstructions ? { systemInstructions } : {}),
|
|
337
|
+
...(hasAgentId ? { agentId: agentId || undefined } : {}),
|
|
240
338
|
});
|
|
241
339
|
results.push({ name: result.name, threadId, ok: true });
|
|
242
340
|
} catch (e) {
|
|
@@ -248,6 +346,7 @@ const registerTelegramRoutes = ({
|
|
|
248
346
|
openclawDir: OPENCLAW_DIR,
|
|
249
347
|
topicRegistry,
|
|
250
348
|
groupId,
|
|
349
|
+
accountId: resolveAccountId(req),
|
|
251
350
|
requireMention: false,
|
|
252
351
|
resolvedUserId: "",
|
|
253
352
|
});
|
|
@@ -262,13 +361,18 @@ const registerTelegramRoutes = ({
|
|
|
262
361
|
async (req, res) => {
|
|
263
362
|
const { groupId, topicId } = req.params;
|
|
264
363
|
try {
|
|
265
|
-
|
|
364
|
+
const tgApi = resolveAccountTelegramApi(
|
|
365
|
+
resolveAccountId(req),
|
|
366
|
+
telegramApi,
|
|
367
|
+
);
|
|
368
|
+
await tgApi.deleteForumTopic(groupId, parseInt(topicId, 10));
|
|
266
369
|
topicRegistry.removeTopic(groupId, topicId);
|
|
267
370
|
syncConfigForTelegram({
|
|
268
371
|
fs,
|
|
269
372
|
openclawDir: OPENCLAW_DIR,
|
|
270
373
|
topicRegistry,
|
|
271
374
|
groupId,
|
|
375
|
+
accountId: resolveAccountId(req),
|
|
272
376
|
requireMention: false,
|
|
273
377
|
resolvedUserId: "",
|
|
274
378
|
});
|
|
@@ -285,6 +389,7 @@ const registerTelegramRoutes = ({
|
|
|
285
389
|
openclawDir: OPENCLAW_DIR,
|
|
286
390
|
topicRegistry,
|
|
287
391
|
groupId,
|
|
392
|
+
accountId: resolveAccountId(req),
|
|
288
393
|
requireMention: false,
|
|
289
394
|
resolvedUserId: "",
|
|
290
395
|
});
|
|
@@ -304,7 +409,7 @@ const registerTelegramRoutes = ({
|
|
|
304
409
|
},
|
|
305
410
|
);
|
|
306
411
|
|
|
307
|
-
//
|
|
412
|
+
// Update a topic (rename, system instructions, agent routing)
|
|
308
413
|
app.put("/api/telegram/groups/:groupId/topics/:topicId", async (req, res) => {
|
|
309
414
|
const { groupId, topicId } = req.params;
|
|
310
415
|
const body = req.body || {};
|
|
@@ -315,6 +420,8 @@ const registerTelegramRoutes = ({
|
|
|
315
420
|
const systemInstructions = String(
|
|
316
421
|
body.systemInstructions ?? body.systemPrompt ?? "",
|
|
317
422
|
).trim();
|
|
423
|
+
const hasAgentId = Object.prototype.hasOwnProperty.call(body, "agentId");
|
|
424
|
+
const agentId = String(body.agentId ?? "").trim();
|
|
318
425
|
if (!name)
|
|
319
426
|
return res.status(400).json({ ok: false, error: "name is required" });
|
|
320
427
|
try {
|
|
@@ -324,13 +431,17 @@ const registerTelegramRoutes = ({
|
|
|
324
431
|
.status(400)
|
|
325
432
|
.json({ ok: false, error: "topicId must be numeric" });
|
|
326
433
|
}
|
|
434
|
+
const tgApi = resolveAccountTelegramApi(
|
|
435
|
+
resolveAccountId(req),
|
|
436
|
+
telegramApi,
|
|
437
|
+
);
|
|
327
438
|
const existingTopic =
|
|
328
439
|
topicRegistry.getGroup(groupId)?.topics?.[String(threadId)] || {};
|
|
329
440
|
const existingName = String(existingTopic.name || "").trim();
|
|
330
441
|
const shouldRename = !existingName || existingName !== name;
|
|
331
442
|
if (shouldRename) {
|
|
332
443
|
try {
|
|
333
|
-
await
|
|
444
|
+
await tgApi.editForumTopic(groupId, threadId, { name });
|
|
334
445
|
} catch (e) {
|
|
335
446
|
// Telegram returns TOPIC_NOT_MODIFIED when the name is unchanged.
|
|
336
447
|
if (!String(e.message || "").includes("TOPIC_NOT_MODIFIED")) {
|
|
@@ -342,23 +453,26 @@ const registerTelegramRoutes = ({
|
|
|
342
453
|
...existingTopic,
|
|
343
454
|
name,
|
|
344
455
|
...(hasSystemInstructions ? { systemInstructions } : {}),
|
|
456
|
+
...(hasAgentId ? { agentId: agentId || undefined } : {}),
|
|
345
457
|
});
|
|
346
458
|
syncConfigForTelegram({
|
|
347
459
|
fs,
|
|
348
460
|
openclawDir: OPENCLAW_DIR,
|
|
349
461
|
topicRegistry,
|
|
350
462
|
groupId,
|
|
463
|
+
accountId: resolveAccountId(req),
|
|
351
464
|
requireMention: false,
|
|
352
465
|
resolvedUserId: "",
|
|
353
466
|
});
|
|
354
467
|
syncPromptFiles();
|
|
355
|
-
const syncWarning = await runTelegramGitSync("
|
|
468
|
+
const syncWarning = await runTelegramGitSync("update-topic", name);
|
|
356
469
|
return res.json({
|
|
357
470
|
ok: true,
|
|
358
471
|
topic: {
|
|
359
472
|
threadId,
|
|
360
473
|
name,
|
|
361
474
|
...(hasSystemInstructions ? { systemInstructions } : {}),
|
|
475
|
+
...(hasAgentId ? { agentId } : {}),
|
|
362
476
|
},
|
|
363
477
|
syncWarning,
|
|
364
478
|
});
|
|
@@ -373,10 +487,15 @@ const registerTelegramRoutes = ({
|
|
|
373
487
|
const body = req.body || {};
|
|
374
488
|
const userId = body.userId ?? "";
|
|
375
489
|
const groupName = body.groupName ?? "";
|
|
490
|
+
const accountId = resolveAccountId(req);
|
|
376
491
|
const requireMention = parseBooleanValue(body.requireMention, false);
|
|
377
492
|
try {
|
|
378
|
-
const
|
|
493
|
+
const tgApi = resolveAccountTelegramApi(
|
|
494
|
+
accountId,
|
|
379
495
|
telegramApi,
|
|
496
|
+
);
|
|
497
|
+
const resolvedUserId = await resolveAllowUserId({
|
|
498
|
+
telegramApi: tgApi,
|
|
380
499
|
groupId,
|
|
381
500
|
preferredUserId: userId,
|
|
382
501
|
});
|
|
@@ -385,15 +504,21 @@ const registerTelegramRoutes = ({
|
|
|
385
504
|
openclawDir: OPENCLAW_DIR,
|
|
386
505
|
topicRegistry,
|
|
387
506
|
groupId,
|
|
507
|
+
accountId,
|
|
388
508
|
requireMention,
|
|
389
509
|
resolvedUserId,
|
|
390
510
|
});
|
|
391
511
|
|
|
392
512
|
// Save metadata in local topic registry only.
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
513
|
+
const configPath = `${OPENCLAW_DIR}/openclaw.json`;
|
|
514
|
+
const cfg = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
515
|
+
const boundAgentId = resolveBoundAgentIdForAccount({ cfg, accountId });
|
|
516
|
+
topicRegistry.setGroup(groupId, {
|
|
517
|
+
...(groupName ? { name: groupName } : {}),
|
|
518
|
+
accountId,
|
|
519
|
+
...(boundAgentId ? { agentId: boundAgentId } : {}),
|
|
520
|
+
});
|
|
521
|
+
syncPromptFiles();
|
|
397
522
|
const syncWarning = await runTelegramGitSync("configure-group", groupId);
|
|
398
523
|
|
|
399
524
|
res.json({ ok: true, userId: resolvedUserId || null, syncWarning });
|
|
@@ -410,47 +535,95 @@ const registerTelegramRoutes = ({
|
|
|
410
535
|
// Workspace bootstrap info (lets UI jump straight to management)
|
|
411
536
|
app.get("/api/telegram/workspace", async (req, res) => {
|
|
412
537
|
try {
|
|
538
|
+
const accountId = resolveAccountId(req);
|
|
413
539
|
const debugEnabled = isDebugEnabled();
|
|
414
540
|
const configPath = `${OPENCLAW_DIR}/openclaw.json`;
|
|
415
541
|
let cfg = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
416
542
|
let telegramConfig = cfg.channels?.telegram || {};
|
|
417
|
-
const
|
|
543
|
+
const { accountConfig } = resolveTelegramConfigForAccount({
|
|
544
|
+
telegramConfig,
|
|
545
|
+
accountId,
|
|
546
|
+
});
|
|
547
|
+
const configuredGroups =
|
|
548
|
+
accountConfig?.groups && typeof accountConfig.groups === "object"
|
|
549
|
+
? accountConfig.groups
|
|
550
|
+
: {};
|
|
418
551
|
const groupIds = Object.keys(configuredGroups);
|
|
419
|
-
|
|
420
|
-
|
|
552
|
+
const registryFallbackGroups = topicRegistry.getGroupsForAccount(accountId);
|
|
553
|
+
const registryFallbackGroupIds = Object.keys(registryFallbackGroups);
|
|
554
|
+
const useRegistryFallback =
|
|
555
|
+
groupIds.length === 0 && registryFallbackGroupIds.length > 0;
|
|
556
|
+
if (groupIds.length === 0 && !useRegistryFallback) {
|
|
557
|
+
return res.json({
|
|
558
|
+
ok: true,
|
|
559
|
+
configured: false,
|
|
560
|
+
groups: [],
|
|
561
|
+
debugEnabled,
|
|
562
|
+
});
|
|
421
563
|
}
|
|
422
|
-
|
|
423
|
-
const
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
564
|
+
|
|
565
|
+
const tgApi = resolveAccountTelegramApi(accountId, telegramApi);
|
|
566
|
+
let activeGroupIds = useRegistryFallback ? registryFallbackGroupIds : groupIds;
|
|
567
|
+
if (!useRegistryFallback) {
|
|
568
|
+
let anyRepaired = false;
|
|
569
|
+
for (const gId of groupIds) {
|
|
570
|
+
const gConfig = configuredGroups[gId] || {};
|
|
571
|
+
const repairResult = await repairGroupAllowFromIfMissing({
|
|
572
|
+
cfg,
|
|
573
|
+
accountId,
|
|
574
|
+
groupId: gId,
|
|
575
|
+
requireMention: !!gConfig.requireMention,
|
|
576
|
+
tgApi,
|
|
577
|
+
});
|
|
578
|
+
if (repairResult.repaired) {
|
|
579
|
+
anyRepaired = true;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
if (anyRepaired) {
|
|
583
|
+
cfg = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
584
|
+
telegramConfig = cfg.channels?.telegram || {};
|
|
585
|
+
}
|
|
586
|
+
const refreshedAccountConfig = resolveTelegramConfigForAccount({
|
|
587
|
+
telegramConfig,
|
|
588
|
+
accountId,
|
|
589
|
+
}).accountConfig;
|
|
590
|
+
const refreshedGroups =
|
|
591
|
+
refreshedAccountConfig?.groups &&
|
|
592
|
+
typeof refreshedAccountConfig.groups === "object"
|
|
593
|
+
? refreshedAccountConfig.groups
|
|
594
|
+
: {};
|
|
595
|
+
activeGroupIds = Object.keys(refreshedGroups);
|
|
432
596
|
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
const
|
|
437
|
-
|
|
438
|
-
|
|
597
|
+
|
|
598
|
+
const groups = [];
|
|
599
|
+
for (const gId of activeGroupIds) {
|
|
600
|
+
const registryGroup = topicRegistry.getGroup(gId);
|
|
601
|
+
let gName = registryGroup?.name || gId;
|
|
602
|
+
try {
|
|
603
|
+
const chat = await tgApi.getChat(gId);
|
|
604
|
+
if (chat?.title) gName = chat.title;
|
|
605
|
+
} catch {}
|
|
606
|
+
groups.push({
|
|
607
|
+
groupId: gId,
|
|
608
|
+
groupName: gName,
|
|
609
|
+
topics: registryGroup?.topics || {},
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
const first = groups[0] || {};
|
|
439
614
|
return res.json({
|
|
440
615
|
ok: true,
|
|
441
616
|
configured: true,
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
617
|
+
groups,
|
|
618
|
+
groupId: first.groupId,
|
|
619
|
+
groupName: first.groupName,
|
|
620
|
+
topics: first.topics,
|
|
445
621
|
debugEnabled,
|
|
446
622
|
concurrency: {
|
|
447
623
|
agentMaxConcurrent: cfg.agents?.defaults?.maxConcurrent ?? null,
|
|
448
624
|
subagentMaxConcurrent:
|
|
449
625
|
cfg.agents?.defaults?.subagents?.maxConcurrent ?? null,
|
|
450
626
|
},
|
|
451
|
-
repairedGroupAllowFrom: !!repairResult.repaired,
|
|
452
|
-
repairedUserId: repairResult.resolvedUserId || null,
|
|
453
|
-
syncWarning: repairResult.syncWarning || null,
|
|
454
627
|
});
|
|
455
628
|
} catch (e) {
|
|
456
629
|
return res.json({ ok: false, error: e.message });
|
|
@@ -460,26 +633,49 @@ const registerTelegramRoutes = ({
|
|
|
460
633
|
// Reset Telegram workspace onboarding state
|
|
461
634
|
app.post("/api/telegram/workspace/reset", async (req, res) => {
|
|
462
635
|
try {
|
|
636
|
+
const accountId = resolveAccountId(req);
|
|
463
637
|
const configPath = `${OPENCLAW_DIR}/openclaw.json`;
|
|
464
638
|
const cfg = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
465
|
-
const
|
|
466
|
-
if (
|
|
467
|
-
|
|
468
|
-
|
|
639
|
+
const telegramConfig = cfg.channels?.telegram;
|
|
640
|
+
if (!telegramConfig || typeof telegramConfig !== "object") {
|
|
641
|
+
return res.json({ ok: true, syncWarning: null });
|
|
642
|
+
}
|
|
643
|
+
const { normalizedAccountId, hasAccounts, accountConfig } =
|
|
644
|
+
resolveTelegramConfigForAccount({
|
|
645
|
+
telegramConfig,
|
|
646
|
+
accountId,
|
|
647
|
+
});
|
|
648
|
+
const telegramGroups = Object.keys(accountConfig?.groups || {});
|
|
649
|
+
const groupsToRemove =
|
|
650
|
+
telegramGroups.length > 0
|
|
651
|
+
? telegramGroups
|
|
652
|
+
: Object.keys(topicRegistry.getGroupsForAccount(accountId));
|
|
653
|
+
if (hasAccounts) {
|
|
654
|
+
const accountEntry = telegramConfig.accounts?.[normalizedAccountId];
|
|
655
|
+
if (accountEntry && typeof accountEntry === "object") {
|
|
656
|
+
delete accountEntry.groups;
|
|
657
|
+
delete accountEntry.groupAllowFrom;
|
|
658
|
+
}
|
|
659
|
+
} else {
|
|
660
|
+
delete telegramConfig.groups;
|
|
661
|
+
delete telegramConfig.groupAllowFrom;
|
|
469
662
|
}
|
|
470
663
|
fs.writeFileSync(configPath, JSON.stringify(cfg, null, 2));
|
|
471
664
|
|
|
472
665
|
// Remove corresponding groups from topic registry
|
|
473
666
|
const registry = topicRegistry.readRegistry();
|
|
474
667
|
if (registry && registry.groups) {
|
|
475
|
-
for (const groupId of
|
|
668
|
+
for (const groupId of groupsToRemove) {
|
|
476
669
|
delete registry.groups[groupId];
|
|
477
670
|
}
|
|
478
671
|
topicRegistry.writeRegistry(registry);
|
|
479
672
|
}
|
|
480
673
|
|
|
481
674
|
syncPromptFiles();
|
|
482
|
-
const syncWarning = await runTelegramGitSync(
|
|
675
|
+
const syncWarning = await runTelegramGitSync(
|
|
676
|
+
"reset-workspace",
|
|
677
|
+
"telegram",
|
|
678
|
+
);
|
|
483
679
|
return res.json({ ok: true, syncWarning });
|
|
484
680
|
} catch (e) {
|
|
485
681
|
return res.json({ ok: false, error: e.message });
|
|
@@ -2,11 +2,39 @@ const kTelegramTopicConcurrencyMultiplier = 3;
|
|
|
2
2
|
const kAgentConcurrencyFloor = 8;
|
|
3
3
|
const kSubagentConcurrencyFloor = 4;
|
|
4
4
|
|
|
5
|
+
const normalizeAccountId = (value) => String(value || "").trim() || "default";
|
|
6
|
+
|
|
7
|
+
const resolveTelegramAccountConfig = ({ telegramConfig, accountId }) => {
|
|
8
|
+
const normalizedAccountId = normalizeAccountId(accountId);
|
|
9
|
+
const accounts =
|
|
10
|
+
telegramConfig?.accounts && typeof telegramConfig.accounts === "object"
|
|
11
|
+
? telegramConfig.accounts
|
|
12
|
+
: null;
|
|
13
|
+
const hasAccounts = !!accounts && Object.keys(accounts).length > 0;
|
|
14
|
+
if (hasAccounts) {
|
|
15
|
+
const nextAccountConfig =
|
|
16
|
+
accounts[normalizedAccountId] && typeof accounts[normalizedAccountId] === "object"
|
|
17
|
+
? accounts[normalizedAccountId]
|
|
18
|
+
: {};
|
|
19
|
+
return {
|
|
20
|
+
normalizedAccountId,
|
|
21
|
+
hasAccounts,
|
|
22
|
+
accountConfig: nextAccountConfig,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
normalizedAccountId,
|
|
27
|
+
hasAccounts: false,
|
|
28
|
+
accountConfig: telegramConfig,
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
5
32
|
const syncConfigForTelegram = ({
|
|
6
33
|
fs,
|
|
7
34
|
openclawDir,
|
|
8
35
|
topicRegistry,
|
|
9
36
|
groupId,
|
|
37
|
+
accountId = "default",
|
|
10
38
|
requireMention = false,
|
|
11
39
|
resolvedUserId = "",
|
|
12
40
|
}) => {
|
|
@@ -20,9 +48,32 @@ const syncConfigForTelegram = ({
|
|
|
20
48
|
|
|
21
49
|
if (!cfg.channels) cfg.channels = {};
|
|
22
50
|
if (!cfg.channels.telegram) cfg.channels.telegram = {};
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
51
|
+
const telegramConfig = cfg.channels.telegram;
|
|
52
|
+
const { normalizedAccountId, hasAccounts, accountConfig } =
|
|
53
|
+
resolveTelegramAccountConfig({
|
|
54
|
+
telegramConfig,
|
|
55
|
+
accountId,
|
|
56
|
+
});
|
|
57
|
+
if (hasAccounts) {
|
|
58
|
+
if (!telegramConfig.accounts || typeof telegramConfig.accounts !== "object") {
|
|
59
|
+
telegramConfig.accounts = {};
|
|
60
|
+
}
|
|
61
|
+
if (
|
|
62
|
+
!telegramConfig.accounts[normalizedAccountId]
|
|
63
|
+
|| typeof telegramConfig.accounts[normalizedAccountId] !== "object"
|
|
64
|
+
) {
|
|
65
|
+
telegramConfig.accounts[normalizedAccountId] = {};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const targetConfig = hasAccounts
|
|
69
|
+
? telegramConfig.accounts[normalizedAccountId]
|
|
70
|
+
: telegramConfig;
|
|
71
|
+
|
|
72
|
+
if (!targetConfig.groups || typeof targetConfig.groups !== "object") {
|
|
73
|
+
targetConfig.groups = {};
|
|
74
|
+
}
|
|
75
|
+
const existingGroupConfig = targetConfig.groups[groupId] || {};
|
|
76
|
+
targetConfig.groups[groupId] = {
|
|
26
77
|
...existingGroupConfig,
|
|
27
78
|
requireMention,
|
|
28
79
|
};
|
|
@@ -35,20 +86,20 @@ const syncConfigForTelegram = ({
|
|
|
35
86
|
promptTopics[threadId] = { systemPrompt };
|
|
36
87
|
}
|
|
37
88
|
if (Object.keys(promptTopics).length > 0) {
|
|
38
|
-
|
|
89
|
+
targetConfig.groups[groupId].topics = promptTopics;
|
|
39
90
|
} else {
|
|
40
|
-
delete
|
|
91
|
+
delete targetConfig.groups[groupId].topics;
|
|
41
92
|
}
|
|
42
93
|
|
|
43
|
-
|
|
44
|
-
if (!Array.isArray(
|
|
45
|
-
|
|
94
|
+
targetConfig.groupPolicy = "allowlist";
|
|
95
|
+
if (!Array.isArray(targetConfig.groupAllowFrom)) {
|
|
96
|
+
targetConfig.groupAllowFrom = [];
|
|
46
97
|
}
|
|
47
98
|
if (
|
|
48
99
|
resolvedUserId
|
|
49
|
-
&& !
|
|
100
|
+
&& !targetConfig.groupAllowFrom.includes(String(resolvedUserId))
|
|
50
101
|
) {
|
|
51
|
-
|
|
102
|
+
targetConfig.groupAllowFrom.push(String(resolvedUserId));
|
|
52
103
|
}
|
|
53
104
|
|
|
54
105
|
// Persist thread sessions and keep concurrency in schema-valid agent defaults.
|