@openacp/cli 0.4.11 → 0.5.1
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 +41 -3
- package/dist/agent-catalog-LAAVBVLY.js +10 -0
- package/dist/agent-dependencies-FCLRGMZM.js +23 -0
- package/dist/agent-registry-KZANAFXQ.js +8 -0
- package/dist/agent-store-ZBXGOFPH.js +8 -0
- package/dist/chunk-5HGXUCMX.js +83 -0
- package/dist/chunk-5HGXUCMX.js.map +1 -0
- package/dist/chunk-5MH66WUY.js +424 -0
- package/dist/chunk-5MH66WUY.js.map +1 -0
- package/dist/{chunk-FKOARMAE.js → chunk-776VAU3T.js} +3 -3
- package/dist/chunk-GUHCS6X7.js +282 -0
- package/dist/chunk-GUHCS6X7.js.map +1 -0
- package/dist/{chunk-3DIPXFZJ.js → chunk-IRGYTNLP.js} +2 -2
- package/dist/chunk-IURZ4QHG.js +91 -0
- package/dist/chunk-IURZ4QHG.js.map +1 -0
- package/dist/{chunk-WYZFGHHI.js → chunk-JRF4G4X7.js} +60 -24
- package/dist/chunk-JRF4G4X7.js.map +1 -0
- package/dist/chunk-NAMYZIS5.js +1 -0
- package/dist/{chunk-ZW444AQY.js → chunk-NDR5JCS7.js} +2 -2
- package/dist/{chunk-66RVSUAR.js → chunk-PHC67OP4.js} +567 -103
- package/dist/chunk-PHC67OP4.js.map +1 -0
- package/dist/{chunk-W7QQA6CW.js → chunk-QODDJ4PH.js} +83 -36
- package/dist/chunk-QODDJ4PH.js.map +1 -0
- package/dist/{chunk-YRJEZD7R.js → chunk-VBEWSWVL.js} +2 -2
- package/dist/{chunk-C33LTDZV.js → chunk-Z46LGZ7R.js} +21 -8
- package/dist/chunk-Z46LGZ7R.js.map +1 -0
- package/dist/cli.js +440 -64
- package/dist/cli.js.map +1 -1
- package/dist/{config-XURP6B3S.js → config-PCPIBPUA.js} +2 -2
- package/dist/config-editor-RGV6VKPZ.js +12 -0
- package/dist/{config-registry-OGX4YM2U.js → config-registry-SNKA2EH2.js} +2 -2
- package/dist/{daemon-GWJM2S4A.js → daemon-JZLFRUW6.js} +3 -3
- package/dist/daemon-JZLFRUW6.js.map +1 -0
- package/dist/data/registry-snapshot.json +876 -0
- package/dist/doctor-N2HKKUUQ.js +9 -0
- package/dist/doctor-N2HKKUUQ.js.map +1 -0
- package/dist/index.d.ts +138 -17
- package/dist/index.js +24 -15
- package/dist/integrate-X7LI6MUO.js +257 -0
- package/dist/integrate-X7LI6MUO.js.map +1 -0
- package/dist/{main-2QKD2EI2.js → main-DSQBCJHR.js} +18 -15
- package/dist/{main-2QKD2EI2.js.map → main-DSQBCJHR.js.map} +1 -1
- package/dist/{menu-CARRTW2F.js → menu-J5YVH665.js} +2 -4
- package/dist/menu-J5YVH665.js.map +1 -0
- package/dist/{setup-TTOL7XAN.js → setup-3A3XDGCM.js} +4 -3
- package/dist/setup-3A3XDGCM.js.map +1 -0
- package/dist/suggest-RST5VOHB.js +36 -0
- package/dist/suggest-RST5VOHB.js.map +1 -0
- package/package.json +11 -2
- package/dist/agent-registry-7HC6D4CH.js +0 -7
- package/dist/chunk-66RVSUAR.js.map +0 -1
- package/dist/chunk-BGKQHQB4.js +0 -276
- package/dist/chunk-BGKQHQB4.js.map +0 -1
- package/dist/chunk-C33LTDZV.js.map +0 -1
- package/dist/chunk-VA2M52CM.js +0 -15
- package/dist/chunk-VA2M52CM.js.map +0 -1
- package/dist/chunk-W7QQA6CW.js.map +0 -1
- package/dist/chunk-WYZFGHHI.js.map +0 -1
- package/dist/config-editor-AALY3URF.js +0 -11
- package/dist/doctor-X477CVZN.js +0 -9
- package/dist/integrate-WUPLRJD3.js +0 -145
- package/dist/integrate-WUPLRJD3.js.map +0 -1
- /package/dist/{agent-registry-7HC6D4CH.js.map → agent-catalog-LAAVBVLY.js.map} +0 -0
- /package/dist/{config-XURP6B3S.js.map → agent-dependencies-FCLRGMZM.js.map} +0 -0
- /package/dist/{config-editor-AALY3URF.js.map → agent-registry-KZANAFXQ.js.map} +0 -0
- /package/dist/{config-registry-OGX4YM2U.js.map → agent-store-ZBXGOFPH.js.map} +0 -0
- /package/dist/{chunk-FKOARMAE.js.map → chunk-776VAU3T.js.map} +0 -0
- /package/dist/{chunk-3DIPXFZJ.js.map → chunk-IRGYTNLP.js.map} +0 -0
- /package/dist/{daemon-GWJM2S4A.js.map → chunk-NAMYZIS5.js.map} +0 -0
- /package/dist/{chunk-ZW444AQY.js.map → chunk-NDR5JCS7.js.map} +0 -0
- /package/dist/{chunk-YRJEZD7R.js.map → chunk-VBEWSWVL.js.map} +0 -0
- /package/dist/{doctor-X477CVZN.js.map → config-PCPIBPUA.js.map} +0 -0
- /package/dist/{menu-CARRTW2F.js.map → config-editor-RGV6VKPZ.js.map} +0 -0
- /package/dist/{setup-TTOL7XAN.js.map → config-registry-SNKA2EH2.js.map} +0 -0
|
@@ -1,29 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DoctorEngine
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import {
|
|
5
|
-
getAgentCapabilities
|
|
6
|
-
} from "./chunk-VA2M52CM.js";
|
|
3
|
+
} from "./chunk-IRGYTNLP.js";
|
|
7
4
|
import {
|
|
8
5
|
buildMenuKeyboard,
|
|
9
6
|
buildSkillMessages,
|
|
10
|
-
escapeHtml,
|
|
11
|
-
formatToolCall,
|
|
12
|
-
formatToolUpdate,
|
|
13
|
-
formatUsage,
|
|
14
|
-
handleAgents,
|
|
15
7
|
handleClear,
|
|
16
8
|
handleHelp,
|
|
17
|
-
handleMenu
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
9
|
+
handleMenu
|
|
10
|
+
} from "./chunk-IURZ4QHG.js";
|
|
11
|
+
import {
|
|
12
|
+
AgentCatalog
|
|
13
|
+
} from "./chunk-5MH66WUY.js";
|
|
14
|
+
import {
|
|
15
|
+
getAgentCapabilities
|
|
16
|
+
} from "./chunk-GUHCS6X7.js";
|
|
21
17
|
import {
|
|
22
18
|
getConfigValue,
|
|
23
19
|
getSafeFields,
|
|
24
20
|
isHotReloadable,
|
|
25
21
|
resolveOptions
|
|
26
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-Z46LGZ7R.js";
|
|
27
23
|
import {
|
|
28
24
|
createChildLogger,
|
|
29
25
|
createSessionLogger
|
|
@@ -72,7 +68,7 @@ var StderrCapture = class {
|
|
|
72
68
|
};
|
|
73
69
|
|
|
74
70
|
// src/core/agent-instance.ts
|
|
75
|
-
import { spawn,
|
|
71
|
+
import { spawn, execFileSync } from "child_process";
|
|
76
72
|
import { Transform } from "stream";
|
|
77
73
|
import fs from "fs";
|
|
78
74
|
import path from "path";
|
|
@@ -123,7 +119,7 @@ function resolveAgentCommand(cmd) {
|
|
|
123
119
|
}
|
|
124
120
|
}
|
|
125
121
|
try {
|
|
126
|
-
const fullPath =
|
|
122
|
+
const fullPath = execFileSync("which", [cmd], { encoding: "utf-8" }).trim();
|
|
127
123
|
if (fullPath) {
|
|
128
124
|
const content = fs.readFileSync(fullPath, "utf-8");
|
|
129
125
|
if (content.startsWith("#!/usr/bin/env node")) {
|
|
@@ -501,31 +497,29 @@ ${stderr}`
|
|
|
501
497
|
|
|
502
498
|
// src/core/agent-manager.ts
|
|
503
499
|
var AgentManager = class {
|
|
504
|
-
constructor(
|
|
505
|
-
this.
|
|
500
|
+
constructor(catalog) {
|
|
501
|
+
this.catalog = catalog;
|
|
506
502
|
}
|
|
507
503
|
getAvailableAgents() {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
env:
|
|
504
|
+
const installed = this.catalog.getInstalledEntries();
|
|
505
|
+
return Object.entries(installed).map(([key, agent]) => ({
|
|
506
|
+
name: key,
|
|
507
|
+
command: agent.command,
|
|
508
|
+
args: agent.args,
|
|
509
|
+
env: agent.env
|
|
514
510
|
}));
|
|
515
511
|
}
|
|
516
512
|
getAgent(name) {
|
|
517
|
-
|
|
518
|
-
if (!cfg) return void 0;
|
|
519
|
-
return { name, ...cfg };
|
|
513
|
+
return this.catalog.resolve(name);
|
|
520
514
|
}
|
|
521
515
|
async spawn(agentName, workingDirectory) {
|
|
522
516
|
const agentDef = this.getAgent(agentName);
|
|
523
|
-
if (!agentDef) throw new Error(`Agent "${agentName}" not
|
|
517
|
+
if (!agentDef) throw new Error(`Agent "${agentName}" is not installed. Run "openacp agents install ${agentName}" to add it.`);
|
|
524
518
|
return AgentInstance.spawn(agentDef, workingDirectory);
|
|
525
519
|
}
|
|
526
520
|
async resume(agentName, workingDirectory, agentSessionId) {
|
|
527
521
|
const agentDef = this.getAgent(agentName);
|
|
528
|
-
if (!agentDef) throw new Error(`Agent "${agentName}" not
|
|
522
|
+
if (!agentDef) throw new Error(`Agent "${agentName}" is not installed. Run "openacp agents install ${agentName}" to add it.`);
|
|
529
523
|
return AgentInstance.resume(agentDef, workingDirectory, agentSessionId);
|
|
530
524
|
}
|
|
531
525
|
};
|
|
@@ -652,28 +646,40 @@ var PromptQueue = class {
|
|
|
652
646
|
};
|
|
653
647
|
|
|
654
648
|
// src/core/permission-gate.ts
|
|
649
|
+
var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
655
650
|
var PermissionGate = class {
|
|
656
651
|
request;
|
|
657
652
|
resolveFn;
|
|
658
653
|
rejectFn;
|
|
659
654
|
settled = false;
|
|
655
|
+
timeoutTimer;
|
|
656
|
+
timeoutMs;
|
|
657
|
+
constructor(timeoutMs) {
|
|
658
|
+
this.timeoutMs = timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
659
|
+
}
|
|
660
660
|
setPending(request) {
|
|
661
661
|
this.request = request;
|
|
662
662
|
this.settled = false;
|
|
663
|
+
this.clearTimeout();
|
|
663
664
|
return new Promise((resolve2, reject) => {
|
|
664
665
|
this.resolveFn = resolve2;
|
|
665
666
|
this.rejectFn = reject;
|
|
667
|
+
this.timeoutTimer = setTimeout(() => {
|
|
668
|
+
this.reject("Permission request timed out (no response received)");
|
|
669
|
+
}, this.timeoutMs);
|
|
666
670
|
});
|
|
667
671
|
}
|
|
668
672
|
resolve(optionId) {
|
|
669
673
|
if (this.settled || !this.resolveFn) return;
|
|
670
674
|
this.settled = true;
|
|
675
|
+
this.clearTimeout();
|
|
671
676
|
this.resolveFn(optionId);
|
|
672
677
|
this.cleanup();
|
|
673
678
|
}
|
|
674
679
|
reject(reason) {
|
|
675
680
|
if (this.settled || !this.rejectFn) return;
|
|
676
681
|
this.settled = true;
|
|
682
|
+
this.clearTimeout();
|
|
677
683
|
this.rejectFn(new Error(reason ?? "Permission rejected"));
|
|
678
684
|
this.cleanup();
|
|
679
685
|
}
|
|
@@ -687,6 +693,12 @@ var PermissionGate = class {
|
|
|
687
693
|
get requestId() {
|
|
688
694
|
return this.request?.id;
|
|
689
695
|
}
|
|
696
|
+
clearTimeout() {
|
|
697
|
+
if (this.timeoutTimer) {
|
|
698
|
+
clearTimeout(this.timeoutTimer);
|
|
699
|
+
this.timeoutTimer = void 0;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
690
702
|
cleanup() {
|
|
691
703
|
this.request = void 0;
|
|
692
704
|
this.resolveFn = void 0;
|
|
@@ -1453,6 +1465,7 @@ var JsonFileSessionStore = class {
|
|
|
1453
1465
|
var log5 = createChildLogger({ module: "core" });
|
|
1454
1466
|
var OpenACPCore = class {
|
|
1455
1467
|
configManager;
|
|
1468
|
+
agentCatalog;
|
|
1456
1469
|
agentManager;
|
|
1457
1470
|
sessionManager;
|
|
1458
1471
|
notificationManager;
|
|
@@ -1466,7 +1479,9 @@ var OpenACPCore = class {
|
|
|
1466
1479
|
constructor(configManager) {
|
|
1467
1480
|
this.configManager = configManager;
|
|
1468
1481
|
const config = configManager.get();
|
|
1469
|
-
this.
|
|
1482
|
+
this.agentCatalog = new AgentCatalog();
|
|
1483
|
+
this.agentCatalog.load();
|
|
1484
|
+
this.agentManager = new AgentManager(this.agentCatalog);
|
|
1470
1485
|
const storePath = path3.join(os.homedir(), ".openacp", "sessions.json");
|
|
1471
1486
|
this.sessionStore = new JsonFileSessionStore(
|
|
1472
1487
|
storePath,
|
|
@@ -1494,6 +1509,9 @@ var OpenACPCore = class {
|
|
|
1494
1509
|
this.adapters.set(name, adapter);
|
|
1495
1510
|
}
|
|
1496
1511
|
async start() {
|
|
1512
|
+
this.agentCatalog.refreshRegistryIfStale().catch((err) => {
|
|
1513
|
+
log5.warn({ err }, "Background registry refresh failed");
|
|
1514
|
+
});
|
|
1497
1515
|
for (const adapter of this.adapters.values()) {
|
|
1498
1516
|
await adapter.start();
|
|
1499
1517
|
}
|
|
@@ -1631,8 +1649,9 @@ var OpenACPCore = class {
|
|
|
1631
1649
|
const config = this.configManager.get();
|
|
1632
1650
|
const resolvedAgent = agentName || config.defaultAgent;
|
|
1633
1651
|
log5.info({ channelId, agentName: resolvedAgent }, "New session request");
|
|
1652
|
+
const agentDef = this.agentCatalog.resolve(resolvedAgent);
|
|
1634
1653
|
const resolvedWorkspace = this.configManager.resolveWorkspace(
|
|
1635
|
-
workspacePath ||
|
|
1654
|
+
workspacePath || agentDef?.workingDirectory
|
|
1636
1655
|
);
|
|
1637
1656
|
return this.createSession({
|
|
1638
1657
|
channelId,
|
|
@@ -2128,7 +2147,7 @@ var ApiServer = class {
|
|
|
2128
2147
|
this.sendJson(res, 200, { version: getVersion() });
|
|
2129
2148
|
}
|
|
2130
2149
|
async handleGetEditableConfig(res) {
|
|
2131
|
-
const { getSafeFields: getSafeFields2, resolveOptions: resolveOptions2, getConfigValue: getConfigValue2 } = await import("./config-registry-
|
|
2150
|
+
const { getSafeFields: getSafeFields2, resolveOptions: resolveOptions2, getConfigValue: getConfigValue2 } = await import("./config-registry-SNKA2EH2.js");
|
|
2132
2151
|
const config = this.core.configManager.get();
|
|
2133
2152
|
const safeFields = getSafeFields2();
|
|
2134
2153
|
const fields = safeFields.map((def) => ({
|
|
@@ -2182,7 +2201,7 @@ var ApiServer = class {
|
|
|
2182
2201
|
return;
|
|
2183
2202
|
}
|
|
2184
2203
|
target[lastKey] = value;
|
|
2185
|
-
const { ConfigSchema } = await import("./config-
|
|
2204
|
+
const { ConfigSchema } = await import("./config-PCPIBPUA.js");
|
|
2186
2205
|
const result = ConfigSchema.safeParse(cloned);
|
|
2187
2206
|
if (!result.success) {
|
|
2188
2207
|
this.sendJson(res, 400, {
|
|
@@ -2199,7 +2218,7 @@ var ApiServer = class {
|
|
|
2199
2218
|
}
|
|
2200
2219
|
updateTarget[lastKey] = value;
|
|
2201
2220
|
await this.core.configManager.save(updates, configPath);
|
|
2202
|
-
const { isHotReloadable: isHotReloadable2 } = await import("./config-registry-
|
|
2221
|
+
const { isHotReloadable: isHotReloadable2 } = await import("./config-registry-SNKA2EH2.js");
|
|
2203
2222
|
const needsRestart = !isHotReloadable2(configPath);
|
|
2204
2223
|
this.sendJson(res, 200, {
|
|
2205
2224
|
ok: true,
|
|
@@ -2485,6 +2504,169 @@ function buildDeepLink(chatId, messageId) {
|
|
|
2485
2504
|
// src/adapters/telegram/commands/new-session.ts
|
|
2486
2505
|
import { InlineKeyboard as InlineKeyboard2 } from "grammy";
|
|
2487
2506
|
|
|
2507
|
+
// src/adapters/telegram/formatting.ts
|
|
2508
|
+
function escapeHtml(text) {
|
|
2509
|
+
if (!text) return "";
|
|
2510
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
2511
|
+
}
|
|
2512
|
+
function markdownToTelegramHtml(md) {
|
|
2513
|
+
const codeBlocks = [];
|
|
2514
|
+
const inlineCodes = [];
|
|
2515
|
+
let text = md.replace(/```(\w*)\n?([\s\S]*?)```/g, (_match, lang, code) => {
|
|
2516
|
+
const index = codeBlocks.length;
|
|
2517
|
+
const escapedCode = escapeHtml(code);
|
|
2518
|
+
const langAttr = lang ? ` class="language-${escapeHtml(lang)}"` : "";
|
|
2519
|
+
codeBlocks.push(`<pre><code${langAttr}>${escapedCode}</code></pre>`);
|
|
2520
|
+
return `\0CODE_BLOCK_${index}\0`;
|
|
2521
|
+
});
|
|
2522
|
+
text = text.replace(/`([^`]+)`/g, (_match, code) => {
|
|
2523
|
+
const index = inlineCodes.length;
|
|
2524
|
+
inlineCodes.push(`<code>${escapeHtml(code)}</code>`);
|
|
2525
|
+
return `\0INLINE_CODE_${index}\0`;
|
|
2526
|
+
});
|
|
2527
|
+
text = escapeHtml(text);
|
|
2528
|
+
text = text.replace(/\*\*(.+?)\*\*/g, "<b>$1</b>");
|
|
2529
|
+
text = text.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, "<i>$1</i>");
|
|
2530
|
+
text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>');
|
|
2531
|
+
text = text.replace(/\x00CODE_BLOCK_(\d+)\x00/g, (_match, idx) => {
|
|
2532
|
+
return codeBlocks[parseInt(idx, 10)];
|
|
2533
|
+
});
|
|
2534
|
+
text = text.replace(/\x00INLINE_CODE_(\d+)\x00/g, (_match, idx) => {
|
|
2535
|
+
return inlineCodes[parseInt(idx, 10)];
|
|
2536
|
+
});
|
|
2537
|
+
return text;
|
|
2538
|
+
}
|
|
2539
|
+
var STATUS_ICON = {
|
|
2540
|
+
pending: "\u23F3",
|
|
2541
|
+
in_progress: "\u{1F504}",
|
|
2542
|
+
completed: "\u2705",
|
|
2543
|
+
failed: "\u274C"
|
|
2544
|
+
};
|
|
2545
|
+
var KIND_ICON = {
|
|
2546
|
+
read: "\u{1F4D6}",
|
|
2547
|
+
edit: "\u270F\uFE0F",
|
|
2548
|
+
delete: "\u{1F5D1}\uFE0F",
|
|
2549
|
+
execute: "\u25B6\uFE0F",
|
|
2550
|
+
search: "\u{1F50D}",
|
|
2551
|
+
fetch: "\u{1F310}",
|
|
2552
|
+
think: "\u{1F9E0}",
|
|
2553
|
+
move: "\u{1F4E6}",
|
|
2554
|
+
other: "\u{1F6E0}\uFE0F"
|
|
2555
|
+
};
|
|
2556
|
+
function extractContentText(content, depth = 0) {
|
|
2557
|
+
if (!content || depth > 5) return "";
|
|
2558
|
+
if (typeof content === "string") return content;
|
|
2559
|
+
if (Array.isArray(content)) {
|
|
2560
|
+
return content.map((c) => extractContentText(c, depth + 1)).filter(Boolean).join("\n");
|
|
2561
|
+
}
|
|
2562
|
+
if (typeof content === "object" && content !== null) {
|
|
2563
|
+
const c = content;
|
|
2564
|
+
if (c.type === "text" && typeof c.text === "string") return c.text;
|
|
2565
|
+
if (typeof c.text === "string") return c.text;
|
|
2566
|
+
if (typeof c.content === "string") return c.content;
|
|
2567
|
+
if (c.content && typeof c.content === "object") return extractContentText(c.content, depth + 1);
|
|
2568
|
+
if (c.input) return extractContentText(c.input, depth + 1);
|
|
2569
|
+
if (c.output) return extractContentText(c.output, depth + 1);
|
|
2570
|
+
const keys = Object.keys(c).filter((k) => k !== "type");
|
|
2571
|
+
if (keys.length === 0) return "";
|
|
2572
|
+
return JSON.stringify(c, null, 2);
|
|
2573
|
+
}
|
|
2574
|
+
return String(content);
|
|
2575
|
+
}
|
|
2576
|
+
function truncateContent(text, maxLen = 3800) {
|
|
2577
|
+
if (text.length <= maxLen) return text;
|
|
2578
|
+
return text.slice(0, maxLen) + "\n\u2026 (truncated)";
|
|
2579
|
+
}
|
|
2580
|
+
function formatToolCall(tool) {
|
|
2581
|
+
const si = STATUS_ICON[tool.status || ""] || "\u{1F527}";
|
|
2582
|
+
const ki = KIND_ICON[tool.kind || ""] || "\u{1F6E0}\uFE0F";
|
|
2583
|
+
let text = `${si} ${ki} <b>${escapeHtml(tool.name || "Tool")}</b>`;
|
|
2584
|
+
text += formatViewerLinks(tool.viewerLinks, tool.viewerFilePath);
|
|
2585
|
+
if (!tool.viewerLinks) {
|
|
2586
|
+
const details = extractContentText(tool.content);
|
|
2587
|
+
if (details) {
|
|
2588
|
+
text += `
|
|
2589
|
+
<pre>${escapeHtml(truncateContent(details))}</pre>`;
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
return text;
|
|
2593
|
+
}
|
|
2594
|
+
function formatToolUpdate(update) {
|
|
2595
|
+
const si = STATUS_ICON[update.status] || "\u{1F527}";
|
|
2596
|
+
const ki = KIND_ICON[update.kind || ""] || "\u{1F6E0}\uFE0F";
|
|
2597
|
+
const name = update.name || "Tool";
|
|
2598
|
+
let text = `${si} ${ki} <b>${escapeHtml(name)}</b>`;
|
|
2599
|
+
text += formatViewerLinks(update.viewerLinks, update.viewerFilePath);
|
|
2600
|
+
if (!update.viewerLinks) {
|
|
2601
|
+
const details = extractContentText(update.content);
|
|
2602
|
+
if (details) {
|
|
2603
|
+
text += `
|
|
2604
|
+
<pre>${escapeHtml(truncateContent(details))}</pre>`;
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
return text;
|
|
2608
|
+
}
|
|
2609
|
+
function formatViewerLinks(links, filePath) {
|
|
2610
|
+
if (!links) return "";
|
|
2611
|
+
const fileName = filePath ? filePath.split("/").pop() || filePath : "";
|
|
2612
|
+
let text = "\n";
|
|
2613
|
+
if (links.file) text += `
|
|
2614
|
+
\u{1F4C4} <a href="${escapeHtml(links.file)}">View ${escapeHtml(fileName || "file")}</a>`;
|
|
2615
|
+
if (links.diff) text += `
|
|
2616
|
+
\u{1F4DD} <a href="${escapeHtml(links.diff)}">View diff${fileName ? ` \u2014 ${escapeHtml(fileName)}` : ""}</a>`;
|
|
2617
|
+
return text;
|
|
2618
|
+
}
|
|
2619
|
+
function formatTokens(n) {
|
|
2620
|
+
return n >= 1e3 ? `${Math.round(n / 1e3)}k` : String(n);
|
|
2621
|
+
}
|
|
2622
|
+
function progressBar(ratio) {
|
|
2623
|
+
const filled = Math.round(Math.min(ratio, 1) * 10);
|
|
2624
|
+
return "\u2593".repeat(filled) + "\u2591".repeat(10 - filled);
|
|
2625
|
+
}
|
|
2626
|
+
function formatUsage(usage) {
|
|
2627
|
+
const { tokensUsed, contextSize } = usage;
|
|
2628
|
+
if (tokensUsed == null) return "\u{1F4CA} Usage data unavailable";
|
|
2629
|
+
if (contextSize == null) return `\u{1F4CA} ${formatTokens(tokensUsed)} tokens`;
|
|
2630
|
+
const ratio = tokensUsed / contextSize;
|
|
2631
|
+
const pct = Math.round(ratio * 100);
|
|
2632
|
+
const bar = progressBar(ratio);
|
|
2633
|
+
const emoji = pct >= 85 ? "\u26A0\uFE0F" : "\u{1F4CA}";
|
|
2634
|
+
return `${emoji} ${formatTokens(tokensUsed)} / ${formatTokens(contextSize)} tokens
|
|
2635
|
+
${bar} ${pct}%`;
|
|
2636
|
+
}
|
|
2637
|
+
function splitMessage(text, maxLength = 3800) {
|
|
2638
|
+
if (text.length <= maxLength) return [text];
|
|
2639
|
+
const chunks = [];
|
|
2640
|
+
let remaining = text;
|
|
2641
|
+
while (remaining.length > 0) {
|
|
2642
|
+
if (remaining.length <= maxLength) {
|
|
2643
|
+
chunks.push(remaining);
|
|
2644
|
+
break;
|
|
2645
|
+
}
|
|
2646
|
+
const wouldLeaveSmall = remaining.length < maxLength * 1.3;
|
|
2647
|
+
const searchLimit = wouldLeaveSmall ? Math.floor(remaining.length / 2) + 300 : maxLength;
|
|
2648
|
+
let splitAt = remaining.lastIndexOf("\n\n", searchLimit);
|
|
2649
|
+
if (splitAt === -1 || splitAt < searchLimit * 0.2) {
|
|
2650
|
+
splitAt = remaining.lastIndexOf("\n", searchLimit);
|
|
2651
|
+
}
|
|
2652
|
+
if (splitAt === -1 || splitAt < searchLimit * 0.2) {
|
|
2653
|
+
splitAt = searchLimit;
|
|
2654
|
+
}
|
|
2655
|
+
const candidate = remaining.slice(0, splitAt);
|
|
2656
|
+
const fences = candidate.match(/```/g);
|
|
2657
|
+
if (fences && fences.length % 2 !== 0) {
|
|
2658
|
+
const closingFence = remaining.indexOf("```", splitAt);
|
|
2659
|
+
if (closingFence !== -1) {
|
|
2660
|
+
const afterFence = remaining.indexOf("\n", closingFence + 3);
|
|
2661
|
+
splitAt = afterFence !== -1 ? afterFence + 1 : closingFence + 3;
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2664
|
+
chunks.push(remaining.slice(0, splitAt));
|
|
2665
|
+
remaining = remaining.slice(splitAt).replace(/^\n+/, "");
|
|
2666
|
+
}
|
|
2667
|
+
return chunks;
|
|
2668
|
+
}
|
|
2669
|
+
|
|
2488
2670
|
// src/adapters/telegram/commands/admin.ts
|
|
2489
2671
|
import { InlineKeyboard } from "grammy";
|
|
2490
2672
|
var log9 = createChildLogger({ module: "telegram-cmd-admin" });
|
|
@@ -2688,37 +2870,12 @@ async function handleNew(ctx, core, chatId, assistant) {
|
|
|
2688
2870
|
return;
|
|
2689
2871
|
}
|
|
2690
2872
|
}
|
|
2691
|
-
|
|
2692
|
-
if (!userId) return;
|
|
2693
|
-
const agents = core.agentManager.getAvailableAgents();
|
|
2694
|
-
const config = core.configManager.get();
|
|
2695
|
-
if (agentName || agents.length === 1) {
|
|
2696
|
-
const selectedAgent = agentName || config.defaultAgent;
|
|
2697
|
-
await startWorkspaceStep(ctx, core, chatId, userId, selectedAgent);
|
|
2698
|
-
return;
|
|
2699
|
-
}
|
|
2700
|
-
const keyboard = new InlineKeyboard2();
|
|
2701
|
-
for (const agent of agents) {
|
|
2702
|
-
const label = agent.name === config.defaultAgent ? `${agent.name} (default)` : agent.name;
|
|
2703
|
-
keyboard.text(label, `m:new:agent:${agent.name}`).row();
|
|
2704
|
-
}
|
|
2705
|
-
keyboard.text("\u274C Cancel", "m:new:cancel");
|
|
2706
|
-
const msg = await ctx.reply(
|
|
2707
|
-
`\u{1F916} <b>Choose an agent:</b>`,
|
|
2708
|
-
{ parse_mode: "HTML", reply_markup: keyboard }
|
|
2709
|
-
);
|
|
2710
|
-
cleanupPending(userId);
|
|
2711
|
-
pendingNewSessions.set(userId, {
|
|
2712
|
-
step: "agent",
|
|
2713
|
-
messageId: msg.message_id,
|
|
2714
|
-
threadId: currentThreadId,
|
|
2715
|
-
timer: setTimeout(() => pendingNewSessions.delete(userId), PENDING_TIMEOUT_MS)
|
|
2716
|
-
});
|
|
2873
|
+
await showAgentPicker(ctx, core, chatId, agentName);
|
|
2717
2874
|
}
|
|
2718
2875
|
async function startWorkspaceStep(ctx, core, chatId, userId, agentName) {
|
|
2719
2876
|
const config = core.configManager.get();
|
|
2720
2877
|
const baseDir = config.workspace.baseDir;
|
|
2721
|
-
const keyboard = new InlineKeyboard2().text(`\u{1F4C1} Use ${baseDir}`, "m:new:ws:default").row().text("\u270F\uFE0F Enter project path", "m:new:ws:custom")
|
|
2878
|
+
const keyboard = new InlineKeyboard2().text(`\u{1F4C1} Use ${baseDir}`, "m:new:ws:default").row().text("\u270F\uFE0F Enter project path", "m:new:ws:custom");
|
|
2722
2879
|
const text = `\u{1F4C1} <b>Where should ${escapeHtml(agentName)} work?</b>
|
|
2723
2880
|
|
|
2724
2881
|
Enter the path to your project folder \u2014 the agent will read, write, and run code there.
|
|
@@ -2953,30 +3110,35 @@ async function handlePendingWorkspaceInput(ctx, core, chatId, assistantTopicId)
|
|
|
2953
3110
|
return true;
|
|
2954
3111
|
}
|
|
2955
3112
|
async function startInteractiveNewSession(ctx, core, chatId, agentName) {
|
|
3113
|
+
await showAgentPicker(ctx, core, chatId, agentName);
|
|
3114
|
+
}
|
|
3115
|
+
async function showAgentPicker(ctx, core, chatId, agentName) {
|
|
2956
3116
|
const userId = ctx.from?.id;
|
|
2957
3117
|
if (!userId) return;
|
|
2958
|
-
const
|
|
3118
|
+
const installedEntries = core.agentCatalog.getInstalledEntries();
|
|
3119
|
+
const agentKeys = Object.keys(installedEntries);
|
|
2959
3120
|
const config = core.configManager.get();
|
|
2960
|
-
if (agentName ||
|
|
3121
|
+
if (agentName || agentKeys.length === 1) {
|
|
2961
3122
|
const selectedAgent = agentName || config.defaultAgent;
|
|
2962
3123
|
await startWorkspaceStep(ctx, core, chatId, userId, selectedAgent);
|
|
2963
3124
|
return;
|
|
2964
3125
|
}
|
|
2965
3126
|
const keyboard = new InlineKeyboard2();
|
|
2966
|
-
for (const
|
|
2967
|
-
const
|
|
2968
|
-
|
|
3127
|
+
for (const key of agentKeys) {
|
|
3128
|
+
const agent = installedEntries[key];
|
|
3129
|
+
const label = key === config.defaultAgent ? `${agent.name} (default)` : agent.name;
|
|
3130
|
+
keyboard.text(label, `m:new:agent:${key}`).row();
|
|
2969
3131
|
}
|
|
2970
|
-
keyboard.text("\u274C Cancel", "m:new:cancel");
|
|
2971
3132
|
const msg = await ctx.reply(
|
|
2972
3133
|
`\u{1F916} <b>Choose an agent:</b>`,
|
|
2973
3134
|
{ parse_mode: "HTML", reply_markup: keyboard }
|
|
2974
3135
|
);
|
|
2975
3136
|
cleanupPending(userId);
|
|
3137
|
+
const threadId = ctx.message?.message_thread_id ?? ctx.callbackQuery?.message?.message_thread_id;
|
|
2976
3138
|
pendingNewSessions.set(userId, {
|
|
2977
3139
|
step: "agent",
|
|
2978
3140
|
messageId: msg.message_id,
|
|
2979
|
-
threadId
|
|
3141
|
+
threadId,
|
|
2980
3142
|
timer: setTimeout(() => pendingNewSessions.delete(userId), PENDING_TIMEOUT_MS)
|
|
2981
3143
|
});
|
|
2982
3144
|
}
|
|
@@ -3350,12 +3512,250 @@ function setupSessionCallbacks(bot, core, chatId, systemTopicIds) {
|
|
|
3350
3512
|
});
|
|
3351
3513
|
}
|
|
3352
3514
|
|
|
3353
|
-
// src/adapters/telegram/commands/
|
|
3515
|
+
// src/adapters/telegram/commands/agents.ts
|
|
3354
3516
|
import { InlineKeyboard as InlineKeyboard4 } from "grammy";
|
|
3517
|
+
var AGENTS_PER_PAGE = 6;
|
|
3518
|
+
async function handleAgents(ctx, core, page = 0) {
|
|
3519
|
+
const catalog = core.agentCatalog;
|
|
3520
|
+
const items = catalog.getAvailable();
|
|
3521
|
+
const installed = items.filter((i) => i.installed);
|
|
3522
|
+
const available = items.filter((i) => !i.installed);
|
|
3523
|
+
let text = "<b>\u{1F916} Agents</b>\n\n";
|
|
3524
|
+
if (installed.length > 0) {
|
|
3525
|
+
text += "<b>Installed:</b>\n";
|
|
3526
|
+
for (const item of installed) {
|
|
3527
|
+
text += `\u2705 <b>${escapeHtml(item.name)}</b>`;
|
|
3528
|
+
if (item.description) {
|
|
3529
|
+
text += ` \u2014 <i>${escapeHtml(truncate(item.description, 50))}</i>`;
|
|
3530
|
+
}
|
|
3531
|
+
text += "\n";
|
|
3532
|
+
}
|
|
3533
|
+
text += "\n";
|
|
3534
|
+
}
|
|
3535
|
+
if (available.length > 0) {
|
|
3536
|
+
const totalPages = Math.ceil(available.length / AGENTS_PER_PAGE);
|
|
3537
|
+
const safePage = Math.max(0, Math.min(page, totalPages - 1));
|
|
3538
|
+
const pageItems = available.slice(safePage * AGENTS_PER_PAGE, (safePage + 1) * AGENTS_PER_PAGE);
|
|
3539
|
+
text += `<b>Available to install:</b>`;
|
|
3540
|
+
if (totalPages > 1) {
|
|
3541
|
+
text += ` (${safePage + 1}/${totalPages})`;
|
|
3542
|
+
}
|
|
3543
|
+
text += "\n";
|
|
3544
|
+
for (const item of pageItems) {
|
|
3545
|
+
if (item.available) {
|
|
3546
|
+
text += `\u2B07\uFE0F <b>${escapeHtml(item.name)}</b>`;
|
|
3547
|
+
} else {
|
|
3548
|
+
const deps = item.missingDeps?.join(", ") ?? "requirements not met";
|
|
3549
|
+
text += `\u26A0\uFE0F <b>${escapeHtml(item.name)}</b> <i>(needs: ${escapeHtml(deps)})</i>`;
|
|
3550
|
+
}
|
|
3551
|
+
if (item.description) {
|
|
3552
|
+
text += `
|
|
3553
|
+
<i>${escapeHtml(truncate(item.description, 60))}</i>`;
|
|
3554
|
+
}
|
|
3555
|
+
text += "\n";
|
|
3556
|
+
}
|
|
3557
|
+
const keyboard = new InlineKeyboard4();
|
|
3558
|
+
const installable = pageItems.filter((i) => i.available);
|
|
3559
|
+
for (let i = 0; i < installable.length; i += 2) {
|
|
3560
|
+
const row = installable.slice(i, i + 2);
|
|
3561
|
+
for (const item of row) {
|
|
3562
|
+
keyboard.text(`\u2B07\uFE0F ${item.name}`, `ag:install:${item.key}`);
|
|
3563
|
+
}
|
|
3564
|
+
keyboard.row();
|
|
3565
|
+
}
|
|
3566
|
+
if (totalPages > 1) {
|
|
3567
|
+
if (safePage > 0) {
|
|
3568
|
+
keyboard.text("\u25C0\uFE0F Prev", `ag:page:${safePage - 1}`);
|
|
3569
|
+
}
|
|
3570
|
+
if (safePage < totalPages - 1) {
|
|
3571
|
+
keyboard.text("Next \u25B6\uFE0F", `ag:page:${safePage + 1}`);
|
|
3572
|
+
}
|
|
3573
|
+
keyboard.row();
|
|
3574
|
+
}
|
|
3575
|
+
if (available.some((i) => !i.available)) {
|
|
3576
|
+
text += "\n\u{1F4A1} <i>Agents marked \u26A0\uFE0F need additional setup. Use</i> <code>openacp agents info <name></code> <i>for details.</i>\n";
|
|
3577
|
+
}
|
|
3578
|
+
await ctx.reply(text, { parse_mode: "HTML", reply_markup: keyboard });
|
|
3579
|
+
} else {
|
|
3580
|
+
text += "<i>All agents are already installed!</i>";
|
|
3581
|
+
await ctx.reply(text, { parse_mode: "HTML" });
|
|
3582
|
+
}
|
|
3583
|
+
}
|
|
3584
|
+
async function handleInstall(ctx, core) {
|
|
3585
|
+
const text = (ctx.message?.text ?? "").trim();
|
|
3586
|
+
const parts = text.split(/\s+/);
|
|
3587
|
+
const nameOrId = parts[1];
|
|
3588
|
+
if (!nameOrId) {
|
|
3589
|
+
await ctx.reply(
|
|
3590
|
+
"\u{1F4E6} <b>Install an agent</b>\n\nUsage: <code>/install <agent-name></code>\nExample: <code>/install gemini</code>\n\nUse /agents to browse available agents.",
|
|
3591
|
+
{ parse_mode: "HTML" }
|
|
3592
|
+
);
|
|
3593
|
+
return;
|
|
3594
|
+
}
|
|
3595
|
+
await installAgentWithProgress(ctx, core, nameOrId);
|
|
3596
|
+
}
|
|
3597
|
+
async function handleAgentCallback(ctx, core) {
|
|
3598
|
+
const data = ctx.callbackQuery?.data ?? "";
|
|
3599
|
+
await ctx.answerCallbackQuery();
|
|
3600
|
+
if (data.startsWith("ag:install:")) {
|
|
3601
|
+
const nameOrId = data.replace("ag:install:", "");
|
|
3602
|
+
await installAgentWithProgress(ctx, core, nameOrId);
|
|
3603
|
+
return;
|
|
3604
|
+
}
|
|
3605
|
+
if (data.startsWith("ag:page:")) {
|
|
3606
|
+
const page = parseInt(data.replace("ag:page:", ""), 10);
|
|
3607
|
+
try {
|
|
3608
|
+
const catalog = core.agentCatalog;
|
|
3609
|
+
const items = catalog.getAvailable();
|
|
3610
|
+
const installed = items.filter((i) => i.installed);
|
|
3611
|
+
const available = items.filter((i) => !i.installed);
|
|
3612
|
+
let text = "<b>\u{1F916} Agents</b>\n\n";
|
|
3613
|
+
if (installed.length > 0) {
|
|
3614
|
+
text += "<b>Installed:</b>\n";
|
|
3615
|
+
for (const item of installed) {
|
|
3616
|
+
text += `\u2705 <b>${escapeHtml(item.name)}</b>`;
|
|
3617
|
+
if (item.description) {
|
|
3618
|
+
text += ` \u2014 <i>${escapeHtml(truncate(item.description, 50))}</i>`;
|
|
3619
|
+
}
|
|
3620
|
+
text += "\n";
|
|
3621
|
+
}
|
|
3622
|
+
text += "\n";
|
|
3623
|
+
}
|
|
3624
|
+
const totalPages = Math.ceil(available.length / AGENTS_PER_PAGE);
|
|
3625
|
+
const safePage = Math.max(0, Math.min(page, totalPages - 1));
|
|
3626
|
+
const pageItems = available.slice(safePage * AGENTS_PER_PAGE, (safePage + 1) * AGENTS_PER_PAGE);
|
|
3627
|
+
text += `<b>Available to install:</b>`;
|
|
3628
|
+
if (totalPages > 1) {
|
|
3629
|
+
text += ` (${safePage + 1}/${totalPages})`;
|
|
3630
|
+
}
|
|
3631
|
+
text += "\n";
|
|
3632
|
+
for (const item of pageItems) {
|
|
3633
|
+
if (item.available) {
|
|
3634
|
+
text += `\u2B07\uFE0F <b>${escapeHtml(item.name)}</b>`;
|
|
3635
|
+
} else {
|
|
3636
|
+
const deps = item.missingDeps?.join(", ") ?? "requirements not met";
|
|
3637
|
+
text += `\u26A0\uFE0F <b>${escapeHtml(item.name)}</b> <i>(needs: ${escapeHtml(deps)})</i>`;
|
|
3638
|
+
}
|
|
3639
|
+
if (item.description) {
|
|
3640
|
+
text += `
|
|
3641
|
+
<i>${escapeHtml(truncate(item.description, 60))}</i>`;
|
|
3642
|
+
}
|
|
3643
|
+
text += "\n";
|
|
3644
|
+
}
|
|
3645
|
+
const keyboard = new InlineKeyboard4();
|
|
3646
|
+
const installable = pageItems.filter((i) => i.available);
|
|
3647
|
+
for (let i = 0; i < installable.length; i += 2) {
|
|
3648
|
+
const row = installable.slice(i, i + 2);
|
|
3649
|
+
for (const item of row) {
|
|
3650
|
+
keyboard.text(`\u2B07\uFE0F ${item.name}`, `ag:install:${item.key}`);
|
|
3651
|
+
}
|
|
3652
|
+
keyboard.row();
|
|
3653
|
+
}
|
|
3654
|
+
if (totalPages > 1) {
|
|
3655
|
+
if (safePage > 0) {
|
|
3656
|
+
keyboard.text("\u25C0\uFE0F Prev", `ag:page:${safePage - 1}`);
|
|
3657
|
+
}
|
|
3658
|
+
if (safePage < totalPages - 1) {
|
|
3659
|
+
keyboard.text("Next \u25B6\uFE0F", `ag:page:${safePage + 1}`);
|
|
3660
|
+
}
|
|
3661
|
+
keyboard.row();
|
|
3662
|
+
}
|
|
3663
|
+
await ctx.editMessageText(text, { parse_mode: "HTML", reply_markup: keyboard });
|
|
3664
|
+
} catch {
|
|
3665
|
+
}
|
|
3666
|
+
}
|
|
3667
|
+
}
|
|
3668
|
+
async function installAgentWithProgress(ctx, core, nameOrId) {
|
|
3669
|
+
const catalog = core.agentCatalog;
|
|
3670
|
+
const msg = await ctx.reply(`\u23F3 Installing <b>${escapeHtml(nameOrId)}</b>...`, { parse_mode: "HTML" });
|
|
3671
|
+
let lastEdit = 0;
|
|
3672
|
+
const EDIT_THROTTLE_MS = 1500;
|
|
3673
|
+
const progress = {
|
|
3674
|
+
onStart(_id, _name) {
|
|
3675
|
+
},
|
|
3676
|
+
async onStep(step) {
|
|
3677
|
+
const now = Date.now();
|
|
3678
|
+
if (now - lastEdit > EDIT_THROTTLE_MS) {
|
|
3679
|
+
lastEdit = now;
|
|
3680
|
+
try {
|
|
3681
|
+
await ctx.api.editMessageText(msg.chat.id, msg.message_id, `\u23F3 <b>${escapeHtml(nameOrId)}</b>: ${escapeHtml(step)}`, { parse_mode: "HTML" });
|
|
3682
|
+
} catch {
|
|
3683
|
+
}
|
|
3684
|
+
}
|
|
3685
|
+
},
|
|
3686
|
+
async onDownloadProgress(percent) {
|
|
3687
|
+
const now = Date.now();
|
|
3688
|
+
if (now - lastEdit > EDIT_THROTTLE_MS) {
|
|
3689
|
+
lastEdit = now;
|
|
3690
|
+
try {
|
|
3691
|
+
const bar = buildProgressBar(percent);
|
|
3692
|
+
await ctx.api.editMessageText(msg.chat.id, msg.message_id, `\u23F3 <b>${escapeHtml(nameOrId)}</b>
|
|
3693
|
+
Downloading... ${bar} ${percent}%`, { parse_mode: "HTML" });
|
|
3694
|
+
} catch {
|
|
3695
|
+
}
|
|
3696
|
+
}
|
|
3697
|
+
},
|
|
3698
|
+
async onSuccess(name) {
|
|
3699
|
+
try {
|
|
3700
|
+
const keyboard = new InlineKeyboard4().text(`\u{1F680} Start session with ${name}`, `na:${nameOrId}`);
|
|
3701
|
+
await ctx.api.editMessageText(msg.chat.id, msg.message_id, `\u2705 <b>${escapeHtml(name)}</b> installed!`, { parse_mode: "HTML", reply_markup: keyboard });
|
|
3702
|
+
} catch {
|
|
3703
|
+
}
|
|
3704
|
+
},
|
|
3705
|
+
async onError(error) {
|
|
3706
|
+
try {
|
|
3707
|
+
await ctx.api.editMessageText(msg.chat.id, msg.message_id, `\u274C ${escapeHtml(error)}`, { parse_mode: "HTML" });
|
|
3708
|
+
} catch {
|
|
3709
|
+
}
|
|
3710
|
+
}
|
|
3711
|
+
};
|
|
3712
|
+
const result = await catalog.install(nameOrId, progress);
|
|
3713
|
+
if (result.ok) {
|
|
3714
|
+
const { getAgentCapabilities: getAgentCapabilities2 } = await import("./agent-dependencies-FCLRGMZM.js");
|
|
3715
|
+
const caps = getAgentCapabilities2(result.agentKey);
|
|
3716
|
+
if (caps.integration) {
|
|
3717
|
+
const { installIntegration } = await import("./integrate-X7LI6MUO.js");
|
|
3718
|
+
const intResult = await installIntegration(result.agentKey, caps.integration);
|
|
3719
|
+
if (intResult.success) {
|
|
3720
|
+
try {
|
|
3721
|
+
await ctx.reply(`\u{1F517} Handoff integration installed for <b>${escapeHtml(result.agentKey)}</b>`, { parse_mode: "HTML" });
|
|
3722
|
+
} catch {
|
|
3723
|
+
}
|
|
3724
|
+
}
|
|
3725
|
+
}
|
|
3726
|
+
}
|
|
3727
|
+
if (result.ok && result.setupSteps?.length) {
|
|
3728
|
+
let setupText = `\u{1F4CB} <b>Setup for ${escapeHtml(result.agentKey)}:</b>
|
|
3729
|
+
|
|
3730
|
+
`;
|
|
3731
|
+
for (const step of result.setupSteps) {
|
|
3732
|
+
setupText += `\u2192 ${escapeHtml(step)}
|
|
3733
|
+
`;
|
|
3734
|
+
}
|
|
3735
|
+
setupText += `
|
|
3736
|
+
<i>Run in terminal: openacp agents info ${escapeHtml(result.agentKey)}</i>`;
|
|
3737
|
+
try {
|
|
3738
|
+
await ctx.reply(setupText, { parse_mode: "HTML" });
|
|
3739
|
+
} catch {
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
3742
|
+
}
|
|
3743
|
+
function truncate(text, maxLen) {
|
|
3744
|
+
if (text.length <= maxLen) return text;
|
|
3745
|
+
return text.slice(0, maxLen - 1) + "\u2026";
|
|
3746
|
+
}
|
|
3747
|
+
function buildProgressBar(percent) {
|
|
3748
|
+
const filled = Math.round(percent / 10);
|
|
3749
|
+
const empty = 10 - filled;
|
|
3750
|
+
return "\u2588".repeat(filled) + "\u2591".repeat(empty);
|
|
3751
|
+
}
|
|
3752
|
+
|
|
3753
|
+
// src/adapters/telegram/commands/integrate.ts
|
|
3754
|
+
import { InlineKeyboard as InlineKeyboard5 } from "grammy";
|
|
3355
3755
|
async function handleIntegrate(ctx, _core) {
|
|
3356
|
-
const { listIntegrations } = await import("./integrate-
|
|
3756
|
+
const { listIntegrations } = await import("./integrate-X7LI6MUO.js");
|
|
3357
3757
|
const agents = listIntegrations();
|
|
3358
|
-
const keyboard = new
|
|
3758
|
+
const keyboard = new InlineKeyboard5();
|
|
3359
3759
|
for (const agent of agents) {
|
|
3360
3760
|
keyboard.text(`\u{1F916} ${agent}`, `i:agent:${agent}`).row();
|
|
3361
3761
|
}
|
|
@@ -3367,7 +3767,7 @@ Select an agent to manage its integrations.`,
|
|
|
3367
3767
|
);
|
|
3368
3768
|
}
|
|
3369
3769
|
function buildAgentItemsKeyboard(agentName, items) {
|
|
3370
|
-
const keyboard = new
|
|
3770
|
+
const keyboard = new InlineKeyboard5();
|
|
3371
3771
|
for (const item of items) {
|
|
3372
3772
|
const installed = item.isInstalled();
|
|
3373
3773
|
keyboard.text(
|
|
@@ -3386,9 +3786,9 @@ function setupIntegrateCallbacks(bot, core) {
|
|
|
3386
3786
|
} catch {
|
|
3387
3787
|
}
|
|
3388
3788
|
if (data === "i:back") {
|
|
3389
|
-
const { listIntegrations } = await import("./integrate-
|
|
3789
|
+
const { listIntegrations } = await import("./integrate-X7LI6MUO.js");
|
|
3390
3790
|
const agents = listIntegrations();
|
|
3391
|
-
const keyboard2 = new
|
|
3791
|
+
const keyboard2 = new InlineKeyboard5();
|
|
3392
3792
|
for (const agent of agents) {
|
|
3393
3793
|
keyboard2.text(`\u{1F916} ${agent}`, `i:agent:${agent}`).row();
|
|
3394
3794
|
}
|
|
@@ -3406,7 +3806,7 @@ Select an agent to manage its integrations.`,
|
|
|
3406
3806
|
const agentMatch = data.match(/^i:agent:(.+)$/);
|
|
3407
3807
|
if (agentMatch) {
|
|
3408
3808
|
const agentName2 = agentMatch[1];
|
|
3409
|
-
const { getIntegration: getIntegration2 } = await import("./integrate-
|
|
3809
|
+
const { getIntegration: getIntegration2 } = await import("./integrate-X7LI6MUO.js");
|
|
3410
3810
|
const integration2 = getIntegration2(agentName2);
|
|
3411
3811
|
if (!integration2) {
|
|
3412
3812
|
await ctx.reply(`\u274C No integration available for '${escapeHtml(agentName2)}'.`, { parse_mode: "HTML" });
|
|
@@ -3433,7 +3833,7 @@ ${integration2.items.map((i) => `\u2022 <b>${escapeHtml(i.name)}</b> \u2014 ${es
|
|
|
3433
3833
|
const action = actionMatch[1];
|
|
3434
3834
|
const agentName = actionMatch[2];
|
|
3435
3835
|
const itemId = actionMatch[3];
|
|
3436
|
-
const { getIntegration } = await import("./integrate-
|
|
3836
|
+
const { getIntegration } = await import("./integrate-X7LI6MUO.js");
|
|
3437
3837
|
const integration = getIntegration(agentName);
|
|
3438
3838
|
if (!integration) return;
|
|
3439
3839
|
const item = integration.items.find((i) => i.id === itemId);
|
|
@@ -3469,12 +3869,12 @@ ${resultText}`,
|
|
|
3469
3869
|
}
|
|
3470
3870
|
|
|
3471
3871
|
// src/adapters/telegram/commands/settings.ts
|
|
3472
|
-
import { InlineKeyboard as
|
|
3872
|
+
import { InlineKeyboard as InlineKeyboard6 } from "grammy";
|
|
3473
3873
|
var log12 = createChildLogger({ module: "telegram-settings" });
|
|
3474
3874
|
function buildSettingsKeyboard(core) {
|
|
3475
3875
|
const config = core.configManager.get();
|
|
3476
3876
|
const fields = getSafeFields();
|
|
3477
|
-
const kb = new
|
|
3877
|
+
const kb = new InlineKeyboard6();
|
|
3478
3878
|
for (const field of fields) {
|
|
3479
3879
|
const value = getConfigValue(config, field.path);
|
|
3480
3880
|
const label = formatFieldLabel(field, value);
|
|
@@ -3545,7 +3945,7 @@ function setupSettingsCallbacks(bot, core, getAssistantSession) {
|
|
|
3545
3945
|
if (!fieldDef) return;
|
|
3546
3946
|
const options = resolveOptions(fieldDef, config) ?? [];
|
|
3547
3947
|
const currentValue = getConfigValue(config, fieldPath);
|
|
3548
|
-
const kb = new
|
|
3948
|
+
const kb = new InlineKeyboard6();
|
|
3549
3949
|
for (const opt of options) {
|
|
3550
3950
|
const marker = opt === String(currentValue) ? " \u2713" : "";
|
|
3551
3951
|
kb.text(`${opt}${marker}`, `s:pick:${fieldPath}:${opt}`).row();
|
|
@@ -3617,7 +4017,7 @@ Tap to change:`, {
|
|
|
3617
4017
|
await ctx.answerCallbackQuery();
|
|
3618
4018
|
} catch {
|
|
3619
4019
|
}
|
|
3620
|
-
const { buildMenuKeyboard: buildMenuKeyboard3 } = await import("./menu-
|
|
4020
|
+
const { buildMenuKeyboard: buildMenuKeyboard3 } = await import("./menu-J5YVH665.js");
|
|
3621
4021
|
try {
|
|
3622
4022
|
await ctx.editMessageText(`<b>OpenACP Menu</b>
|
|
3623
4023
|
Choose an action:`, {
|
|
@@ -3655,7 +4055,7 @@ function buildNestedUpdate(dotPath, value) {
|
|
|
3655
4055
|
}
|
|
3656
4056
|
|
|
3657
4057
|
// src/adapters/telegram/commands/doctor.ts
|
|
3658
|
-
import { InlineKeyboard as
|
|
4058
|
+
import { InlineKeyboard as InlineKeyboard7 } from "grammy";
|
|
3659
4059
|
var log13 = createChildLogger({ module: "telegram-cmd-doctor" });
|
|
3660
4060
|
var pendingFixesStore = /* @__PURE__ */ new Map();
|
|
3661
4061
|
function renderReport(report) {
|
|
@@ -3673,7 +4073,7 @@ function renderReport(report) {
|
|
|
3673
4073
|
lines.push(`<b>Result:</b> ${passed} passed, ${warnings} warnings, ${failed} failed${fixedStr}`);
|
|
3674
4074
|
let keyboard;
|
|
3675
4075
|
if (report.pendingFixes.length > 0) {
|
|
3676
|
-
keyboard = new
|
|
4076
|
+
keyboard = new InlineKeyboard7();
|
|
3677
4077
|
for (let i = 0; i < report.pendingFixes.length; i++) {
|
|
3678
4078
|
const label = `\u{1F527} Fix: ${report.pendingFixes[i].message.slice(0, 30)}`;
|
|
3679
4079
|
keyboard.text(label, `m:doctor:fix:${i}`).row();
|
|
@@ -3768,6 +4168,7 @@ function setupCommands(bot, core, chatId, assistant) {
|
|
|
3768
4168
|
bot.command("status", (ctx) => handleStatus(ctx, core));
|
|
3769
4169
|
bot.command("sessions", (ctx) => handleTopics(ctx, core));
|
|
3770
4170
|
bot.command("agents", (ctx) => handleAgents(ctx, core));
|
|
4171
|
+
bot.command("install", (ctx) => handleInstall(ctx, core));
|
|
3771
4172
|
bot.command("help", (ctx) => handleHelp(ctx));
|
|
3772
4173
|
bot.command("menu", (ctx) => handleMenu(ctx));
|
|
3773
4174
|
bot.command("enable_dangerous", (ctx) => handleEnableDangerous(ctx, core));
|
|
@@ -3783,6 +4184,12 @@ function setupAllCallbacks(bot, core, chatId, systemTopicIds, getAssistantSessio
|
|
|
3783
4184
|
setupSessionCallbacks(bot, core, chatId, systemTopicIds);
|
|
3784
4185
|
setupSettingsCallbacks(bot, core, getAssistantSession ?? (() => void 0));
|
|
3785
4186
|
setupDoctorCallbacks(bot);
|
|
4187
|
+
bot.callbackQuery(/^ag:/, (ctx) => handleAgentCallback(ctx, core));
|
|
4188
|
+
bot.callbackQuery(/^na:/, async (ctx) => {
|
|
4189
|
+
const agentKey = ctx.callbackQuery.data.replace("na:", "");
|
|
4190
|
+
await ctx.answerCallbackQuery();
|
|
4191
|
+
await createSessionDirect(ctx, core, chatId, agentKey, core.configManager.get().workspace.baseDir);
|
|
4192
|
+
});
|
|
3786
4193
|
bot.callbackQuery(/^m:/, async (ctx) => {
|
|
3787
4194
|
const data = ctx.callbackQuery.data;
|
|
3788
4195
|
try {
|
|
@@ -3827,6 +4234,7 @@ var STATIC_COMMANDS = [
|
|
|
3827
4234
|
{ command: "status", description: "Show status" },
|
|
3828
4235
|
{ command: "sessions", description: "List all sessions" },
|
|
3829
4236
|
{ command: "agents", description: "List available agents" },
|
|
4237
|
+
{ command: "install", description: "Install a new agent" },
|
|
3830
4238
|
{ command: "help", description: "Help" },
|
|
3831
4239
|
{ command: "menu", description: "Show menu" },
|
|
3832
4240
|
{ command: "enable_dangerous", description: "Auto-approve all permission requests (session only)" },
|
|
@@ -3840,7 +4248,7 @@ var STATIC_COMMANDS = [
|
|
|
3840
4248
|
];
|
|
3841
4249
|
|
|
3842
4250
|
// src/adapters/telegram/permissions.ts
|
|
3843
|
-
import { InlineKeyboard as
|
|
4251
|
+
import { InlineKeyboard as InlineKeyboard8 } from "grammy";
|
|
3844
4252
|
import { nanoid as nanoid2 } from "nanoid";
|
|
3845
4253
|
var log14 = createChildLogger({ module: "telegram-permissions" });
|
|
3846
4254
|
var PermissionHandler = class {
|
|
@@ -3859,7 +4267,7 @@ var PermissionHandler = class {
|
|
|
3859
4267
|
requestId: request.id,
|
|
3860
4268
|
options: request.options.map((o) => ({ id: o.id, isAllow: o.isAllow }))
|
|
3861
4269
|
});
|
|
3862
|
-
const keyboard = new
|
|
4270
|
+
const keyboard = new InlineKeyboard8();
|
|
3863
4271
|
for (const option of request.options) {
|
|
3864
4272
|
const emoji = option.isAllow ? "\u2705" : "\u274C";
|
|
3865
4273
|
keyboard.text(`${emoji} ${option.label}`, `p:${callbackKey}:${option.id}`);
|
|
@@ -3945,9 +4353,29 @@ A session = one conversation with one AI agent working in one project folder.
|
|
|
3945
4353
|
Each session gets its own Telegram topic. Chat there to give instructions to the agent.
|
|
3946
4354
|
|
|
3947
4355
|
### Agents
|
|
3948
|
-
An agent is an AI coding tool (e.g., Claude Code
|
|
4356
|
+
An agent is an AI coding tool (e.g., Claude Code, Gemini, Cursor, Codex, etc.).
|
|
4357
|
+
OpenACP supports 28+ agents from the official ACP Registry (agentclientprotocol.com).
|
|
4358
|
+
You can install multiple agents and choose which one to use per session.
|
|
3949
4359
|
The default agent is used when you don't specify one.
|
|
3950
4360
|
|
|
4361
|
+
### Agent Management
|
|
4362
|
+
- Browse agents: \`/agents\` in Telegram or \`openacp agents\` in CLI
|
|
4363
|
+
- Install: tap the install button in /agents, or \`openacp agents install <name>\`
|
|
4364
|
+
- Uninstall: \`openacp agents uninstall <name>\`
|
|
4365
|
+
- Setup/login: \`openacp agents run <name> -- <args>\` (e.g., \`openacp agents run gemini -- auth login\`)
|
|
4366
|
+
- Details: \`openacp agents info <name>\` shows version, dependencies, and setup steps
|
|
4367
|
+
|
|
4368
|
+
Some agents need additional setup before they can be used:
|
|
4369
|
+
- Claude: requires \`claude login\`
|
|
4370
|
+
- Gemini: requires \`openacp agents run gemini -- auth login\`
|
|
4371
|
+
- Codex: requires setting \`OPENAI_API_KEY\` environment variable
|
|
4372
|
+
- GitHub Copilot: requires \`openacp agents run copilot -- auth login\`
|
|
4373
|
+
|
|
4374
|
+
Agents are installed in three ways depending on the agent:
|
|
4375
|
+
- **npx** \u2014 Node.js agents, downloaded automatically on first use
|
|
4376
|
+
- **uvx** \u2014 Python agents, downloaded automatically on first use
|
|
4377
|
+
- **binary** \u2014 Platform-specific binaries, downloaded to \`~/.openacp/agents/\`
|
|
4378
|
+
|
|
3951
4379
|
### Project Folder (Workspace)
|
|
3952
4380
|
The directory where the agent reads, writes, and runs code.
|
|
3953
4381
|
When creating a session, you choose which folder the agent works in.
|
|
@@ -4080,7 +4508,8 @@ Just chat naturally: "How do I create a session?", "What's the status?", "Someth
|
|
|
4080
4508
|
| \`/cancel\` | Session topic | Cancel current session |
|
|
4081
4509
|
| \`/status\` | Anywhere | Show status |
|
|
4082
4510
|
| \`/sessions\` | Anywhere | List all sessions |
|
|
4083
|
-
| \`/agents\` | Anywhere |
|
|
4511
|
+
| \`/agents\` | Anywhere | Browse & install agents from ACP Registry |
|
|
4512
|
+
| \`/install <name>\` | Anywhere | Install an agent |
|
|
4084
4513
|
| \`/enable_dangerous\` | Session topic | Auto-approve all permissions |
|
|
4085
4514
|
| \`/disable_dangerous\` | Session topic | Restore permission prompts |
|
|
4086
4515
|
| \`/handoff\` | Session topic | Transfer session to terminal |
|
|
@@ -4127,6 +4556,14 @@ Just chat naturally: "How do I create a session?", "What's the status?", "Someth
|
|
|
4127
4556
|
- \`openacp config\` \u2014 Interactive config editor
|
|
4128
4557
|
- \`openacp reset\` \u2014 Delete all data and start fresh
|
|
4129
4558
|
|
|
4559
|
+
### Agent Management (CLI)
|
|
4560
|
+
- \`openacp agents\` \u2014 List all agents (installed + available from ACP Registry)
|
|
4561
|
+
- \`openacp agents install <name>\` \u2014 Install an agent
|
|
4562
|
+
- \`openacp agents uninstall <name>\` \u2014 Remove an agent
|
|
4563
|
+
- \`openacp agents info <name>\` \u2014 Show details, dependencies, and setup guide
|
|
4564
|
+
- \`openacp agents run <name> [-- args]\` \u2014 Run agent CLI directly (for login, config, etc.)
|
|
4565
|
+
- \`openacp agents refresh\` \u2014 Force-refresh registry cache
|
|
4566
|
+
|
|
4130
4567
|
### Plugins
|
|
4131
4568
|
- \`openacp install <package>\` \u2014 Install adapter plugin (e.g., \`@openacp/adapter-discord\`)
|
|
4132
4569
|
- \`openacp uninstall <package>\` \u2014 Remove adapter plugin
|
|
@@ -4185,10 +4622,10 @@ Config file: \`~/.openacp/config.json\`
|
|
|
4185
4622
|
- **telegram.chatId** \u2014 Your Telegram supergroup ID
|
|
4186
4623
|
|
|
4187
4624
|
### Agents
|
|
4188
|
-
- **agents.<name>.command** \u2014 Agent executable path (e.g., \`claude\`, \`codex\`)
|
|
4189
|
-
- **agents.<name>.args** \u2014 Arguments to pass to the agent command
|
|
4190
|
-
- **agents.<name>.env** \u2014 Custom environment variables for the agent subprocess
|
|
4191
4625
|
- **defaultAgent** \u2014 Which agent to use by default
|
|
4626
|
+
- Agents are managed via \`/agents\` (Telegram) or \`openacp agents\` (CLI)
|
|
4627
|
+
- Installed agents are stored in \`~/.openacp/agents.json\`
|
|
4628
|
+
- Agent list is fetched from the ACP Registry CDN and cached locally (24h)
|
|
4192
4629
|
|
|
4193
4630
|
### Workspace
|
|
4194
4631
|
- **workspace.baseDir** \u2014 Base directory for project folders (default: \`~/openacp-workspace\`)
|
|
@@ -4240,9 +4677,10 @@ Override config with env vars:
|
|
|
4240
4677
|
- Check system health: Assistant can run health check
|
|
4241
4678
|
|
|
4242
4679
|
### Agent not found
|
|
4243
|
-
- Check available agents: \`/agents\`
|
|
4244
|
-
-
|
|
4245
|
-
-
|
|
4680
|
+
- Check available agents: \`/agents\` or \`openacp agents\`
|
|
4681
|
+
- Install missing agent: \`openacp agents install <name>\`
|
|
4682
|
+
- Some agents need login first: \`openacp agents info <name>\` to see setup steps
|
|
4683
|
+
- Run agent CLI for setup: \`openacp agents run <name> -- <args>\`
|
|
4246
4684
|
|
|
4247
4685
|
### Permission request not showing
|
|
4248
4686
|
- Check Notifications topic for the alert
|
|
@@ -4273,6 +4711,9 @@ Override config with env vars:
|
|
|
4273
4711
|
|
|
4274
4712
|
All data is stored in \`~/.openacp/\`:
|
|
4275
4713
|
- \`config.json\` \u2014 Configuration
|
|
4714
|
+
- \`agents.json\` \u2014 Installed agents (managed by AgentCatalog)
|
|
4715
|
+
- \`registry-cache.json\` \u2014 Cached ACP Registry data (refreshes every 24h)
|
|
4716
|
+
- \`agents/\` \u2014 Downloaded binary agents
|
|
4276
4717
|
- \`sessions/\` \u2014 Session records and state
|
|
4277
4718
|
- \`topics/\` \u2014 Topic-to-session mappings
|
|
4278
4719
|
- \`logs/\` \u2014 System and session logs
|
|
@@ -4304,11 +4745,16 @@ async function spawnAssistant(core, adapter, assistantTopicId) {
|
|
|
4304
4745
|
statusCounts.set(r.status, (statusCounts.get(r.status) ?? 0) + 1);
|
|
4305
4746
|
}
|
|
4306
4747
|
const topicSummary = Array.from(statusCounts.entries()).map(([status, count]) => ({ status, count }));
|
|
4748
|
+
const installedAgents = Object.keys(core.agentCatalog.getInstalledEntries());
|
|
4749
|
+
const availableItems = core.agentCatalog.getAvailable();
|
|
4750
|
+
const availableAgentCount = availableItems.filter((i) => !i.installed).length;
|
|
4307
4751
|
const ctx = {
|
|
4308
4752
|
config,
|
|
4309
4753
|
activeSessionCount: activeCount,
|
|
4310
4754
|
totalSessionCount: allRecords.length,
|
|
4311
|
-
topicSummary
|
|
4755
|
+
topicSummary,
|
|
4756
|
+
installedAgents,
|
|
4757
|
+
availableAgentCount
|
|
4312
4758
|
};
|
|
4313
4759
|
const systemPrompt = buildAssistantSystemPrompt(ctx);
|
|
4314
4760
|
const ready = session.enqueuePrompt(systemPrompt).then(() => {
|
|
@@ -4340,15 +4786,16 @@ Agents: ${agentList}`;
|
|
|
4340
4786
|
Agents: ${agentList}`;
|
|
4341
4787
|
}
|
|
4342
4788
|
function buildAssistantSystemPrompt(ctx) {
|
|
4343
|
-
const { config, activeSessionCount, totalSessionCount, topicSummary } = ctx;
|
|
4344
|
-
const agentNames = Object.keys(config.agents).join(", ");
|
|
4789
|
+
const { config, activeSessionCount, totalSessionCount, topicSummary, installedAgents, availableAgentCount } = ctx;
|
|
4790
|
+
const agentNames = installedAgents?.length ? installedAgents.join(", ") : Object.keys(config.agents).join(", ");
|
|
4345
4791
|
const topicBreakdown = topicSummary.map((s) => `${s.status}: ${s.count}`).join(", ") || "none";
|
|
4346
4792
|
return `You are the OpenACP Assistant \u2014 a helpful guide for managing AI coding sessions.
|
|
4347
4793
|
|
|
4348
4794
|
## Current State
|
|
4349
4795
|
- Active sessions: ${activeSessionCount} / ${totalSessionCount} total
|
|
4350
4796
|
- Topics by status: ${topicBreakdown}
|
|
4351
|
-
-
|
|
4797
|
+
- Installed agents: ${agentNames}
|
|
4798
|
+
- Available in ACP Registry: ${availableAgentCount ?? "28+"} more agents (use /agents to browse)
|
|
4352
4799
|
- Default agent: ${config.defaultAgent}
|
|
4353
4800
|
- Workspace base directory: ${config.workspace.baseDir}
|
|
4354
4801
|
|
|
@@ -4356,11 +4803,22 @@ function buildAssistantSystemPrompt(ctx) {
|
|
|
4356
4803
|
|
|
4357
4804
|
### Create Session
|
|
4358
4805
|
- The workspace is the project directory where the agent will work (read, write, execute code). It is NOT the base directory \u2014 it should be a specific project folder like \`~/code/my-project\` or \`${config.workspace.baseDir}/my-app\`.
|
|
4359
|
-
- Ask which agent to use (if multiple are
|
|
4806
|
+
- Ask which agent to use (if multiple are installed). Show installed: ${agentNames}
|
|
4360
4807
|
- Ask which project directory to use as workspace. Suggest \`${config.workspace.baseDir}\` as the base, but explain the user can provide any path.
|
|
4361
4808
|
- Confirm before creating: show agent name + full workspace path.
|
|
4362
4809
|
- Create via: \`openacp api new <agent> <workspace>\`
|
|
4363
4810
|
|
|
4811
|
+
### Browse & Install Agents
|
|
4812
|
+
- Guide users to /agents command to see all available agents (installed + from ACP Registry)
|
|
4813
|
+
- The /agents list is paginated with install buttons \u2014 users can tap to install directly
|
|
4814
|
+
- For CLI users: \`openacp agents install <name>\`
|
|
4815
|
+
- Some agents need login/setup after install \u2014 guide users to \`openacp agents info <name>\` for setup steps
|
|
4816
|
+
- To run agent CLI for login: \`openacp agents run <name> -- <args>\`
|
|
4817
|
+
- Common setup examples:
|
|
4818
|
+
- Gemini: \`openacp agents run gemini -- auth login\`
|
|
4819
|
+
- GitHub Copilot: \`openacp agents run copilot -- auth login\`
|
|
4820
|
+
- Codex: needs OPENAI_API_KEY environment variable
|
|
4821
|
+
|
|
4364
4822
|
### Check Status / List Sessions
|
|
4365
4823
|
- Run \`openacp api status\` for active sessions overview
|
|
4366
4824
|
- Run \`openacp api topics\` for full list with statuses
|
|
@@ -4413,12 +4871,18 @@ openacp api delete-topic <id> --force # Force delete active
|
|
|
4413
4871
|
openacp api cleanup # Cleanup finished topics
|
|
4414
4872
|
openacp api cleanup --status finished,error
|
|
4415
4873
|
|
|
4874
|
+
# Agent management (user-facing CLI commands)
|
|
4875
|
+
openacp agents # List installed + available agents
|
|
4876
|
+
openacp agents install <name> # Install agent from ACP Registry
|
|
4877
|
+
openacp agents uninstall <name> # Remove agent
|
|
4878
|
+
openacp agents info <name> # Show details & setup guide
|
|
4879
|
+
openacp agents run <name> -- <args> # Run agent CLI (for login, etc.)
|
|
4880
|
+
openacp agents refresh # Force-refresh registry
|
|
4881
|
+
|
|
4416
4882
|
# System
|
|
4417
4883
|
openacp api health # System health
|
|
4418
4884
|
openacp config # Edit config (interactive)
|
|
4419
4885
|
openacp config set <key> <value> # Update config value
|
|
4420
|
-
openacp api config # Show config (deprecated)
|
|
4421
|
-
openacp api config set <key> <value> # Update config (deprecated)
|
|
4422
4886
|
openacp api adapters # List adapters
|
|
4423
4887
|
openacp api tunnel # Tunnel status
|
|
4424
4888
|
openacp api notify "message" # Send notification
|
|
@@ -4801,7 +5265,7 @@ var TelegramSendQueue = class {
|
|
|
4801
5265
|
|
|
4802
5266
|
// src/adapters/telegram/action-detect.ts
|
|
4803
5267
|
import { nanoid as nanoid3 } from "nanoid";
|
|
4804
|
-
import { InlineKeyboard as
|
|
5268
|
+
import { InlineKeyboard as InlineKeyboard9 } from "grammy";
|
|
4805
5269
|
var CMD_NEW_RE = /\/new(?:\s+([^\s\u0080-\uFFFF]+)(?:\s+([^\s\u0080-\uFFFF]+))?)?/;
|
|
4806
5270
|
var CMD_CANCEL_RE = /\/cancel\b/;
|
|
4807
5271
|
var KW_NEW_RE = /(?:create|new)\s+session/i;
|
|
@@ -4848,7 +5312,7 @@ function removeAction(id) {
|
|
|
4848
5312
|
actionMap.delete(id);
|
|
4849
5313
|
}
|
|
4850
5314
|
function buildActionKeyboard(actionId, action) {
|
|
4851
|
-
const keyboard = new
|
|
5315
|
+
const keyboard = new InlineKeyboard9();
|
|
4852
5316
|
if (action.action === "new_session") {
|
|
4853
5317
|
keyboard.text("\u2705 Create session", `a:${actionId}`);
|
|
4854
5318
|
keyboard.text("\u274C Cancel", `a:dismiss:${actionId}`);
|
|
@@ -5577,7 +6041,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
5577
6041
|
});
|
|
5578
6042
|
return;
|
|
5579
6043
|
}
|
|
5580
|
-
const { getAgentCapabilities: getAgentCapabilities2 } = await import("./agent-registry-
|
|
6044
|
+
const { getAgentCapabilities: getAgentCapabilities2 } = await import("./agent-registry-KZANAFXQ.js");
|
|
5581
6045
|
const caps = getAgentCapabilities2(agentName);
|
|
5582
6046
|
if (!caps.supportsResume || !caps.resumeCommand) {
|
|
5583
6047
|
await ctx.reply("This agent does not support session transfer.", {
|
|
@@ -5953,4 +6417,4 @@ export {
|
|
|
5953
6417
|
TopicManager,
|
|
5954
6418
|
TelegramAdapter
|
|
5955
6419
|
};
|
|
5956
|
-
//# sourceMappingURL=chunk-
|
|
6420
|
+
//# sourceMappingURL=chunk-PHC67OP4.js.map
|