@songsid/agend 2.1.4-beta.9 → 2.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.
- package/README.md +1 -1
- package/README.zh-TW.md +1 -1
- package/dist/agent-cli-instructions.md +2 -2
- package/dist/agent-endpoint.js +1 -1
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/backend/antigravity.d.ts +12 -8
- package/dist/backend/antigravity.js +189 -42
- package/dist/backend/antigravity.js.map +1 -1
- package/dist/backend/claude-code.d.ts +87 -23
- package/dist/backend/claude-code.js +361 -46
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex.d.ts +18 -0
- package/dist/backend/codex.js +139 -5
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/gemini-cli.js +2 -2
- package/dist/backend/gemini-cli.js.map +1 -1
- package/dist/backend/grok.js +2 -2
- package/dist/backend/grok.js.map +1 -1
- package/dist/backend/kiro.d.ts +52 -2
- package/dist/backend/kiro.js +267 -12
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/opencode.js +17 -4
- package/dist/backend/opencode.js.map +1 -1
- package/dist/backend/types.d.ts +122 -3
- package/dist/backend/types.js +5 -1
- package/dist/backend/types.js.map +1 -1
- package/dist/backend-outage.d.ts +61 -0
- package/dist/backend-outage.js +71 -0
- package/dist/backend-outage.js.map +1 -0
- package/dist/channel/adapters/discord.d.ts +44 -2
- package/dist/channel/adapters/discord.js +523 -90
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +30 -0
- package/dist/channel/adapters/telegram.js +221 -19
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/agy-mcp-launcher.d.ts +2 -0
- package/dist/channel/agy-mcp-launcher.js +28 -0
- package/dist/channel/agy-mcp-launcher.js.map +1 -0
- package/dist/channel/ipc-bridge.d.ts +2 -1
- package/dist/channel/ipc-bridge.js +33 -4
- package/dist/channel/ipc-bridge.js.map +1 -1
- package/dist/channel/markdown-chunk.d.ts +42 -0
- package/dist/channel/markdown-chunk.js +205 -0
- package/dist/channel/markdown-chunk.js.map +1 -0
- package/dist/channel/mcp-server.js +34 -5
- package/dist/channel/mcp-server.js.map +1 -1
- package/dist/channel/mcp-tools.js +5 -3
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/message-queue.js +64 -8
- package/dist/channel/message-queue.js.map +1 -1
- package/dist/channel/types.d.ts +31 -1
- package/dist/classic-channel-manager.d.ts +65 -0
- package/dist/classic-channel-manager.js +172 -3
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli.js +124 -83
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.js +13 -0
- package/dist/config-validator.js.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.js +12 -1
- package/dist/config.js.map +1 -1
- package/dist/cross-instance-envelope.d.ts +6 -0
- package/dist/cross-instance-envelope.js +30 -0
- package/dist/cross-instance-envelope.js.map +1 -0
- package/dist/daemon.d.ts +491 -14
- package/dist/daemon.js +2121 -276
- package/dist/daemon.js.map +1 -1
- package/dist/doctor.d.ts +41 -0
- package/dist/doctor.js +267 -0
- package/dist/doctor.js.map +1 -0
- package/dist/fleet-context.d.ts +63 -0
- package/dist/fleet-context.js.map +1 -1
- package/dist/fleet-manager.d.ts +402 -11
- package/dist/fleet-manager.js +2594 -422
- package/dist/fleet-manager.js.map +1 -1
- package/dist/fleet-yaml-slim.d.ts +10 -0
- package/dist/fleet-yaml-slim.js +59 -0
- package/dist/fleet-yaml-slim.js.map +1 -0
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +1 -0
- package/dist/general-knowledge/skills/model-discovery/SKILL.md +63 -16
- package/dist/general-knowledge/skills/session-management/SKILL.md +171 -8
- package/dist/instance-lifecycle.d.ts +114 -1
- package/dist/instance-lifecycle.js +404 -14
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/instructions.js +42 -4
- package/dist/instructions.js.map +1 -1
- package/dist/locale.js +1024 -320
- package/dist/locale.js.map +1 -1
- package/dist/login-flows.d.ts +91 -0
- package/dist/login-flows.js +170 -0
- package/dist/login-flows.js.map +1 -0
- package/dist/login-manager.d.ts +63 -0
- package/dist/login-manager.js +134 -0
- package/dist/login-manager.js.map +1 -0
- package/dist/network-family.d.ts +18 -0
- package/dist/network-family.js +20 -0
- package/dist/network-family.js.map +1 -0
- package/dist/outbound-handlers.d.ts +16 -1
- package/dist/outbound-handlers.js +174 -40
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +10 -3
- package/dist/outbound-schemas.js +10 -0
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/pane-input-residue.d.ts +52 -0
- package/dist/pane-input-residue.js +107 -0
- package/dist/pane-input-residue.js.map +1 -0
- package/dist/process-memory.d.ts +6 -0
- package/dist/process-memory.js +45 -0
- package/dist/process-memory.js.map +1 -1
- package/dist/reply-dedup.d.ts +7 -8
- package/dist/reply-dedup.js +0 -0
- package/dist/reply-dedup.js.map +1 -1
- package/dist/restart-progress.d.ts +2 -0
- package/dist/restart-progress.js +3 -0
- package/dist/restart-progress.js.map +1 -1
- package/dist/scheduler/db.d.ts +12 -0
- package/dist/scheduler/db.js +59 -0
- package/dist/scheduler/db.js.map +1 -1
- package/dist/service-installer.d.ts +11 -0
- package/dist/service-installer.js +84 -18
- package/dist/service-installer.js.map +1 -1
- package/dist/settings-api.js +1 -1
- package/dist/settings-api.js.map +1 -1
- package/dist/setup-wizard.js +2 -2
- package/dist/setup-wizard.js.map +1 -1
- package/dist/spawn-gate.d.ts +30 -0
- package/dist/spawn-gate.js +79 -0
- package/dist/spawn-gate.js.map +1 -0
- package/dist/steer-capability.d.ts +1 -0
- package/dist/steer-capability.js +6 -0
- package/dist/steer-capability.js.map +1 -0
- package/dist/storm-window.d.ts +83 -0
- package/dist/storm-window.js +251 -0
- package/dist/storm-window.js.map +1 -0
- package/dist/tips.d.ts +44 -0
- package/dist/tips.js +355 -0
- package/dist/tips.js.map +1 -0
- package/dist/tmux-manager.d.ts +16 -0
- package/dist/tmux-manager.js +110 -25
- package/dist/tmux-manager.js.map +1 -1
- package/dist/topic-commands.d.ts +31 -7
- package/dist/topic-commands.js +342 -70
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-sources.d.ts +35 -10
- package/dist/transcript-sources.js +190 -38
- package/dist/transcript-sources.js.map +1 -1
- package/dist/types.d.ts +6 -1
- package/dist/ui/dashboard.html +55 -32
- package/dist/ui/settings.html +200 -60
- package/dist/ui/view.html +147 -30
- package/dist/usage/format-rich.d.ts +1 -1
- package/dist/usage/format-rich.js +28 -24
- package/dist/usage/format-rich.js.map +1 -1
- package/dist/usage/i18n-keys.d.ts +7 -0
- package/dist/usage/i18n-keys.js +34 -0
- package/dist/usage/i18n-keys.js.map +1 -0
- package/dist/usage/i18n.d.ts +5 -0
- package/dist/usage/i18n.js +27 -0
- package/dist/usage/i18n.js.map +1 -0
- package/dist/usage/providers.d.ts +21 -0
- package/dist/usage/providers.js +153 -75
- package/dist/usage/providers.js.map +1 -1
- package/dist/usage/usage-api.d.ts +11 -3
- package/dist/usage/usage-api.js +61 -24
- package/dist/usage/usage-api.js.map +1 -1
- package/package.json +2 -2
package/dist/topic-commands.js
CHANGED
|
@@ -8,9 +8,10 @@ const execAsync = promisify(exec);
|
|
|
8
8
|
import { DEFAULT_INSTANCE_CONFIG } from "./config.js";
|
|
9
9
|
import { formatCents } from "./cost-guard.js";
|
|
10
10
|
import { getTmuxSocketName, getTmuxSessionName } from "./paths.js";
|
|
11
|
-
import { t } from "./locale.js";
|
|
11
|
+
import { t, getLocale } from "./locale.js";
|
|
12
12
|
import { clampContextPercent, parseContextPercent, parseTokenContextRatio, } from "./context-percent.js";
|
|
13
|
-
import {
|
|
13
|
+
import { isGeneralInstance } from "./general-instance.js";
|
|
14
|
+
import { backendSupportsSteer } from "./steer-capability.js";
|
|
14
15
|
export { parseContextPercent, parseTokenContextRatio } from "./context-percent.js";
|
|
15
16
|
/** Sanitize a directory name into a valid instance name. Keeps Unicode letters (incl. CJK). */
|
|
16
17
|
export function sanitizeInstanceName(name) {
|
|
@@ -19,6 +20,8 @@ export function sanitizeInstanceName(name) {
|
|
|
19
20
|
}
|
|
20
21
|
/** Allowed filename for /save and /load (no path separators, no shell/inject chars). */
|
|
21
22
|
export const SAVE_FILENAME_RE = /^[\w.-]+$/;
|
|
23
|
+
/** Backends with a native side-question command that does not steer the active turn. */
|
|
24
|
+
export const BTW_SUPPORTED_BACKENDS = new Set(["claude-code"]);
|
|
22
25
|
/**
|
|
23
26
|
* Build the backend-specific session-save command, or null if the backend has no
|
|
24
27
|
* /save equivalent. kiro-cli → `/chat save <name>`; claude-code → `/export <name>.md`.
|
|
@@ -36,7 +39,8 @@ export function parseSaveFilename(text) {
|
|
|
36
39
|
return (m?.[1] ?? "").trim();
|
|
37
40
|
}
|
|
38
41
|
/** Shared message when a backend doesn't support /save. */
|
|
39
|
-
|
|
42
|
+
/** @deprecated Prefer t("save.unsupported") at the user-facing call site. */
|
|
43
|
+
export const SAVE_UNSUPPORTED_MSG = "⚠️ /save is not supported for this backend (only Kiro CLI and Claude Code).";
|
|
40
44
|
export function parsePauseWakeCommand(text) {
|
|
41
45
|
const match = text.match(/^\/(pause|wake)(?:@\S+)?(?:\s+(\S+))?$/);
|
|
42
46
|
if (!match)
|
|
@@ -103,7 +107,8 @@ export function clearCommandForBackend(backend) {
|
|
|
103
107
|
return null;
|
|
104
108
|
}
|
|
105
109
|
}
|
|
106
|
-
|
|
110
|
+
/** @deprecated Prefer t("clear.unsupported") at the user-facing call site. */
|
|
111
|
+
export const CLEAR_UNSUPPORTED_MSG = "⚠️ Clear is not supported for this backend.";
|
|
107
112
|
/**
|
|
108
113
|
* Extract context-usage % from a captured CLI pane. Scans bottom-up so the
|
|
109
114
|
* MOST RECENT prompt wins (a captured scrollback may hold several). Covers the
|
|
@@ -137,6 +142,29 @@ export function readStatuslineContextPct(dataDir, instanceName) {
|
|
|
137
142
|
return null;
|
|
138
143
|
}
|
|
139
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Claude Code's statusline payload reports the model that actually served the
|
|
147
|
+
* current session. This can differ from fleet.yaml after the CLI rejects a
|
|
148
|
+
* plan-gated model and keeps using its previous/default model.
|
|
149
|
+
*/
|
|
150
|
+
export function readStatuslineModel(dataDir, instanceName) {
|
|
151
|
+
try {
|
|
152
|
+
const statusFile = join(dataDir, "instances", instanceName, "statusline.json");
|
|
153
|
+
if (!existsSync(statusFile))
|
|
154
|
+
return null;
|
|
155
|
+
const data = JSON.parse(readFileSync(statusFile, "utf-8"));
|
|
156
|
+
const id = typeof data.model?.id === "string" ? data.model.id.trim() : "";
|
|
157
|
+
const displayName = typeof data.model?.display_name === "string"
|
|
158
|
+
? data.model.display_name.trim()
|
|
159
|
+
: "";
|
|
160
|
+
if (displayName && id && displayName !== id)
|
|
161
|
+
return `${displayName} (${id})`;
|
|
162
|
+
return id || displayName || null;
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
140
168
|
/** Live-pane scrape used by /ctx, /status Ctx, and /view sidebar. */
|
|
141
169
|
export function scrapePaneContext(instanceName, backend) {
|
|
142
170
|
try {
|
|
@@ -270,6 +298,20 @@ export class TopicCommands {
|
|
|
270
298
|
await this.handleUsageCommand(msg);
|
|
271
299
|
return true;
|
|
272
300
|
}
|
|
301
|
+
if (text === "/tips" || text.startsWith("/tips ") || text.startsWith("/tips@")) {
|
|
302
|
+
await this.handleTipsCommand(msg);
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
if (text === "/login" || text.startsWith("/login ") || text.startsWith("/login@")) {
|
|
306
|
+
await this.handleLoginCommand(msg);
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
// Telegram command names cannot contain "-", so the menu registers
|
|
310
|
+
// /install_cli; accept both spellings as typed text.
|
|
311
|
+
if (/^\/install[-_]cli(?:@\S+)?(?:\s|$)/.test(text)) {
|
|
312
|
+
await this.handleInstallCliCommand(msg);
|
|
313
|
+
return true;
|
|
314
|
+
}
|
|
273
315
|
if (text === "/update" || text.startsWith("/update@")) {
|
|
274
316
|
await this.handleUpdateCommand(msg);
|
|
275
317
|
return true;
|
|
@@ -332,6 +374,13 @@ export class TopicCommands {
|
|
|
332
374
|
const text = msg.text?.trim();
|
|
333
375
|
if (!text)
|
|
334
376
|
return false;
|
|
377
|
+
// Tips are informational and should appear where requested, including a
|
|
378
|
+
// worker topic. This also keeps Telegram text commands aligned with
|
|
379
|
+
// Discord's channel-local slash-command behavior.
|
|
380
|
+
if (text === "/tips" || text.startsWith("/tips ") || text.startsWith("/tips@")) {
|
|
381
|
+
await this.handleTipsCommand(msg);
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
335
384
|
const pauseWake = parsePauseWakeCommand(text);
|
|
336
385
|
if (pauseWake) {
|
|
337
386
|
const adapter = this.getReplyAdapter(msg);
|
|
@@ -352,7 +401,7 @@ export class TopicCommands {
|
|
|
352
401
|
return true;
|
|
353
402
|
}
|
|
354
403
|
if (pauseWake.action === "pause" && isGeneralInstance(this.ctx.fleetConfig, target)) {
|
|
355
|
-
await adapter.sendText(msg.chatId,
|
|
404
|
+
await adapter.sendText(msg.chatId, t("general.pause_forbidden"), { threadId: msg.threadId });
|
|
356
405
|
return true;
|
|
357
406
|
}
|
|
358
407
|
await adapter.sendText(msg.chatId, await this.runPauseWake(target, pauseWake.action), { threadId: msg.threadId });
|
|
@@ -379,7 +428,7 @@ export class TopicCommands {
|
|
|
379
428
|
const level = text.replace(/^\/effort(@\S+)?/, "").trim();
|
|
380
429
|
if (level) {
|
|
381
430
|
const reply = await this.ctx.applyEffort?.(instanceName, level)
|
|
382
|
-
?? "
|
|
431
|
+
?? t("effort.unsupported", this.effectiveBackend(instanceName));
|
|
383
432
|
await adapter.sendText(msg.chatId, reply, { threadId: msg.threadId });
|
|
384
433
|
}
|
|
385
434
|
else if (this.ctx.promptEffortMenu) {
|
|
@@ -410,7 +459,7 @@ export class TopicCommands {
|
|
|
410
459
|
await adapter.sendText(msg.chatId, fallback, { threadId: msg.threadId });
|
|
411
460
|
}
|
|
412
461
|
else {
|
|
413
|
-
await adapter.sendText(msg.chatId, "
|
|
462
|
+
await adapter.sendText(msg.chatId, t("model.usage"), { threadId: msg.threadId });
|
|
414
463
|
}
|
|
415
464
|
return true;
|
|
416
465
|
}
|
|
@@ -439,6 +488,22 @@ export class TopicCommands {
|
|
|
439
488
|
await adapter.sendText(msg.chatId, result, { threadId: msg.threadId });
|
|
440
489
|
return true;
|
|
441
490
|
}
|
|
491
|
+
if (text === "/btw" || text.startsWith("/btw ") || text.startsWith("/btw@")) {
|
|
492
|
+
const adapter = this.getReplyAdapter(msg);
|
|
493
|
+
if (!adapter)
|
|
494
|
+
return false;
|
|
495
|
+
// Like /steer, /btw is not admin-gated: anyone who can send the agent a
|
|
496
|
+
// normal message may ask a side question. Claude owns the isolation from
|
|
497
|
+
// the active turn; AgEnD only submits its native command immediately.
|
|
498
|
+
const content = text.replace(/^\/btw(@\S+)?/, "").trim();
|
|
499
|
+
if (!content) {
|
|
500
|
+
await adapter.sendText(msg.chatId, t("btw.usage"), { threadId: msg.threadId });
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
const result = this.sendBtw(instanceName, content, msg);
|
|
504
|
+
await adapter.sendText(msg.chatId, result, { threadId: msg.threadId });
|
|
505
|
+
return true;
|
|
506
|
+
}
|
|
442
507
|
if (text === "/clear" || text.startsWith("/clear@")) {
|
|
443
508
|
const adapter = this.getReplyAdapter(msg);
|
|
444
509
|
if (!adapter)
|
|
@@ -492,7 +557,7 @@ export class TopicCommands {
|
|
|
492
557
|
}
|
|
493
558
|
async runPauseWake(instanceName, action) {
|
|
494
559
|
if (action === "pause" && isGeneralInstance(this.ctx.fleetConfig, instanceName)) {
|
|
495
|
-
return
|
|
560
|
+
return t("general.pause_forbidden");
|
|
496
561
|
}
|
|
497
562
|
try {
|
|
498
563
|
const result = await this.ctx.changeInstancePauseState(instanceName, action);
|
|
@@ -523,7 +588,10 @@ export class TopicCommands {
|
|
|
523
588
|
const contextLine = context == null ? null : formatContextUsageLine(context, tokenRatio);
|
|
524
589
|
// Effective model (resolves per-instance → fleet default → classic channel →
|
|
525
590
|
// the CLI's own default) via the shared resolver, so /ctx and /model agree.
|
|
526
|
-
const modelDisplay =
|
|
591
|
+
const modelDisplay = backend === "claude-code"
|
|
592
|
+
? readStatuslineModel(this.ctx.dataDir, instanceName)
|
|
593
|
+
?? this.ctx.modelDisplayForInstance?.(instanceName)
|
|
594
|
+
: this.ctx.modelDisplayForInstance?.(instanceName);
|
|
527
595
|
const modelLine = modelDisplay ? `\n${t("ctx.model", modelDisplay)}` : "";
|
|
528
596
|
return context != null
|
|
529
597
|
? `${contextLine}\n${t("ctx.backend", backend)}${modelLine}\n${t("ctx.instance", instanceName)}`
|
|
@@ -541,9 +609,9 @@ export class TopicCommands {
|
|
|
541
609
|
?? this.ctx.fleetConfig?.defaults?.backend ?? "claude-code";
|
|
542
610
|
const cmd = compactCommandForBackend(backend);
|
|
543
611
|
ipc.send({ type: "raw_paste", content: cmd });
|
|
544
|
-
return
|
|
612
|
+
return t("compact.sent", cmd);
|
|
545
613
|
}
|
|
546
|
-
return "
|
|
614
|
+
return t("compact.not_connected");
|
|
547
615
|
}
|
|
548
616
|
/** Whether the instance backend exposes a verified full-reset command. */
|
|
549
617
|
supportsClear(instanceName) {
|
|
@@ -562,10 +630,9 @@ export class TopicCommands {
|
|
|
562
630
|
* the user gets an honest "not supported" instead of a silent queue
|
|
563
631
|
* fallback that looks like a steer but behaves like a normal message.
|
|
564
632
|
*/
|
|
565
|
-
static STEER_SUPPORTED_BACKENDS = new Set(["claude-code", "codex", "grok", "mock"]);
|
|
566
633
|
sendSteer(instanceName, content, msg) {
|
|
567
634
|
const backend = this.effectiveBackend(instanceName);
|
|
568
|
-
if (!
|
|
635
|
+
if (!backendSupportsSteer(backend)) {
|
|
569
636
|
return t("steer.unsupported", backend);
|
|
570
637
|
}
|
|
571
638
|
const ipc = this.ctx.instanceIpcClients.get(instanceName);
|
|
@@ -574,6 +641,7 @@ export class TopicCommands {
|
|
|
574
641
|
ipc.send({
|
|
575
642
|
type: "steer",
|
|
576
643
|
content,
|
|
644
|
+
delivery_epoch: this.ctx.getDeliveryEpoch?.(instanceName) ?? undefined,
|
|
577
645
|
meta: {
|
|
578
646
|
chat_id: msg.chatId,
|
|
579
647
|
message_id: msg.messageId ?? "",
|
|
@@ -586,15 +654,43 @@ export class TopicCommands {
|
|
|
586
654
|
});
|
|
587
655
|
return t("steer.sent", instanceName);
|
|
588
656
|
}
|
|
657
|
+
/** Ask a Claude Code side question without folding it into the active turn. */
|
|
658
|
+
sendBtw(instanceName, content, msg) {
|
|
659
|
+
const backend = this.effectiveBackend(instanceName);
|
|
660
|
+
if (!BTW_SUPPORTED_BACKENDS.has(backend))
|
|
661
|
+
return t("btw.unsupported", backend);
|
|
662
|
+
const ipc = this.ctx.instanceIpcClients.get(instanceName);
|
|
663
|
+
if (!ipc?.connected)
|
|
664
|
+
return t("btw.not_connected");
|
|
665
|
+
ipc.send({
|
|
666
|
+
type: "btw",
|
|
667
|
+
content,
|
|
668
|
+
delivery_epoch: this.ctx.getDeliveryEpoch?.(instanceName) ?? undefined,
|
|
669
|
+
meta: {
|
|
670
|
+
chat_id: msg.chatId,
|
|
671
|
+
message_id: msg.messageId ?? "",
|
|
672
|
+
user: msg.username,
|
|
673
|
+
user_id: msg.userId,
|
|
674
|
+
thread_id: msg.threadId ?? "",
|
|
675
|
+
adapter_id: msg.adapterId ?? "",
|
|
676
|
+
source: msg.source,
|
|
677
|
+
},
|
|
678
|
+
});
|
|
679
|
+
return t("btw.sent", instanceName);
|
|
680
|
+
}
|
|
589
681
|
/** Send the backend-appropriate full conversation reset through raw_paste. */
|
|
590
682
|
async sendClear(instanceName) {
|
|
591
683
|
const backend = this.effectiveBackend(instanceName);
|
|
592
684
|
const cmd = clearCommandForBackend(backend);
|
|
593
685
|
if (!cmd)
|
|
594
|
-
return
|
|
686
|
+
return t("clear.unsupported");
|
|
595
687
|
const ipc = this.ctx.instanceIpcClients.get(instanceName);
|
|
596
688
|
if (ipc?.connected) {
|
|
597
|
-
ipc.send({
|
|
689
|
+
ipc.send({
|
|
690
|
+
type: "raw_paste",
|
|
691
|
+
content: cmd,
|
|
692
|
+
...(backend === "kiro-cli" ? { confirm_clear: true } : {}),
|
|
693
|
+
});
|
|
598
694
|
return t("clear.sent", cmd);
|
|
599
695
|
}
|
|
600
696
|
return t("clear.not_connected");
|
|
@@ -609,13 +705,13 @@ export class TopicCommands {
|
|
|
609
705
|
?? this.ctx.fleetConfig?.defaults?.backend ?? "claude-code";
|
|
610
706
|
const cmd = saveCommandForBackend(backend, filename);
|
|
611
707
|
if (!cmd)
|
|
612
|
-
return
|
|
708
|
+
return t("save.unsupported");
|
|
613
709
|
const ipc = this.ctx.instanceIpcClients.get(instanceName);
|
|
614
710
|
if (ipc?.connected) {
|
|
615
711
|
ipc.send({ type: "raw_paste", content: cmd });
|
|
616
|
-
return
|
|
712
|
+
return t("save.sent", cmd, instanceName);
|
|
617
713
|
}
|
|
618
|
-
return "
|
|
714
|
+
return t("save.not_connected");
|
|
619
715
|
}
|
|
620
716
|
async handleRestartCommand(msg) {
|
|
621
717
|
const adapter = this.getReplyAdapter(msg);
|
|
@@ -632,8 +728,8 @@ export class TopicCommands {
|
|
|
632
728
|
process.kill(process.pid, "SIGUSR2");
|
|
633
729
|
}
|
|
634
730
|
/**
|
|
635
|
-
* `/usage` — AI subscription usage for
|
|
636
|
-
*
|
|
731
|
+
* `/usage` — AI subscription usage for CLI backends used by running or paused
|
|
732
|
+
* instances, one compact message. Same permission level as /ctx (none): whoever
|
|
637
733
|
* can talk to the fleet may see how much headroom it has left. Vendor rate
|
|
638
734
|
* limits are protected by the shared 5-minute cache, not by gating callers.
|
|
639
735
|
*/
|
|
@@ -644,7 +740,7 @@ export class TopicCommands {
|
|
|
644
740
|
try {
|
|
645
741
|
const { getUsageSnapshot } = await import("./usage/usage-api.js");
|
|
646
742
|
const { renderUsageHtml, renderUsageMarkdown } = await import("./usage/format-rich.js");
|
|
647
|
-
const payload = await getUsageSnapshot();
|
|
743
|
+
const payload = await getUsageSnapshot(false, this.ctx.getActiveUsageProviderIds?.());
|
|
648
744
|
// Each platform gets its native rich format: Telegram needs parse_mode
|
|
649
745
|
// HTML; Discord renders Markdown in plain content. Anything else falls back
|
|
650
746
|
// to Markdown, which degrades to readable text.
|
|
@@ -656,7 +752,140 @@ export class TopicCommands {
|
|
|
656
752
|
}
|
|
657
753
|
}
|
|
658
754
|
catch (err) {
|
|
659
|
-
await adapter.sendText(msg.chatId,
|
|
755
|
+
await adapter.sendText(msg.chatId, t("usage.failed", err.message), { threadId: msg.threadId });
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* `/login [backend] | code <text> | cancel` — remote CLI re-login.
|
|
760
|
+
* Fleet-admin only: the flow emits live authorization URLs/codes, and anyone
|
|
761
|
+
* completing one binds their own account to this fleet's CLI.
|
|
762
|
+
*/
|
|
763
|
+
async handleLoginCommand(msg) {
|
|
764
|
+
const adapter = this.getReplyAdapter(msg);
|
|
765
|
+
if (!adapter)
|
|
766
|
+
return;
|
|
767
|
+
if (!this.ctx.isFleetAdmin(msg.userId, msg.adapterId)) {
|
|
768
|
+
await adapter.sendText(msg.chatId, t("permission.denied"), { threadId: msg.threadId });
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
if (!this.ctx.startLoginSession || !this.ctx.promptLoginBackends
|
|
772
|
+
|| !this.ctx.loginSubmitInput || !this.ctx.cancelLoginSession) {
|
|
773
|
+
await adapter.sendText(msg.chatId, t("login.no_session"), { threadId: msg.threadId });
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
const arg = msg.text.trim().replace(/^\/login(?:@\S+)?/, "").trim();
|
|
777
|
+
const chat = {
|
|
778
|
+
adapter,
|
|
779
|
+
adapterId: msg.adapterId ?? adapter.id,
|
|
780
|
+
chatId: msg.chatId,
|
|
781
|
+
threadId: msg.threadId,
|
|
782
|
+
};
|
|
783
|
+
if (!arg) {
|
|
784
|
+
await this.ctx.promptLoginBackends(chat);
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
if (arg === "cancel") {
|
|
788
|
+
await adapter.sendText(msg.chatId, await this.ctx.cancelLoginSession(), { threadId: msg.threadId });
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
if (arg.startsWith("code ")) {
|
|
792
|
+
const input = arg.slice(5).trim();
|
|
793
|
+
const reply = input ? await this.ctx.loginSubmitInput(input) : t("login.usage");
|
|
794
|
+
await adapter.sendText(msg.chatId, reply, { threadId: msg.threadId });
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
if (/\s/.test(arg)) {
|
|
798
|
+
await adapter.sendText(msg.chatId, t("login.usage"), { threadId: msg.threadId });
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
const started = await this.ctx.startLoginSession(arg, chat);
|
|
802
|
+
// null → the still-valid-auth confirmation prompt was posted instead.
|
|
803
|
+
if (started)
|
|
804
|
+
await adapter.sendText(msg.chatId, started, { threadId: msg.threadId });
|
|
805
|
+
}
|
|
806
|
+
/** `/install-cli <backend> | cancel` — remote CLI install. Fleet-admin only. */
|
|
807
|
+
async handleInstallCliCommand(msg) {
|
|
808
|
+
const adapter = this.getReplyAdapter(msg);
|
|
809
|
+
if (!adapter)
|
|
810
|
+
return;
|
|
811
|
+
if (!this.ctx.isFleetAdmin(msg.userId, msg.adapterId)) {
|
|
812
|
+
await adapter.sendText(msg.chatId, t("permission.denied"), { threadId: msg.threadId });
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
if (!this.ctx.startInstallSession || !this.ctx.cancelInstallSession) {
|
|
816
|
+
await adapter.sendText(msg.chatId, t("install.no_session"), { threadId: msg.threadId });
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
const arg = msg.text.trim().replace(/^\/install[-_]cli(?:@\S+)?/, "").trim();
|
|
820
|
+
// Bare call → offer the backends, matching bare `/login`. A multi-word arg
|
|
821
|
+
// is still a usage error: it means they typed something, just not a backend.
|
|
822
|
+
if (!arg && this.ctx.promptInstallBackends) {
|
|
823
|
+
await this.ctx.promptInstallBackends({
|
|
824
|
+
adapter,
|
|
825
|
+
adapterId: msg.adapterId ?? adapter.id,
|
|
826
|
+
chatId: msg.chatId,
|
|
827
|
+
threadId: msg.threadId,
|
|
828
|
+
});
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
if (!arg || /\s/.test(arg)) {
|
|
832
|
+
await adapter.sendText(msg.chatId, t("install.usage"), { threadId: msg.threadId });
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
if (arg === "cancel") {
|
|
836
|
+
await adapter.sendText(msg.chatId, await this.ctx.cancelInstallSession(), { threadId: msg.threadId });
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
const chat = {
|
|
840
|
+
adapter,
|
|
841
|
+
adapterId: msg.adapterId ?? adapter.id,
|
|
842
|
+
chatId: msg.chatId,
|
|
843
|
+
threadId: msg.threadId,
|
|
844
|
+
};
|
|
845
|
+
await adapter.sendText(msg.chatId, await this.ctx.startInstallSession(arg, chat), { threadId: msg.threadId });
|
|
846
|
+
}
|
|
847
|
+
async handleTipsCommand(msg) {
|
|
848
|
+
const adapter = this.getReplyAdapter(msg);
|
|
849
|
+
if (!adapter || !this.ctx.fleetConfig)
|
|
850
|
+
return;
|
|
851
|
+
const mode = msg.text.trim().replace(/^\/tips(?:@\S+)?/, "").trim().toLowerCase();
|
|
852
|
+
if (mode === "advanced on") {
|
|
853
|
+
if (!this.ctx.isFleetAdmin(msg.userId, msg.adapterId)) {
|
|
854
|
+
await adapter.sendText(msg.chatId, t("permission.denied"), { threadId: msg.threadId });
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
await adapter.sendText(msg.chatId, this.ctx.unlockAdvancedTips?.(msg.userId)
|
|
858
|
+
? t("tips.advanced.unlocked")
|
|
859
|
+
: t("tips.unavailable"), { threadId: msg.threadId });
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
if (mode === "on" || mode === "off") {
|
|
863
|
+
if (!this.ctx.isFleetAdmin(msg.userId, msg.adapterId)) {
|
|
864
|
+
await adapter.sendText(msg.chatId, t("permission.denied"), { threadId: msg.threadId });
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
this.ctx.fleetConfig.defaults.tips = mode === "on";
|
|
868
|
+
this.ctx.saveFleetConfig();
|
|
869
|
+
await adapter.sendText(msg.chatId, t(mode === "on" ? "tips.enabled" : "tips.disabled"), {
|
|
870
|
+
threadId: msg.threadId,
|
|
871
|
+
});
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
if (mode) {
|
|
875
|
+
await adapter.sendText(msg.chatId, t("tips.usage"), { threadId: msg.threadId });
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
const result = await this.ctx.promptTip?.(
|
|
879
|
+
// The instance name scopes prompt cleanup; the Tip is posted at the
|
|
880
|
+
// caller's current chat/thread below.
|
|
881
|
+
Object.entries(this.ctx.fleetConfig.instances)
|
|
882
|
+
.find(([, config]) => config.general_topic === true
|
|
883
|
+
&& (!config.channel_id || config.channel_id === msg.adapterId))?.[0] ?? "general", adapter, msg.chatId, msg.threadId) ?? "unavailable";
|
|
884
|
+
if (result === "empty") {
|
|
885
|
+
await adapter.sendText(msg.chatId, t("tips.empty"), { threadId: msg.threadId });
|
|
886
|
+
}
|
|
887
|
+
else if (result === "unavailable") {
|
|
888
|
+
await adapter.sendText(msg.chatId, t("tips.unavailable"), { threadId: msg.threadId });
|
|
660
889
|
}
|
|
661
890
|
}
|
|
662
891
|
async handleStatusCommand(msg) {
|
|
@@ -685,7 +914,7 @@ export class TopicCommands {
|
|
|
685
914
|
/** Get fleet status as markdown text (shared by TG + DC) */
|
|
686
915
|
async getStatusText() {
|
|
687
916
|
if (!this.ctx.fleetConfig)
|
|
688
|
-
return "
|
|
917
|
+
return t("status.no_config");
|
|
689
918
|
const rows = [];
|
|
690
919
|
let pausedCount = 0;
|
|
691
920
|
const fleetNames = new Set(Object.keys(this.ctx.fleetConfig.instances));
|
|
@@ -722,10 +951,10 @@ export class TopicCommands {
|
|
|
722
951
|
icon = "🔴";
|
|
723
952
|
else
|
|
724
953
|
icon = "⚪";
|
|
725
|
-
const stateLabel = executionState === "idle" ? "
|
|
726
|
-
: executionState === "working" ? "
|
|
727
|
-
: executionState === "stuck" ? "
|
|
728
|
-
: executionState === "paused" ? "
|
|
954
|
+
const stateLabel = executionState === "idle" ? `🟢 ${t("state.idle")}`
|
|
955
|
+
: executionState === "working" ? `🔵 ${t("state.working")}`
|
|
956
|
+
: executionState === "stuck" ? `🔴 ${t("state.stuck")}`
|
|
957
|
+
: executionState === "paused" ? `⏸ ${t("state.paused")}`
|
|
729
958
|
: "—";
|
|
730
959
|
const displayName = this.shortInstanceName(name);
|
|
731
960
|
// IPC reachability moved here from /sysinfo: it is per-instance health, and
|
|
@@ -737,27 +966,27 @@ export class TopicCommands {
|
|
|
737
966
|
rows.push(`| ${displayName} | ${backend} | ${contextStr} | ${effort} | ${formatCents(costCents)} | ${icon} | ${stateLabel} | ${ipc} |`);
|
|
738
967
|
}
|
|
739
968
|
if (rows.length === 0)
|
|
740
|
-
return "
|
|
969
|
+
return t("status.no_instances");
|
|
741
970
|
const lines = [
|
|
742
|
-
|
|
971
|
+
`## ${t("status.title")}`,
|
|
743
972
|
"",
|
|
744
|
-
"
|
|
973
|
+
t("status.table_header"),
|
|
745
974
|
"|----------|---------|-----|--------|------|--------|-------|-----|",
|
|
746
975
|
...rows,
|
|
747
976
|
"",
|
|
748
|
-
|
|
977
|
+
t("status.paused_count", pausedCount),
|
|
749
978
|
];
|
|
750
979
|
const limitCents = this.ctx.costGuard?.getLimitCents() ?? 0;
|
|
751
980
|
const totalCents = this.ctx.costGuard?.getFleetTotalCents() ?? 0;
|
|
752
981
|
if (limitCents > 0) {
|
|
753
|
-
lines.push("",
|
|
982
|
+
lines.push("", t("status.daily_cost", formatCents(totalCents), formatCents(limitCents)));
|
|
754
983
|
}
|
|
755
984
|
// Adapter states (only show if any are not connected)
|
|
756
985
|
const adapterStates = this.ctx.getAdapterStates?.();
|
|
757
986
|
if (adapterStates && adapterStates.size > 0) {
|
|
758
987
|
const issues = [...adapterStates.entries()].filter(([, s]) => s.status !== "connected");
|
|
759
988
|
if (issues.length > 0) {
|
|
760
|
-
lines.push("", "
|
|
989
|
+
lines.push("", `**${t("status.adapters")}**`);
|
|
761
990
|
for (const [id, s] of adapterStates) {
|
|
762
991
|
const icon = s.status === "connected" ? "✅" : s.status === "retrying" ? "🔄" : "❌";
|
|
763
992
|
lines.push(` ${icon} ${id}: ${s.status}${s.lastError ? ` (${s.lastError.slice(0, 60)})` : ""}`);
|
|
@@ -770,35 +999,73 @@ export class TopicCommands {
|
|
|
770
999
|
const adapter = this.getReplyAdapter(msg);
|
|
771
1000
|
if (!adapter)
|
|
772
1001
|
return;
|
|
773
|
-
const
|
|
1002
|
+
const platform = adapter.type === "discord" ? "discord" : "telegram";
|
|
1003
|
+
const text = this.getSysInfoText({ platform });
|
|
774
1004
|
await adapter.sendText(msg.chatId, text, { threadId: msg.threadId });
|
|
775
1005
|
}
|
|
776
1006
|
/**
|
|
777
|
-
* Get system info as
|
|
1007
|
+
* Get system info as formatted text.
|
|
778
1008
|
*
|
|
779
1009
|
* System-level only. The per-instance table (state/IPC/cost) moved to /status,
|
|
780
1010
|
* which was already the fleet-per-instance view — the two tables overlapped on
|
|
781
1011
|
* everything but the IPC column, and each command answered half of "is the
|
|
782
1012
|
* machine fine and are the instances fine".
|
|
1013
|
+
*
|
|
1014
|
+
* @param opts.platform - "telegram" uses markdown table, "discord" uses plain lines
|
|
783
1015
|
*/
|
|
784
|
-
getSysInfoText() {
|
|
1016
|
+
getSysInfoText(opts) {
|
|
785
1017
|
const info = this.ctx.getSysInfo();
|
|
786
1018
|
const upHours = Math.floor(info.uptime_seconds / 3600);
|
|
787
1019
|
const upMins = Math.floor((info.uptime_seconds % 3600) / 60);
|
|
788
1020
|
const require = createRequire(import.meta.url);
|
|
789
1021
|
const agendVersion = require("../package.json").version ?? "unknown";
|
|
1022
|
+
const tipsLang = getLocale() === "zh-TW" ? "zh" : "en";
|
|
1023
|
+
// Fleet summary lines (multi-line for mobile readability)
|
|
1024
|
+
const summaryLines = [
|
|
1025
|
+
`${t("sysinfo.instances")}: ${info.running_count} ${t("sysinfo.running")}, ${info.paused_count} ${t("sysinfo.paused")}`,
|
|
1026
|
+
...(info.fleet_mem_mb !== null ? [`${t("sysinfo.fleet_mem")}: ${(info.fleet_mem_mb / 1024).toFixed(1)} GB`] : []),
|
|
1027
|
+
`${t("sysinfo.system_mem")}: ${info.system_mem_gb.used} / ${info.system_mem_gb.total} GB`,
|
|
1028
|
+
];
|
|
1029
|
+
const resources = [
|
|
1030
|
+
`📚 **${t("sysinfo.resources")}**`,
|
|
1031
|
+
`- ${t("sysinfo.docs")}: https://songsid.github.io/AgEnD`,
|
|
1032
|
+
`- ${t("sysinfo.tips")}: https://songsid.github.io/AgEnD/tips-${tipsLang}.html`,
|
|
1033
|
+
`- ${t("sysinfo.github")}: https://github.com/songsid/AgEnD`,
|
|
1034
|
+
];
|
|
1035
|
+
// Discord: plain key-value lines (Discord doesn't render markdown tables)
|
|
1036
|
+
if (opts?.platform === "discord") {
|
|
1037
|
+
return [
|
|
1038
|
+
`## ${t("sysinfo.title")}`,
|
|
1039
|
+
`AgEnD: v${agendVersion}`,
|
|
1040
|
+
`OS: ${process.platform} ${osRelease()} (${process.arch})`,
|
|
1041
|
+
`Node: ${process.version}`,
|
|
1042
|
+
`tmux: ${tmuxVersion()}`,
|
|
1043
|
+
`${t("sysinfo.uptime")}: ${upHours}h ${upMins}m`,
|
|
1044
|
+
`${t("sysinfo.memory")}: ${info.memory_mb.rss} MB RSS`,
|
|
1045
|
+
`${t("sysinfo.heap")}: ${info.memory_mb.heapUsed} / ${info.memory_mb.heapTotal} MB`,
|
|
1046
|
+
"",
|
|
1047
|
+
...summaryLines,
|
|
1048
|
+
"",
|
|
1049
|
+
...resources,
|
|
1050
|
+
].join("\n");
|
|
1051
|
+
}
|
|
1052
|
+
// Telegram (default): markdown table
|
|
790
1053
|
return [
|
|
791
|
-
|
|
1054
|
+
`## ${t("sysinfo.title")}`,
|
|
792
1055
|
"",
|
|
793
|
-
"
|
|
1056
|
+
`| ${t("sysinfo.metric")} | ${t("sysinfo.value")} |`,
|
|
794
1057
|
"|--------|-------|",
|
|
795
1058
|
`| AgEnD | v${agendVersion} |`,
|
|
796
1059
|
`| OS | ${process.platform} ${osRelease()} (${process.arch}) |`,
|
|
797
1060
|
`| Node | ${process.version} |`,
|
|
798
1061
|
`| tmux | ${tmuxVersion()} |`,
|
|
799
|
-
`|
|
|
800
|
-
`|
|
|
801
|
-
`|
|
|
1062
|
+
`| ${t("sysinfo.uptime")} | ${upHours}h ${upMins}m |`,
|
|
1063
|
+
`| ${t("sysinfo.memory")} | ${info.memory_mb.rss} MB RSS |`,
|
|
1064
|
+
`| ${t("sysinfo.heap")} | ${info.memory_mb.heapUsed} / ${info.memory_mb.heapTotal} MB |`,
|
|
1065
|
+
"",
|
|
1066
|
+
...summaryLines,
|
|
1067
|
+
"",
|
|
1068
|
+
...resources,
|
|
802
1069
|
].join("\n");
|
|
803
1070
|
}
|
|
804
1071
|
async handleUpdateCommand(msg) {
|
|
@@ -851,11 +1118,11 @@ export class TopicCommands {
|
|
|
851
1118
|
timeout: 30_000,
|
|
852
1119
|
encoding: "utf-8",
|
|
853
1120
|
});
|
|
854
|
-
output = stripAnsi(stdout) || "
|
|
1121
|
+
output = stripAnsi(stdout) || t("doctor.no_output");
|
|
855
1122
|
}
|
|
856
1123
|
catch (err) {
|
|
857
1124
|
const e = err;
|
|
858
|
-
output = stripAnsi(e.stdout ?? e.message ?? "
|
|
1125
|
+
output = stripAnsi(e.stdout ?? e.message ?? t("doctor.failed"));
|
|
859
1126
|
}
|
|
860
1127
|
await adapter.sendText(chatId, output, { threadId });
|
|
861
1128
|
}
|
|
@@ -864,7 +1131,7 @@ export class TopicCommands {
|
|
|
864
1131
|
const adapter = this.getReplyAdapter(msg);
|
|
865
1132
|
if (!adapter)
|
|
866
1133
|
return;
|
|
867
|
-
await adapter.sendText(msg.chatId, "
|
|
1134
|
+
await adapter.sendText(msg.chatId, t("topic.unbound"), { threadId: msg.threadId });
|
|
868
1135
|
}
|
|
869
1136
|
/** Handle topic deletion — stop daemon and remove from config */
|
|
870
1137
|
async handleTopicDeleted(threadId) {
|
|
@@ -977,22 +1244,26 @@ export class TopicCommands {
|
|
|
977
1244
|
}
|
|
978
1245
|
try {
|
|
979
1246
|
const fleetCommands = [
|
|
980
|
-
{ command: "status", description: "🔒
|
|
981
|
-
{ command: "sysinfo", description: "
|
|
982
|
-
{ command: "dashboard", description: "🔒
|
|
983
|
-
{ command: "ctx", description: "
|
|
984
|
-
{ command: "compact", description: "
|
|
985
|
-
{ command: "steer", description: "
|
|
986
|
-
{ command: "
|
|
987
|
-
{ command: "
|
|
988
|
-
{ command: "
|
|
989
|
-
{ command: "
|
|
990
|
-
{ command: "
|
|
991
|
-
{ command: "
|
|
992
|
-
{ command: "
|
|
993
|
-
{ command: "
|
|
994
|
-
{ command: "
|
|
995
|
-
{ command: "
|
|
1247
|
+
{ command: "status", description: "🔒 " + t("slash.status") },
|
|
1248
|
+
{ command: "sysinfo", description: t("slash.sysinfo") },
|
|
1249
|
+
{ command: "dashboard", description: "🔒 " + t("slash.dashboard") },
|
|
1250
|
+
{ command: "ctx", description: t("slash.ctx") },
|
|
1251
|
+
{ command: "compact", description: t("slash.compact") },
|
|
1252
|
+
{ command: "steer", description: t("slash.steer") },
|
|
1253
|
+
{ command: "btw", description: t("slash.btw") },
|
|
1254
|
+
{ command: "clear", description: "🔒 " + t("slash.clear") },
|
|
1255
|
+
{ command: "model", description: "🔒 " + t("slash.model") },
|
|
1256
|
+
{ command: "effort", description: "🔒 " + t("slash.effort") },
|
|
1257
|
+
{ command: "pause", description: "🔒 " + t("slash.pause") },
|
|
1258
|
+
{ command: "wake", description: "🔒 " + t("slash.wake") },
|
|
1259
|
+
{ command: "restart", description: "🔒 " + t("slash.restart") },
|
|
1260
|
+
{ command: "collab", description: "🔒 " + t("slash.collab") },
|
|
1261
|
+
{ command: "update", description: "🔒 " + t("slash.update") },
|
|
1262
|
+
{ command: "doctor", description: "🔒 " + t("slash.doctor") },
|
|
1263
|
+
{ command: "login", description: "🔒 " + t("slash.login") },
|
|
1264
|
+
{ command: "install_cli", description: "🔒 " + t("slash.install_cli") },
|
|
1265
|
+
{ command: "usage", description: t("slash.usage") },
|
|
1266
|
+
{ command: "tips", description: t("slash.tips") },
|
|
996
1267
|
];
|
|
997
1268
|
const setCommands = async (commands, scope) => {
|
|
998
1269
|
const response = await fetch(`https://api.telegram.org/bot${botToken}/setMyCommands`, {
|
|
@@ -1013,16 +1284,17 @@ export class TopicCommands {
|
|
|
1013
1284
|
}
|
|
1014
1285
|
};
|
|
1015
1286
|
const classicCommands = [
|
|
1016
|
-
{ command: "start", description: "🔒
|
|
1017
|
-
{ command: "stop", description: "🔒
|
|
1018
|
-
{ command: "compact", description: "🔒
|
|
1019
|
-
{ command: "steer", description: "
|
|
1020
|
-
{ command: "
|
|
1021
|
-
{ command: "
|
|
1022
|
-
{ command: "
|
|
1023
|
-
{ command: "
|
|
1024
|
-
{ command: "
|
|
1025
|
-
{ command: "
|
|
1287
|
+
{ command: "start", description: "🔒 " + t("slash.start") },
|
|
1288
|
+
{ command: "stop", description: "🔒 " + t("slash.stop") },
|
|
1289
|
+
{ command: "compact", description: "🔒 " + t("slash.compact") },
|
|
1290
|
+
{ command: "steer", description: t("slash.steer") },
|
|
1291
|
+
{ command: "btw", description: t("slash.btw") },
|
|
1292
|
+
{ command: "clear", description: "🔒 " + t("slash.clear") },
|
|
1293
|
+
{ command: "model", description: "🔒 " + t("slash.model") },
|
|
1294
|
+
{ command: "effort", description: "🔒 " + t("slash.effort") },
|
|
1295
|
+
{ command: "pause", description: "🔒 " + t("slash.pause") },
|
|
1296
|
+
{ command: "wake", description: "🔒 " + t("slash.wake") },
|
|
1297
|
+
{ command: "ctx", description: t("slash.ctx") },
|
|
1026
1298
|
];
|
|
1027
1299
|
// A chat_administrators scope has higher precedence than the chat scope.
|
|
1028
1300
|
// Keep both synchronized so a stale admin-only list from BotFather or an
|