@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.
Files changed (74) hide show
  1. package/README.md +41 -3
  2. package/dist/agent-catalog-LAAVBVLY.js +10 -0
  3. package/dist/agent-dependencies-FCLRGMZM.js +23 -0
  4. package/dist/agent-registry-KZANAFXQ.js +8 -0
  5. package/dist/agent-store-ZBXGOFPH.js +8 -0
  6. package/dist/chunk-5HGXUCMX.js +83 -0
  7. package/dist/chunk-5HGXUCMX.js.map +1 -0
  8. package/dist/chunk-5MH66WUY.js +424 -0
  9. package/dist/chunk-5MH66WUY.js.map +1 -0
  10. package/dist/{chunk-FKOARMAE.js → chunk-776VAU3T.js} +3 -3
  11. package/dist/chunk-GUHCS6X7.js +282 -0
  12. package/dist/chunk-GUHCS6X7.js.map +1 -0
  13. package/dist/{chunk-3DIPXFZJ.js → chunk-IRGYTNLP.js} +2 -2
  14. package/dist/chunk-IURZ4QHG.js +91 -0
  15. package/dist/chunk-IURZ4QHG.js.map +1 -0
  16. package/dist/{chunk-WYZFGHHI.js → chunk-JRF4G4X7.js} +60 -24
  17. package/dist/chunk-JRF4G4X7.js.map +1 -0
  18. package/dist/chunk-NAMYZIS5.js +1 -0
  19. package/dist/{chunk-ZW444AQY.js → chunk-NDR5JCS7.js} +2 -2
  20. package/dist/{chunk-66RVSUAR.js → chunk-PHC67OP4.js} +567 -103
  21. package/dist/chunk-PHC67OP4.js.map +1 -0
  22. package/dist/{chunk-W7QQA6CW.js → chunk-QODDJ4PH.js} +83 -36
  23. package/dist/chunk-QODDJ4PH.js.map +1 -0
  24. package/dist/{chunk-YRJEZD7R.js → chunk-VBEWSWVL.js} +2 -2
  25. package/dist/{chunk-C33LTDZV.js → chunk-Z46LGZ7R.js} +21 -8
  26. package/dist/chunk-Z46LGZ7R.js.map +1 -0
  27. package/dist/cli.js +440 -64
  28. package/dist/cli.js.map +1 -1
  29. package/dist/{config-XURP6B3S.js → config-PCPIBPUA.js} +2 -2
  30. package/dist/config-editor-RGV6VKPZ.js +12 -0
  31. package/dist/{config-registry-OGX4YM2U.js → config-registry-SNKA2EH2.js} +2 -2
  32. package/dist/{daemon-GWJM2S4A.js → daemon-JZLFRUW6.js} +3 -3
  33. package/dist/daemon-JZLFRUW6.js.map +1 -0
  34. package/dist/data/registry-snapshot.json +876 -0
  35. package/dist/doctor-N2HKKUUQ.js +9 -0
  36. package/dist/doctor-N2HKKUUQ.js.map +1 -0
  37. package/dist/index.d.ts +138 -17
  38. package/dist/index.js +24 -15
  39. package/dist/integrate-X7LI6MUO.js +257 -0
  40. package/dist/integrate-X7LI6MUO.js.map +1 -0
  41. package/dist/{main-2QKD2EI2.js → main-DSQBCJHR.js} +18 -15
  42. package/dist/{main-2QKD2EI2.js.map → main-DSQBCJHR.js.map} +1 -1
  43. package/dist/{menu-CARRTW2F.js → menu-J5YVH665.js} +2 -4
  44. package/dist/menu-J5YVH665.js.map +1 -0
  45. package/dist/{setup-TTOL7XAN.js → setup-3A3XDGCM.js} +4 -3
  46. package/dist/setup-3A3XDGCM.js.map +1 -0
  47. package/dist/suggest-RST5VOHB.js +36 -0
  48. package/dist/suggest-RST5VOHB.js.map +1 -0
  49. package/package.json +11 -2
  50. package/dist/agent-registry-7HC6D4CH.js +0 -7
  51. package/dist/chunk-66RVSUAR.js.map +0 -1
  52. package/dist/chunk-BGKQHQB4.js +0 -276
  53. package/dist/chunk-BGKQHQB4.js.map +0 -1
  54. package/dist/chunk-C33LTDZV.js.map +0 -1
  55. package/dist/chunk-VA2M52CM.js +0 -15
  56. package/dist/chunk-VA2M52CM.js.map +0 -1
  57. package/dist/chunk-W7QQA6CW.js.map +0 -1
  58. package/dist/chunk-WYZFGHHI.js.map +0 -1
  59. package/dist/config-editor-AALY3URF.js +0 -11
  60. package/dist/doctor-X477CVZN.js +0 -9
  61. package/dist/integrate-WUPLRJD3.js +0 -145
  62. package/dist/integrate-WUPLRJD3.js.map +0 -1
  63. /package/dist/{agent-registry-7HC6D4CH.js.map → agent-catalog-LAAVBVLY.js.map} +0 -0
  64. /package/dist/{config-XURP6B3S.js.map → agent-dependencies-FCLRGMZM.js.map} +0 -0
  65. /package/dist/{config-editor-AALY3URF.js.map → agent-registry-KZANAFXQ.js.map} +0 -0
  66. /package/dist/{config-registry-OGX4YM2U.js.map → agent-store-ZBXGOFPH.js.map} +0 -0
  67. /package/dist/{chunk-FKOARMAE.js.map → chunk-776VAU3T.js.map} +0 -0
  68. /package/dist/{chunk-3DIPXFZJ.js.map → chunk-IRGYTNLP.js.map} +0 -0
  69. /package/dist/{daemon-GWJM2S4A.js.map → chunk-NAMYZIS5.js.map} +0 -0
  70. /package/dist/{chunk-ZW444AQY.js.map → chunk-NDR5JCS7.js.map} +0 -0
  71. /package/dist/{chunk-YRJEZD7R.js.map → chunk-VBEWSWVL.js.map} +0 -0
  72. /package/dist/{doctor-X477CVZN.js.map → config-PCPIBPUA.js.map} +0 -0
  73. /package/dist/{menu-CARRTW2F.js.map → config-editor-RGV6VKPZ.js.map} +0 -0
  74. /package/dist/{setup-TTOL7XAN.js.map → config-registry-SNKA2EH2.js.map} +0 -0
package/README.md CHANGED
@@ -9,8 +9,10 @@ One message, any channel, any agent.
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
10
10
  [![Node.js >= 20](https://img.shields.io/badge/Node.js-%3E%3D%2020-green.svg)](https://nodejs.org/)
11
11
  [![ACP Protocol](https://img.shields.io/badge/Protocol-ACP-purple.svg)](https://agentclientprotocol.org/)
12
+ [![npm](https://img.shields.io/npm/v/@openacp/cli.svg)](https://www.npmjs.com/package/@openacp/cli)
13
+ [![Twitter Follow](https://img.shields.io/twitter/follow/Open_ACP?style=social)](https://x.com/Open_ACP)
12
14
 
13
- [Getting Started](docs/guide/getting-started.md) | [Usage](docs/guide/usage.md) | [Configuration](docs/guide/configuration.md) | [Tunnel](docs/guide/tunnel.md) | [Plugins](docs/guide/plugins.md) | [Development](docs/guide/development.md)
15
+ [Getting Started](docs/guide/getting-started.md) | [Agents](docs/guide/agents.md) | [Usage](docs/guide/usage.md) | [Configuration](docs/guide/configuration.md) | [Tunnel](docs/guide/tunnel.md) | [Plugins](docs/guide/plugins.md) | [Development](docs/guide/development.md)
14
16
 
15
17
  </div>
16
18
 
@@ -47,9 +49,32 @@ AI Agent (Claude Code, Codex, ...) File/Diff Viewer
47
49
  </table>
48
50
  </div>
49
51
 
52
+ ## Supported Agents
53
+
54
+ OpenACP follows the [Agent Client Protocol (ACP)](https://agentclientprotocol.com/) standard — an open protocol for connecting AI coding agents to client applications. Agent definitions are loaded from the [official ACP Registry](https://agentclientprotocol.com/get-started/registry) via CDN ([`registry.json`](https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json)), so new agents are available as soon as they're registered.
55
+
56
+ | Agent | Distribution | Description |
57
+ |-------|-------------|-------------|
58
+ | [Claude Agent](https://github.com/anthropics/claude-code) | npx | Anthropic's Claude coding agent |
59
+ | [Gemini CLI](https://github.com/google-gemini/gemini-cli) | npx | Google's official CLI for Gemini |
60
+ | [Codex CLI](https://github.com/openai/codex) | npx | OpenAI's coding assistant |
61
+ | [GitHub Copilot](https://github.com/github/copilot-cli) | npx | GitHub's AI pair programmer |
62
+ | [Cursor](https://www.cursor.com/) | binary | Cursor's coding agent |
63
+ | [Cline](https://github.com/cline/cline) | npx | Autonomous coding agent with file editing, commands, and browser |
64
+ | [goose](https://github.com/block/goose) | binary | Open source AI agent for engineering tasks |
65
+ | [Amp](https://github.com/tao12345666333/amp-acp) | binary | The frontier coding agent |
66
+ | [Auggie CLI](https://www.augmentcode.com/) | npx | Augment Code's agent with industry-leading context engine |
67
+ | [Junie](https://www.jetbrains.com/) | binary | AI coding agent by JetBrains |
68
+ | [Kilo](https://github.com/kilocode/kilo) | npx | The open source coding agent |
69
+ | [Qwen Code](https://github.com/QwenLM/qwen-code) | npx | Alibaba's Qwen coding assistant |
70
+ | [crow-cli](https://github.com/crowdecode/crow-cli) | uvx | Minimal ACP native coding agent |
71
+ | ...and more | | [See full registry →](https://agentclientprotocol.com/get-started/registry) |
72
+
73
+ > **28+ agents supported** — any agent registered in the ACP Registry works out of the box. Install with `openacp agents install <name>` or browse from Telegram with `/agents`.
74
+
50
75
  ## Features
51
76
 
52
- - **Multi-agent** — Claude Code, Codex, or any ACP-compatible agent
77
+ - **Multi-agent** — Claude Code, Codex, Gemini, Cursor, and [28+ ACP-compatible agents](#supported-agents)
53
78
  - **Telegram** — Forum topics, real-time streaming, permission buttons, skill commands
54
79
  - **Tunnel & file viewer** — Public file/diff viewer via Cloudflare, ngrok, bore, or Tailscale
55
80
  - **Session persistence** — Resume sessions across restarts
@@ -102,6 +127,14 @@ openacp logs
102
127
  ### Other CLI commands
103
128
 
104
129
  ```bash
130
+ # Agent management
131
+ openacp agents # List all agents (installed + available)
132
+ openacp agents install <name> # Install an agent from the ACP Registry
133
+ openacp agents uninstall <name> # Remove an installed agent
134
+ openacp agents info <name> # Show agent details & dependencies
135
+ openacp agents refresh # Force-refresh the registry
136
+
137
+ # System
105
138
  openacp config # Show current config
106
139
  openacp reset # Re-run the setup wizard
107
140
  openacp update # Update to latest version
@@ -120,7 +153,8 @@ Once OpenACP is running, control it from Telegram:
120
153
  | `/newchat` | New session, same agent & workspace |
121
154
  | `/cancel` | Cancel current session |
122
155
  | `/status` | Show session or system status |
123
- | `/agents` | List available agents |
156
+ | `/agents` | Browse & install agents from ACP Registry |
157
+ | `/install <name>` | Install an agent directly |
124
158
 
125
159
  Each session gets its own forum topic. The agent streams responses in real time, shows tool calls, and asks for permission when needed.
126
160
 
@@ -165,6 +199,10 @@ Sessions are not locked after transfer — you can continue from either side.
165
199
 
166
200
  See [development guide](docs/guide/development.md).
167
201
 
202
+ ## Follow Us
203
+
204
+ [![Twitter Follow](https://img.shields.io/twitter/follow/Open_ACP?style=social)](https://x.com/Open_ACP)
205
+
168
206
  ## License
169
207
 
170
208
  [MIT](LICENSE)
@@ -0,0 +1,10 @@
1
+ import {
2
+ AgentCatalog
3
+ } from "./chunk-5MH66WUY.js";
4
+ import "./chunk-5HGXUCMX.js";
5
+ import "./chunk-GUHCS6X7.js";
6
+ import "./chunk-ESOPMQAY.js";
7
+ export {
8
+ AgentCatalog
9
+ };
10
+ //# sourceMappingURL=agent-catalog-LAAVBVLY.js.map
@@ -0,0 +1,23 @@
1
+ import {
2
+ REGISTRY_AGENT_ALIASES,
3
+ checkDependencies,
4
+ checkRuntimeAvailable,
5
+ commandExists,
6
+ getAgentAlias,
7
+ getAgentCapabilities,
8
+ getAgentDependencies,
9
+ getAgentSetup,
10
+ listAgentsWithIntegration
11
+ } from "./chunk-GUHCS6X7.js";
12
+ export {
13
+ REGISTRY_AGENT_ALIASES,
14
+ checkDependencies,
15
+ checkRuntimeAvailable,
16
+ commandExists,
17
+ getAgentAlias,
18
+ getAgentCapabilities,
19
+ getAgentDependencies,
20
+ getAgentSetup,
21
+ listAgentsWithIntegration
22
+ };
23
+ //# sourceMappingURL=agent-dependencies-FCLRGMZM.js.map
@@ -0,0 +1,8 @@
1
+ import "./chunk-NAMYZIS5.js";
2
+ import {
3
+ getAgentCapabilities
4
+ } from "./chunk-GUHCS6X7.js";
5
+ export {
6
+ getAgentCapabilities
7
+ };
8
+ //# sourceMappingURL=agent-registry-KZANAFXQ.js.map
@@ -0,0 +1,8 @@
1
+ import {
2
+ AgentStore
3
+ } from "./chunk-5HGXUCMX.js";
4
+ import "./chunk-ESOPMQAY.js";
5
+ export {
6
+ AgentStore
7
+ };
8
+ //# sourceMappingURL=agent-store-ZBXGOFPH.js.map
@@ -0,0 +1,83 @@
1
+ import {
2
+ createChildLogger
3
+ } from "./chunk-ESOPMQAY.js";
4
+
5
+ // src/core/agent-store.ts
6
+ import * as fs from "fs";
7
+ import * as path from "path";
8
+ import * as os from "os";
9
+ import { z } from "zod";
10
+ var log = createChildLogger({ module: "agent-store" });
11
+ var InstalledAgentSchema = z.object({
12
+ registryId: z.string().nullable(),
13
+ name: z.string(),
14
+ version: z.string(),
15
+ distribution: z.enum(["npx", "uvx", "binary", "custom"]),
16
+ command: z.string(),
17
+ args: z.array(z.string()).default([]),
18
+ env: z.record(z.string(), z.string()).default({}),
19
+ workingDirectory: z.string().optional(),
20
+ installedAt: z.string(),
21
+ binaryPath: z.string().nullable().default(null)
22
+ });
23
+ var AgentStoreSchema = z.object({
24
+ version: z.number().default(1),
25
+ installed: z.record(z.string(), InstalledAgentSchema).default({})
26
+ });
27
+ var AgentStore = class {
28
+ data = { version: 1, installed: {} };
29
+ filePath;
30
+ constructor(filePath) {
31
+ this.filePath = filePath ?? path.join(os.homedir(), ".openacp", "agents.json");
32
+ }
33
+ load() {
34
+ fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
35
+ if (!fs.existsSync(this.filePath)) {
36
+ this.data = { version: 1, installed: {} };
37
+ return;
38
+ }
39
+ try {
40
+ const raw = JSON.parse(fs.readFileSync(this.filePath, "utf-8"));
41
+ const result = AgentStoreSchema.safeParse(raw);
42
+ if (result.success) {
43
+ this.data = result.data;
44
+ } else {
45
+ log.warn({ errors: result.error.issues }, "Invalid agents.json, starting fresh");
46
+ this.data = { version: 1, installed: {} };
47
+ }
48
+ } catch (err) {
49
+ log.warn({ err }, "Failed to read agents.json, starting fresh");
50
+ this.data = { version: 1, installed: {} };
51
+ }
52
+ }
53
+ exists() {
54
+ return fs.existsSync(this.filePath);
55
+ }
56
+ getInstalled() {
57
+ return this.data.installed;
58
+ }
59
+ getAgent(key) {
60
+ return this.data.installed[key];
61
+ }
62
+ addAgent(key, agent) {
63
+ this.data.installed[key] = agent;
64
+ this.save();
65
+ }
66
+ removeAgent(key) {
67
+ delete this.data.installed[key];
68
+ this.save();
69
+ }
70
+ hasAgent(key) {
71
+ return key in this.data.installed;
72
+ }
73
+ save() {
74
+ const tmpPath = this.filePath + ".tmp";
75
+ fs.writeFileSync(tmpPath, JSON.stringify(this.data, null, 2));
76
+ fs.renameSync(tmpPath, this.filePath);
77
+ }
78
+ };
79
+
80
+ export {
81
+ AgentStore
82
+ };
83
+ //# sourceMappingURL=chunk-5HGXUCMX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/agent-store.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { z } from \"zod\";\nimport type { InstalledAgent } from \"./types.js\";\nimport { createChildLogger } from \"./log.js\";\n\nconst log = createChildLogger({ module: \"agent-store\" });\n\nconst InstalledAgentSchema = z.object({\n registryId: z.string().nullable(),\n name: z.string(),\n version: z.string(),\n distribution: z.enum([\"npx\", \"uvx\", \"binary\", \"custom\"]),\n command: z.string(),\n args: z.array(z.string()).default([]),\n env: z.record(z.string(), z.string()).default({}),\n workingDirectory: z.string().optional(),\n installedAt: z.string(),\n binaryPath: z.string().nullable().default(null),\n});\n\nconst AgentStoreSchema = z.object({\n version: z.number().default(1),\n installed: z.record(z.string(), InstalledAgentSchema).default({}),\n});\n\ntype AgentStoreData = z.infer<typeof AgentStoreSchema>;\n\nexport class AgentStore {\n private data: AgentStoreData = { version: 1, installed: {} };\n private filePath: string;\n\n constructor(filePath?: string) {\n this.filePath = filePath ?? path.join(os.homedir(), \".openacp\", \"agents.json\");\n }\n\n load(): void {\n fs.mkdirSync(path.dirname(this.filePath), { recursive: true });\n\n if (!fs.existsSync(this.filePath)) {\n this.data = { version: 1, installed: {} };\n return;\n }\n\n try {\n const raw = JSON.parse(fs.readFileSync(this.filePath, \"utf-8\") as string);\n const result = AgentStoreSchema.safeParse(raw);\n if (result.success) {\n this.data = result.data;\n } else {\n log.warn({ errors: result.error.issues }, \"Invalid agents.json, starting fresh\");\n this.data = { version: 1, installed: {} };\n }\n } catch (err) {\n log.warn({ err }, \"Failed to read agents.json, starting fresh\");\n this.data = { version: 1, installed: {} };\n }\n }\n\n exists(): boolean {\n return fs.existsSync(this.filePath);\n }\n\n getInstalled(): Record<string, InstalledAgent> {\n return this.data.installed;\n }\n\n getAgent(key: string): InstalledAgent | undefined {\n return this.data.installed[key];\n }\n\n addAgent(key: string, agent: InstalledAgent): void {\n this.data.installed[key] = agent;\n this.save();\n }\n\n removeAgent(key: string): void {\n delete this.data.installed[key];\n this.save();\n }\n\n hasAgent(key: string): boolean {\n return key in this.data.installed;\n }\n\n private save(): void {\n const tmpPath = this.filePath + \".tmp\";\n fs.writeFileSync(tmpPath, JSON.stringify(this.data, null, 2));\n fs.renameSync(tmpPath, this.filePath);\n }\n}\n"],"mappings":";;;;;AAAA,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,YAAY,QAAQ;AACpB,SAAS,SAAS;AAIlB,IAAM,MAAM,kBAAkB,EAAE,QAAQ,cAAc,CAAC;AAEvD,IAAM,uBAAuB,EAAE,OAAO;AAAA,EACpC,YAAY,EAAE,OAAO,EAAE,SAAS;AAAA,EAChC,MAAM,EAAE,OAAO;AAAA,EACf,SAAS,EAAE,OAAO;AAAA,EAClB,cAAc,EAAE,KAAK,CAAC,OAAO,OAAO,UAAU,QAAQ,CAAC;AAAA,EACvD,SAAS,EAAE,OAAO;AAAA,EAClB,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AAAA,EACpC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AAAA,EAChD,kBAAkB,EAAE,OAAO,EAAE,SAAS;AAAA,EACtC,aAAa,EAAE,OAAO;AAAA,EACtB,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,IAAI;AAChD,CAAC;AAED,IAAM,mBAAmB,EAAE,OAAO;AAAA,EAChC,SAAS,EAAE,OAAO,EAAE,QAAQ,CAAC;AAAA,EAC7B,WAAW,EAAE,OAAO,EAAE,OAAO,GAAG,oBAAoB,EAAE,QAAQ,CAAC,CAAC;AAClE,CAAC;AAIM,IAAM,aAAN,MAAiB;AAAA,EACd,OAAuB,EAAE,SAAS,GAAG,WAAW,CAAC,EAAE;AAAA,EACnD;AAAA,EAER,YAAY,UAAmB;AAC7B,SAAK,WAAW,YAAiB,UAAQ,WAAQ,GAAG,YAAY,aAAa;AAAA,EAC/E;AAAA,EAEA,OAAa;AACX,IAAG,aAAe,aAAQ,KAAK,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAE7D,QAAI,CAAI,cAAW,KAAK,QAAQ,GAAG;AACjC,WAAK,OAAO,EAAE,SAAS,GAAG,WAAW,CAAC,EAAE;AACxC;AAAA,IACF;AAEA,QAAI;AACF,YAAM,MAAM,KAAK,MAAS,gBAAa,KAAK,UAAU,OAAO,CAAW;AACxE,YAAM,SAAS,iBAAiB,UAAU,GAAG;AAC7C,UAAI,OAAO,SAAS;AAClB,aAAK,OAAO,OAAO;AAAA,MACrB,OAAO;AACL,YAAI,KAAK,EAAE,QAAQ,OAAO,MAAM,OAAO,GAAG,qCAAqC;AAC/E,aAAK,OAAO,EAAE,SAAS,GAAG,WAAW,CAAC,EAAE;AAAA,MAC1C;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,KAAK,EAAE,IAAI,GAAG,4CAA4C;AAC9D,WAAK,OAAO,EAAE,SAAS,GAAG,WAAW,CAAC,EAAE;AAAA,IAC1C;AAAA,EACF;AAAA,EAEA,SAAkB;AAChB,WAAU,cAAW,KAAK,QAAQ;AAAA,EACpC;AAAA,EAEA,eAA+C;AAC7C,WAAO,KAAK,KAAK;AAAA,EACnB;AAAA,EAEA,SAAS,KAAyC;AAChD,WAAO,KAAK,KAAK,UAAU,GAAG;AAAA,EAChC;AAAA,EAEA,SAAS,KAAa,OAA6B;AACjD,SAAK,KAAK,UAAU,GAAG,IAAI;AAC3B,SAAK,KAAK;AAAA,EACZ;AAAA,EAEA,YAAY,KAAmB;AAC7B,WAAO,KAAK,KAAK,UAAU,GAAG;AAC9B,SAAK,KAAK;AAAA,EACZ;AAAA,EAEA,SAAS,KAAsB;AAC7B,WAAO,OAAO,KAAK,KAAK;AAAA,EAC1B;AAAA,EAEQ,OAAa;AACnB,UAAM,UAAU,KAAK,WAAW;AAChC,IAAG,iBAAc,SAAS,KAAK,UAAU,KAAK,MAAM,MAAM,CAAC,CAAC;AAC5D,IAAG,cAAW,SAAS,KAAK,QAAQ;AAAA,EACtC;AACF;","names":[]}
@@ -0,0 +1,424 @@
1
+ import {
2
+ AgentStore
3
+ } from "./chunk-5HGXUCMX.js";
4
+ import {
5
+ checkDependencies,
6
+ checkRuntimeAvailable,
7
+ getAgentAlias,
8
+ getAgentSetup
9
+ } from "./chunk-GUHCS6X7.js";
10
+ import {
11
+ createChildLogger
12
+ } from "./chunk-ESOPMQAY.js";
13
+
14
+ // src/core/agent-catalog.ts
15
+ import * as fs2 from "fs";
16
+ import * as path2 from "path";
17
+ import * as os2 from "os";
18
+
19
+ // src/core/agent-installer.ts
20
+ import * as fs from "fs";
21
+ import * as path from "path";
22
+ import * as os from "os";
23
+ var log = createChildLogger({ module: "agent-installer" });
24
+ var AGENTS_DIR = path.join(os.homedir(), ".openacp", "agents");
25
+ var ARCH_MAP = {
26
+ arm64: "aarch64",
27
+ x64: "x86_64"
28
+ };
29
+ var PLATFORM_MAP = {
30
+ darwin: "darwin",
31
+ linux: "linux",
32
+ win32: "windows"
33
+ };
34
+ function getPlatformKey() {
35
+ const platform = PLATFORM_MAP[process.platform] ?? process.platform;
36
+ const arch = ARCH_MAP[process.arch] ?? process.arch;
37
+ return `${platform}-${arch}`;
38
+ }
39
+ function resolveDistribution(agent) {
40
+ const dist = agent.distribution;
41
+ if (dist.npx) {
42
+ return { type: "npx", package: dist.npx.package, args: dist.npx.args ?? [], env: dist.npx.env };
43
+ }
44
+ if (dist.uvx) {
45
+ return { type: "uvx", package: dist.uvx.package, args: dist.uvx.args ?? [], env: dist.uvx.env };
46
+ }
47
+ if (dist.binary) {
48
+ const platformKey = getPlatformKey();
49
+ const target = dist.binary[platformKey];
50
+ if (!target) return null;
51
+ return { type: "binary", archive: target.archive, cmd: target.cmd, args: target.args ?? [], env: target.env };
52
+ }
53
+ return null;
54
+ }
55
+ function buildInstalledAgent(registryId, name, version, dist, binaryPath) {
56
+ if (dist.type === "npx") {
57
+ return {
58
+ registryId,
59
+ name,
60
+ version,
61
+ distribution: "npx",
62
+ command: "npx",
63
+ args: [dist.package, ...dist.args],
64
+ env: dist.env ?? {},
65
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
66
+ binaryPath: null
67
+ };
68
+ }
69
+ if (dist.type === "uvx") {
70
+ return {
71
+ registryId,
72
+ name,
73
+ version,
74
+ distribution: "uvx",
75
+ command: "uvx",
76
+ args: [dist.package, ...dist.args],
77
+ env: dist.env ?? {},
78
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
79
+ binaryPath: null
80
+ };
81
+ }
82
+ const absCmd = path.resolve(binaryPath, dist.cmd);
83
+ return {
84
+ registryId,
85
+ name,
86
+ version,
87
+ distribution: "binary",
88
+ command: absCmd,
89
+ args: dist.args,
90
+ env: dist.env ?? {},
91
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
92
+ binaryPath
93
+ };
94
+ }
95
+ async function installAgent(agent, store, progress) {
96
+ const agentKey = getAgentAlias(agent.id);
97
+ await progress?.onStart(agent.id, agent.name);
98
+ await progress?.onStep("Checking requirements...");
99
+ const depResult = checkDependencies(agent.id);
100
+ if (!depResult.available) {
101
+ const hints = depResult.missing.map((m) => ` ${m.label}: ${m.installHint}`).join("\n");
102
+ const msg = `${agent.name} needs some tools installed first:
103
+ ${hints}`;
104
+ await progress?.onError(msg);
105
+ return { ok: false, agentKey, error: msg };
106
+ }
107
+ const dist = resolveDistribution(agent);
108
+ if (!dist) {
109
+ const platformKey = getPlatformKey();
110
+ const msg = `${agent.name} is not available for your system (${platformKey}). Check their website for other install options.`;
111
+ await progress?.onError(msg);
112
+ return { ok: false, agentKey, error: msg };
113
+ }
114
+ if (dist.type === "uvx" && !checkRuntimeAvailable("uvx")) {
115
+ const msg = `${agent.name} requires Python's uvx tool.
116
+ Install it with: pip install uv`;
117
+ await progress?.onError(msg, "pip install uv");
118
+ return { ok: false, agentKey, error: msg, hint: "pip install uv" };
119
+ }
120
+ let binaryPath;
121
+ if (dist.type === "binary") {
122
+ try {
123
+ binaryPath = await downloadAndExtract(agent.id, dist.archive, progress);
124
+ } catch (err) {
125
+ const msg = `Failed to download ${agent.name}. Please try again or install manually.`;
126
+ await progress?.onError(msg);
127
+ return { ok: false, agentKey, error: msg };
128
+ }
129
+ } else {
130
+ await progress?.onStep("Setting up... (will download on first use)");
131
+ }
132
+ const installed = buildInstalledAgent(agent.id, agent.name, agent.version, dist, binaryPath);
133
+ store.addAgent(agentKey, installed);
134
+ const setup = getAgentSetup(agent.id);
135
+ await progress?.onSuccess(agent.name);
136
+ return { ok: true, agentKey, setupSteps: setup?.setupSteps };
137
+ }
138
+ async function downloadAndExtract(agentId, archiveUrl, progress) {
139
+ const destDir = path.join(AGENTS_DIR, agentId);
140
+ fs.mkdirSync(destDir, { recursive: true });
141
+ await progress?.onStep("Downloading...");
142
+ log.info({ agentId, url: archiveUrl }, "Downloading agent binary");
143
+ const response = await fetch(archiveUrl);
144
+ if (!response.ok) {
145
+ throw new Error(`Download failed: ${response.status} ${response.statusText}`);
146
+ }
147
+ const contentLength = Number(response.headers.get("content-length") || 0);
148
+ const buffer = await readResponseWithProgress(response, contentLength, progress);
149
+ await progress?.onStep("Extracting...");
150
+ if (archiveUrl.endsWith(".zip")) {
151
+ await extractZip(buffer, destDir);
152
+ } else {
153
+ await extractTarGz(buffer, destDir);
154
+ }
155
+ await progress?.onStep("Ready!");
156
+ return destDir;
157
+ }
158
+ async function readResponseWithProgress(response, contentLength, progress) {
159
+ if (!response.body || contentLength === 0) {
160
+ const arrayBuffer = await response.arrayBuffer();
161
+ return Buffer.from(arrayBuffer);
162
+ }
163
+ const reader = response.body.getReader();
164
+ const chunks = [];
165
+ let received = 0;
166
+ while (true) {
167
+ const { done, value } = await reader.read();
168
+ if (done) break;
169
+ chunks.push(value);
170
+ received += value.length;
171
+ if (contentLength > 0) {
172
+ await progress?.onDownloadProgress(Math.round(received / contentLength * 100));
173
+ }
174
+ }
175
+ return Buffer.concat(chunks);
176
+ }
177
+ function validateExtractedPaths(destDir) {
178
+ const realDest = fs.realpathSync(destDir);
179
+ const entries = fs.readdirSync(destDir, { recursive: true, withFileTypes: true });
180
+ for (const entry of entries) {
181
+ const parentPath = entry.parentPath ?? entry.path;
182
+ const fullPath = path.join(parentPath, entry.name);
183
+ let realPath;
184
+ try {
185
+ realPath = fs.realpathSync(fullPath);
186
+ } catch {
187
+ const linkTarget = fs.readlinkSync(fullPath);
188
+ realPath = path.resolve(path.dirname(fullPath), linkTarget);
189
+ }
190
+ if (!realPath.startsWith(realDest + path.sep) && realPath !== realDest) {
191
+ fs.rmSync(destDir, { recursive: true, force: true });
192
+ throw new Error(`Archive contains unsafe path: ${entry.name}`);
193
+ }
194
+ }
195
+ }
196
+ async function extractTarGz(buffer, destDir) {
197
+ const { execFileSync } = await import("child_process");
198
+ const tmpFile = path.join(destDir, "_archive.tar.gz");
199
+ fs.writeFileSync(tmpFile, buffer);
200
+ try {
201
+ execFileSync("tar", ["xzf", tmpFile, "-C", destDir], { stdio: "pipe" });
202
+ } finally {
203
+ fs.unlinkSync(tmpFile);
204
+ }
205
+ validateExtractedPaths(destDir);
206
+ }
207
+ async function extractZip(buffer, destDir) {
208
+ const { execFileSync } = await import("child_process");
209
+ const tmpFile = path.join(destDir, "_archive.zip");
210
+ fs.writeFileSync(tmpFile, buffer);
211
+ try {
212
+ execFileSync("unzip", ["-o", tmpFile, "-d", destDir], { stdio: "pipe" });
213
+ } finally {
214
+ fs.unlinkSync(tmpFile);
215
+ }
216
+ validateExtractedPaths(destDir);
217
+ }
218
+ async function uninstallAgent(agentKey, store) {
219
+ const agent = store.getAgent(agentKey);
220
+ if (!agent) return;
221
+ if (agent.binaryPath && fs.existsSync(agent.binaryPath)) {
222
+ fs.rmSync(agent.binaryPath, { recursive: true, force: true });
223
+ log.info({ agentKey, binaryPath: agent.binaryPath }, "Deleted agent binary");
224
+ }
225
+ store.removeAgent(agentKey);
226
+ }
227
+
228
+ // src/core/agent-catalog.ts
229
+ var log2 = createChildLogger({ module: "agent-catalog" });
230
+ var REGISTRY_URL = "https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json";
231
+ var CACHE_PATH = path2.join(os2.homedir(), ".openacp", "registry-cache.json");
232
+ var DEFAULT_TTL_HOURS = 24;
233
+ var AgentCatalog = class {
234
+ store;
235
+ registryAgents = [];
236
+ constructor(store) {
237
+ this.store = store ?? new AgentStore();
238
+ }
239
+ load() {
240
+ this.store.load();
241
+ this.loadRegistryFromCacheOrSnapshot();
242
+ }
243
+ // --- Registry ---
244
+ async fetchRegistry() {
245
+ try {
246
+ log2.info("Fetching agent registry from CDN...");
247
+ const response = await fetch(REGISTRY_URL);
248
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
249
+ const data = await response.json();
250
+ this.registryAgents = data.agents ?? [];
251
+ const cache = {
252
+ fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
253
+ ttlHours: DEFAULT_TTL_HOURS,
254
+ data
255
+ };
256
+ fs2.mkdirSync(path2.dirname(CACHE_PATH), { recursive: true });
257
+ fs2.writeFileSync(CACHE_PATH, JSON.stringify(cache, null, 2));
258
+ log2.info({ count: this.registryAgents.length }, "Registry updated");
259
+ } catch (err) {
260
+ log2.warn({ err }, "Failed to fetch registry, using cached data");
261
+ }
262
+ }
263
+ async refreshRegistryIfStale() {
264
+ if (this.isCacheStale()) {
265
+ await this.fetchRegistry();
266
+ }
267
+ }
268
+ getRegistryAgents() {
269
+ return this.registryAgents;
270
+ }
271
+ getRegistryAgent(registryId) {
272
+ return this.registryAgents.find((a) => a.id === registryId);
273
+ }
274
+ findRegistryAgent(keyOrId) {
275
+ const byId = this.registryAgents.find((a) => a.id === keyOrId);
276
+ if (byId) return byId;
277
+ return this.registryAgents.find((a) => getAgentAlias(a.id) === keyOrId);
278
+ }
279
+ // --- Installed ---
280
+ getInstalled() {
281
+ return Object.values(this.store.getInstalled());
282
+ }
283
+ getInstalledEntries() {
284
+ return this.store.getInstalled();
285
+ }
286
+ getInstalledAgent(key) {
287
+ return this.store.getAgent(key);
288
+ }
289
+ // --- Discovery ---
290
+ getAvailable() {
291
+ const installed = this.store.getInstalled();
292
+ const items = [];
293
+ const seenKeys = /* @__PURE__ */ new Set();
294
+ for (const [key, agent] of Object.entries(installed)) {
295
+ seenKeys.add(key);
296
+ const availability = agent.registryId ? checkDependencies(agent.registryId) : { available: true };
297
+ const registryEntry = agent.registryId ? this.registryAgents.find((a) => a.id === agent.registryId) : void 0;
298
+ items.push({
299
+ key,
300
+ registryId: agent.registryId ?? key,
301
+ name: agent.name,
302
+ version: agent.version,
303
+ description: registryEntry?.description,
304
+ distribution: agent.distribution,
305
+ installed: true,
306
+ available: availability.available,
307
+ missingDeps: availability.missing?.map((m) => m.label)
308
+ });
309
+ }
310
+ for (const agent of this.registryAgents) {
311
+ const alias = getAgentAlias(agent.id);
312
+ if (seenKeys.has(alias)) continue;
313
+ seenKeys.add(alias);
314
+ const dist = resolveDistribution(agent);
315
+ const availability = checkDependencies(agent.id);
316
+ items.push({
317
+ key: alias,
318
+ registryId: agent.id,
319
+ name: agent.name,
320
+ version: agent.version,
321
+ description: agent.description,
322
+ distribution: dist?.type ?? "binary",
323
+ installed: false,
324
+ available: dist !== null && availability.available,
325
+ missingDeps: availability.missing?.map((m) => m.label)
326
+ });
327
+ }
328
+ return items;
329
+ }
330
+ checkAvailability(keyOrId) {
331
+ const agent = this.findRegistryAgent(keyOrId);
332
+ if (!agent) return { available: false, reason: "Not found in the agent registry." };
333
+ const dist = resolveDistribution(agent);
334
+ if (!dist) {
335
+ return { available: false, reason: `Not available for your system. Check ${agent.website ?? agent.repository ?? "their website"} for other options.` };
336
+ }
337
+ return checkDependencies(agent.id);
338
+ }
339
+ // --- Install/Uninstall ---
340
+ async install(keyOrId, progress, force) {
341
+ const agent = this.findRegistryAgent(keyOrId);
342
+ if (!agent) {
343
+ const msg = `"${keyOrId}" was not found in the agent registry. Run "openacp agents" to see what's available.`;
344
+ await progress?.onError(msg);
345
+ return { ok: false, agentKey: keyOrId, error: msg };
346
+ }
347
+ const agentKey = getAgentAlias(agent.id);
348
+ if (this.store.hasAgent(agentKey) && !force) {
349
+ const existing = this.store.getAgent(agentKey);
350
+ const msg = `${agent.name} is already installed (v${existing.version}). Use --force to reinstall.`;
351
+ await progress?.onError(msg);
352
+ return { ok: false, agentKey, error: msg };
353
+ }
354
+ return installAgent(agent, this.store, progress);
355
+ }
356
+ async uninstall(key) {
357
+ if (!this.store.hasAgent(key)) {
358
+ return { ok: false, error: `"${key}" is not installed.` };
359
+ }
360
+ await uninstallAgent(key, this.store);
361
+ return { ok: true };
362
+ }
363
+ // --- Resolution (for AgentManager) ---
364
+ resolve(key) {
365
+ const agent = this.store.getAgent(key);
366
+ if (!agent) return void 0;
367
+ return {
368
+ name: key,
369
+ command: agent.command,
370
+ args: agent.args,
371
+ workingDirectory: agent.workingDirectory,
372
+ env: agent.env
373
+ };
374
+ }
375
+ // --- Internal ---
376
+ isCacheStale() {
377
+ if (!fs2.existsSync(CACHE_PATH)) return true;
378
+ try {
379
+ const raw = JSON.parse(fs2.readFileSync(CACHE_PATH, "utf-8"));
380
+ const fetchedAt = new Date(raw.fetchedAt).getTime();
381
+ const ttlMs = (raw.ttlHours ?? DEFAULT_TTL_HOURS) * 60 * 60 * 1e3;
382
+ return Date.now() - fetchedAt > ttlMs;
383
+ } catch {
384
+ return true;
385
+ }
386
+ }
387
+ loadRegistryFromCacheOrSnapshot() {
388
+ if (fs2.existsSync(CACHE_PATH)) {
389
+ try {
390
+ const raw = JSON.parse(fs2.readFileSync(CACHE_PATH, "utf-8"));
391
+ if (raw.data?.agents) {
392
+ this.registryAgents = raw.data.agents;
393
+ log2.debug({ count: this.registryAgents.length }, "Loaded registry from cache");
394
+ return;
395
+ }
396
+ } catch {
397
+ log2.warn("Failed to load registry cache");
398
+ }
399
+ }
400
+ try {
401
+ const candidates = [
402
+ path2.join(import.meta.dirname, "data", "registry-snapshot.json"),
403
+ path2.join(import.meta.dirname, "..", "data", "registry-snapshot.json"),
404
+ path2.join(import.meta.dirname, "..", "..", "data", "registry-snapshot.json")
405
+ ];
406
+ for (const candidate of candidates) {
407
+ if (fs2.existsSync(candidate)) {
408
+ const raw = JSON.parse(fs2.readFileSync(candidate, "utf-8"));
409
+ this.registryAgents = raw.agents ?? [];
410
+ log2.debug({ count: this.registryAgents.length }, "Loaded registry from bundled snapshot");
411
+ return;
412
+ }
413
+ }
414
+ log2.warn("No registry data available (no cache, no snapshot)");
415
+ } catch {
416
+ log2.warn("Failed to load bundled registry snapshot");
417
+ }
418
+ }
419
+ };
420
+
421
+ export {
422
+ AgentCatalog
423
+ };
424
+ //# sourceMappingURL=chunk-5MH66WUY.js.map