@openacp/cli 0.5.3 → 0.6.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 (100) hide show
  1. package/README.md +51 -16
  2. package/dist/action-detect-6M5GCGAU.js +15 -0
  3. package/dist/admin-IKPS5PFC.js +16 -0
  4. package/dist/agents-55NX3DHM.js +14 -0
  5. package/dist/{api-client-UN7BXQOQ.js → api-client-BH2JFHQW.js} +4 -2
  6. package/dist/{autostart-K73RQZVV.js → autostart-A7JRU4WJ.js} +6 -2
  7. package/dist/{chunk-ECBD5I5R.js → chunk-2KJC3ILH.js} +123 -16
  8. package/dist/chunk-2KJC3ILH.js.map +1 -0
  9. package/dist/{chunk-2Z2XPUD5.js → chunk-4LFDEW22.js} +53 -5
  10. package/dist/chunk-4LFDEW22.js.map +1 -0
  11. package/dist/{chunk-Z46LGZ7R.js → chunk-4TR5Y3MP.js} +18 -1
  12. package/dist/chunk-4TR5Y3MP.js.map +1 -0
  13. package/dist/chunk-7G5QKLLF.js +105 -0
  14. package/dist/chunk-7G5QKLLF.js.map +1 -0
  15. package/dist/{chunk-IURZ4QHG.js → chunk-7QJS2XBD.js} +2 -1
  16. package/dist/chunk-7QJS2XBD.js.map +1 -0
  17. package/dist/chunk-AKIU4JBF.js +145 -0
  18. package/dist/chunk-AKIU4JBF.js.map +1 -0
  19. package/dist/{chunk-KSIQZC3J.js → chunk-EVFJW45N.js} +1 -1
  20. package/dist/chunk-EVFJW45N.js.map +1 -0
  21. package/dist/chunk-GINCOFNW.js +134 -0
  22. package/dist/chunk-GINCOFNW.js.map +1 -0
  23. package/dist/chunk-H7ZMPBZC.js +203 -0
  24. package/dist/chunk-H7ZMPBZC.js.map +1 -0
  25. package/dist/chunk-I7WC6E5S.js +71 -0
  26. package/dist/chunk-I7WC6E5S.js.map +1 -0
  27. package/dist/{chunk-6DAZSKE5.js → chunk-IMILOCR5.js} +2 -2
  28. package/dist/chunk-LGQYTK55.js +442 -0
  29. package/dist/chunk-LGQYTK55.js.map +1 -0
  30. package/dist/{chunk-X6LLG7XN.js → chunk-PMGNLNSH.js} +15 -6
  31. package/dist/chunk-PMGNLNSH.js.map +1 -0
  32. package/dist/{chunk-LCJIPE5S.js → chunk-R3UJUOXI.js} +889 -591
  33. package/dist/chunk-R3UJUOXI.js.map +1 -0
  34. package/dist/chunk-SM3G6UAX.js +122 -0
  35. package/dist/chunk-SM3G6UAX.js.map +1 -0
  36. package/dist/chunk-T22OLSET.js +265 -0
  37. package/dist/chunk-T22OLSET.js.map +1 -0
  38. package/dist/chunk-THBR6OXH.js +62 -0
  39. package/dist/chunk-THBR6OXH.js.map +1 -0
  40. package/dist/{chunk-5KYLXEG3.js → chunk-TOZQ3JFN.js} +52 -9
  41. package/dist/chunk-TOZQ3JFN.js.map +1 -0
  42. package/dist/{chunk-IQIPQTQT.js → chunk-UB7XUO7C.js} +171 -26
  43. package/dist/chunk-UB7XUO7C.js.map +1 -0
  44. package/dist/{chunk-OORPX73T.js → chunk-W3EYKZNQ.js} +17 -2
  45. package/dist/chunk-W3EYKZNQ.js.map +1 -0
  46. package/dist/{chunk-K53OZH5Y.js → chunk-ZCHNAM3B.js} +76 -2
  47. package/dist/chunk-ZCHNAM3B.js.map +1 -0
  48. package/dist/cli.js +30 -29
  49. package/dist/cli.js.map +1 -1
  50. package/dist/{config-OH26EIWN.js → config-AK2W3E67.js} +2 -2
  51. package/dist/config-editor-VIA7A72X.js +12 -0
  52. package/dist/{config-registry-SNKA2EH2.js → config-registry-QQOJ2GQP.js} +2 -2
  53. package/dist/{daemon-VKCONJUY.js → daemon-G27YZUWB.js} +3 -3
  54. package/dist/discord-2DKRH45T.js +2044 -0
  55. package/dist/discord-2DKRH45T.js.map +1 -0
  56. package/dist/doctor-AN6AZ3PF.js +9 -0
  57. package/dist/doctor-CHCYUTV5.js +14 -0
  58. package/dist/doctor-CHCYUTV5.js.map +1 -0
  59. package/dist/index.d.ts +331 -6
  60. package/dist/index.js +21 -11
  61. package/dist/{main-NEYPQHB4.js → main-56SPFYW4.js} +32 -24
  62. package/dist/main-56SPFYW4.js.map +1 -0
  63. package/dist/{menu-J5YVH665.js → menu-XR2GET2B.js} +2 -2
  64. package/dist/menu-XR2GET2B.js.map +1 -0
  65. package/dist/new-session-DRRP2J7E.js +16 -0
  66. package/dist/new-session-DRRP2J7E.js.map +1 -0
  67. package/dist/session-FVFLBREJ.js +19 -0
  68. package/dist/session-FVFLBREJ.js.map +1 -0
  69. package/dist/settings-LPOLJ6SA.js +12 -0
  70. package/dist/settings-LPOLJ6SA.js.map +1 -0
  71. package/dist/{setup-ZCWGOEAH.js → setup-IPWJCIJM.js} +9 -5
  72. package/dist/setup-IPWJCIJM.js.map +1 -0
  73. package/dist/{version-VC5CPXBX.js → version-ALWGGVKM.js} +2 -2
  74. package/dist/version-ALWGGVKM.js.map +1 -0
  75. package/package.json +2 -1
  76. package/dist/chunk-2Z2XPUD5.js.map +0 -1
  77. package/dist/chunk-5KYLXEG3.js.map +0 -1
  78. package/dist/chunk-ECBD5I5R.js.map +0 -1
  79. package/dist/chunk-IQIPQTQT.js.map +0 -1
  80. package/dist/chunk-IURZ4QHG.js.map +0 -1
  81. package/dist/chunk-K53OZH5Y.js.map +0 -1
  82. package/dist/chunk-KSIQZC3J.js.map +0 -1
  83. package/dist/chunk-LCJIPE5S.js.map +0 -1
  84. package/dist/chunk-OORPX73T.js.map +0 -1
  85. package/dist/chunk-X6LLG7XN.js.map +0 -1
  86. package/dist/chunk-Z46LGZ7R.js.map +0 -1
  87. package/dist/config-editor-5TICUK3K.js +0 -12
  88. package/dist/doctor-X6UCE7GQ.js +0 -9
  89. package/dist/main-NEYPQHB4.js.map +0 -1
  90. /package/dist/{api-client-UN7BXQOQ.js.map → action-detect-6M5GCGAU.js.map} +0 -0
  91. /package/dist/{autostart-K73RQZVV.js.map → admin-IKPS5PFC.js.map} +0 -0
  92. /package/dist/{config-OH26EIWN.js.map → agents-55NX3DHM.js.map} +0 -0
  93. /package/dist/{config-editor-5TICUK3K.js.map → api-client-BH2JFHQW.js.map} +0 -0
  94. /package/dist/{config-registry-SNKA2EH2.js.map → autostart-A7JRU4WJ.js.map} +0 -0
  95. /package/dist/{chunk-6DAZSKE5.js.map → chunk-IMILOCR5.js.map} +0 -0
  96. /package/dist/{daemon-VKCONJUY.js.map → config-AK2W3E67.js.map} +0 -0
  97. /package/dist/{doctor-X6UCE7GQ.js.map → config-editor-VIA7A72X.js.map} +0 -0
  98. /package/dist/{menu-J5YVH665.js.map → config-registry-QQOJ2GQP.js.map} +0 -0
  99. /package/dist/{setup-ZCWGOEAH.js.map → daemon-G27YZUWB.js.map} +0 -0
  100. /package/dist/{version-VC5CPXBX.js.map → doctor-AN6AZ3PF.js.map} +0 -0
package/README.md CHANGED
@@ -12,7 +12,7 @@ One message, any channel, any agent.
12
12
  [![npm](https://img.shields.io/npm/v/@openacp/cli.svg)](https://www.npmjs.com/package/@openacp/cli)
13
13
  [![Twitter Follow](https://img.shields.io/twitter/follow/Open_ACP?style=social)](https://x.com/Open_ACP)
14
14
 
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)
15
+ [Getting Started](docs/guide/getting-started.md) | [Agents](docs/guide/agents.md) | [Usage](docs/guide/usage.md) | [Configuration](docs/guide/configuration.md) | [Plugins](docs/guide/plugins.md) | [Development](docs/guide/development.md)
16
16
 
17
17
  </div>
18
18
 
@@ -20,7 +20,7 @@ One message, any channel, any agent.
20
20
 
21
21
  ## What is OpenACP?
22
22
 
23
- OpenACP lets you control AI coding agents (Claude Code, Codex, ...) from messaging apps like Telegram. You send a message, the agent writes code, runs commands, and streams everything back — in real time.
23
+ OpenACP lets you control AI coding agents (Claude Code, Codex, ...) from messaging apps like Telegram and Discord. You send a message, the agent writes code, runs commands, and streams everything back — in real time.
24
24
 
25
25
  It uses the [Agent Client Protocol (ACP)](https://agentclientprotocol.org/) to talk to agents. You host it on your own machine, so you own the data.
26
26
 
@@ -76,19 +76,34 @@ OpenACP follows the [Agent Client Protocol (ACP)](https://agentclientprotocol.co
76
76
 
77
77
  - **Multi-agent** — Claude Code, Codex, Gemini, Cursor, and [28+ ACP-compatible agents](#supported-agents)
78
78
  - **Telegram** — Forum topics, real-time streaming, permission buttons, skill commands
79
- - **Tunnel & file viewer** — Public file/diff viewer via Cloudflare, ngrok, bore, or Tailscale
79
+ - **Discord** — Forum/thread-based sessions, slash commands, button interactions
80
80
  - **Session persistence** — Resume sessions across restarts
81
+ - **Daemon mode** — Background service with auto-start on boot
82
+ - **CLI API** — Create and manage sessions from the terminal
83
+ - **Config editor** — Interactive `openacp config` for all settings
84
+ - **Setup wizard** — Interactive first-run setup with bot validation and auto-detect
81
85
  - **Plugin system** — Install channel adapters as npm packages
82
86
  - **Structured logging** — Pino with rotation, per-session log files
83
87
  - **Self-hosted** — Your keys, your data, your machine
84
88
 
89
+ ### [Tunnel & Port Forwarding](docs/guide/tunnel.md)
90
+
91
+ Expose any local port to the internet — dev servers, APIs, static sites. Tunnel is enabled by default with Cloudflare (free, no account needed).
92
+
93
+ - `/tunnel 3000 my-app` in Telegram or `openacp tunnel add 3000 --label my-app` from CLI
94
+ - `/tunnels` to list active tunnels with public URLs and stop buttons
95
+ - Agents can create tunnels too — say "expose port 5173" and the agent handles it
96
+ - Built-in file/diff viewer — Monaco Editor (VS Code engine) with syntax highlighting, line range links, markdown preview
97
+ - Providers: Cloudflare (default), ngrok, bore, Tailscale Funnel
98
+
85
99
  ## Setup
86
100
 
87
101
  ### Prerequisites
88
102
 
89
103
  - **Node.js 20+**
90
- - **A Telegram bot** — Create one via [@BotFather](https://t.me/BotFather) and save the token
91
- - **A Telegram supergroup** with Topics enabled Add your bot as admin
104
+ - **A messaging platform** — Telegram, Discord, or both:
105
+ - **Telegram**: Create a bot via [@BotFather](https://t.me/BotFather), create a supergroup with Topics enabled, add bot as admin
106
+ - **Discord**: Create a bot at [Discord Developer Portal](https://discord.com/developers/applications), invite it to your server with Manage Channels + Send Messages permissions
92
107
 
93
108
  ### Install & first run
94
109
 
@@ -98,13 +113,13 @@ openacp
98
113
  ```
99
114
 
100
115
  > **Important: `openacp` is an interactive CLI.**
101
- > The first run launches a setup wizard that asks you questions in the terminal (bot token, group selection, workspace path, etc.).
116
+ > The first run launches a setup wizard that asks you questions in the terminal (bot token, server selection, workspace path, etc.).
102
117
  > You **must run it yourself in a terminal** — it cannot be run by a script or an AI agent because it requires interactive input.
103
118
 
104
119
  The wizard will:
105
120
 
106
- 1. **Ask for your Telegram bot token** — validates it against the Telegram API
107
- 2. **Auto-detect your group** — send "hi" in the group and it picks it up, or enter the chat ID manually
121
+ 1. **Choose your channel(s)** — Telegram, Discord, or both
122
+ 2. **Configure bot credentials** — bot token and server/group ID, validated against the platform API
108
123
  3. **Set a workspace directory** — where agents will create project folders (default: `~/openacp-workspace`)
109
124
  4. **Detect installed agents** — finds Claude Code, Codex, etc.
110
125
  5. **Choose run mode** — foreground (in terminal) or background (daemon with auto-start)
@@ -134,8 +149,14 @@ openacp agents uninstall <name> # Remove an installed agent
134
149
  openacp agents info <name> # Show agent details & dependencies
135
150
  openacp agents refresh # Force-refresh the registry
136
151
 
152
+ # API (requires running daemon)
153
+ openacp api new [agent] [workspace] # Create a new session
154
+ openacp api cancel <id> # Cancel a session
155
+ openacp api status # Show active sessions
156
+ openacp api agents # List available agents
157
+
137
158
  # System
138
- openacp config # Show current config
159
+ openacp config # Interactive config editor
139
160
  openacp reset # Re-run the setup wizard
140
161
  openacp update # Update to latest version
141
162
  openacp install <plugin> # Install a plugin (e.g. @openacp/adapter-discord)
@@ -145,7 +166,9 @@ openacp plugins # List installed plugins
145
166
 
146
167
  ## Usage
147
168
 
148
- Once OpenACP is running, control it from Telegram:
169
+ Once OpenACP is running, control it from Telegram or Discord:
170
+
171
+ **Telegram** — Bot commands in your supergroup:
149
172
 
150
173
  | Command | Description |
151
174
  |---------|-------------|
@@ -156,13 +179,25 @@ Once OpenACP is running, control it from Telegram:
156
179
  | `/agents` | Browse & install agents from ACP Registry |
157
180
  | `/install <name>` | Install an agent directly |
158
181
 
159
- Each session gets its own forum topic. The agent streams responses in real time, shows tool calls, and asks for permission when needed.
182
+ Each session gets its own forum topic.
183
+
184
+ **Discord** — Slash commands in your server:
185
+
186
+ | Command | Description |
187
+ |---------|-------------|
188
+ | `/new [agent] [workspace]` | Create a new session |
189
+ | `/newchat` | New session, same agent & workspace |
190
+ | `/cancel` | Cancel current session |
191
+ | `/status` | Show session or system status |
192
+ | `/menu` | Open the control panel |
193
+
194
+ Each session gets its own thread in the sessions channel. The agent streams responses in real time, shows tool calls, and asks for permission when needed.
160
195
 
161
196
  ### Session Transfer
162
197
 
163
- Move sessions between your terminal and Telegram:
198
+ Move sessions between your terminal and messaging platforms:
164
199
 
165
- **Terminal → Telegram:**
200
+ **Terminal → Chat:**
166
201
  ```bash
167
202
  # Install integration (one-time)
168
203
  openacp integrate claude
@@ -172,8 +207,8 @@ openacp integrate claude
172
207
  openacp adopt claude <session_id> --cwd /path/to/project
173
208
  ```
174
209
 
175
- **Telegram → Terminal:**
176
- Type `/handoff` in any session topic. The bot replies with a command you can paste in your terminal to continue.
210
+ **Chat → Terminal:**
211
+ Type `/handoff` in any session topic/thread. The bot replies with a command you can paste in your terminal to continue.
177
212
 
178
213
  Sessions are not locked after transfer — you can continue from either side.
179
214
 
@@ -181,7 +216,7 @@ Sessions are not locked after transfer — you can continue from either side.
181
216
 
182
217
  - **Phase 1** — Core + Telegram + ACP agents
183
218
  - **Phase 2** — Tunnel/file viewer, session persistence, logging, plugin system
184
- - **Phase 3** — Agent skills as commands, Discord adapter, Web UI
219
+ - **Phase 3** — Agent skills as commands, Discord adapter 🚧, Web UI
185
220
  - **Phase 4** — Voice control, file/image sharing
186
221
  - **Phase 5** — WhatsApp, agent chaining, plugin marketplace
187
222
 
@@ -0,0 +1,15 @@
1
+ import {
2
+ buildActionKeyboard,
3
+ detectAction,
4
+ getAction,
5
+ removeAction,
6
+ storeAction
7
+ } from "./chunk-I7WC6E5S.js";
8
+ export {
9
+ buildActionKeyboard,
10
+ detectAction,
11
+ getAction,
12
+ removeAction,
13
+ storeAction
14
+ };
15
+ //# sourceMappingURL=action-detect-6M5GCGAU.js.map
@@ -0,0 +1,16 @@
1
+ import {
2
+ buildDangerousModeKeyboard,
3
+ handleDangerous,
4
+ handleDangerousButton,
5
+ handleRestart,
6
+ handleUpdate
7
+ } from "./chunk-7G5QKLLF.js";
8
+ import "./chunk-ESOPMQAY.js";
9
+ export {
10
+ buildDangerousModeKeyboard,
11
+ handleDangerous,
12
+ handleDangerousButton,
13
+ handleRestart,
14
+ handleUpdate
15
+ };
16
+ //# sourceMappingURL=admin-IKPS5PFC.js.map
@@ -0,0 +1,14 @@
1
+ import {
2
+ handleAgentButton,
3
+ handleAgents,
4
+ handleInstall,
5
+ showAgentsList
6
+ } from "./chunk-H7ZMPBZC.js";
7
+ import "./chunk-ESOPMQAY.js";
8
+ export {
9
+ handleAgentButton,
10
+ handleAgents,
11
+ handleInstall,
12
+ showAgentsList
13
+ };
14
+ //# sourceMappingURL=agents-55NX3DHM.js.map
@@ -1,11 +1,13 @@
1
1
  import {
2
2
  apiCall,
3
3
  readApiPort,
4
+ readApiSecret,
4
5
  removeStalePortFile
5
- } from "./chunk-OORPX73T.js";
6
+ } from "./chunk-W3EYKZNQ.js";
6
7
  export {
7
8
  apiCall,
8
9
  readApiPort,
10
+ readApiSecret,
9
11
  removeStalePortFile
10
12
  };
11
- //# sourceMappingURL=api-client-UN7BXQOQ.js.map
13
+ //# sourceMappingURL=api-client-BH2JFHQW.js.map
@@ -1,13 +1,17 @@
1
1
  import {
2
+ escapeSystemdValue,
3
+ escapeXml,
2
4
  generateLaunchdPlist,
3
5
  generateSystemdUnit,
4
6
  installAutoStart,
5
7
  isAutoStartInstalled,
6
8
  isAutoStartSupported,
7
9
  uninstallAutoStart
8
- } from "./chunk-X6LLG7XN.js";
10
+ } from "./chunk-PMGNLNSH.js";
9
11
  import "./chunk-ESOPMQAY.js";
10
12
  export {
13
+ escapeSystemdValue,
14
+ escapeXml,
11
15
  generateLaunchdPlist,
12
16
  generateSystemdUnit,
13
17
  installAutoStart,
@@ -15,4 +19,4 @@ export {
15
19
  isAutoStartSupported,
16
20
  uninstallAutoStart
17
21
  };
18
- //# sourceMappingURL=autostart-K73RQZVV.js.map
22
+ //# sourceMappingURL=autostart-A7JRU4WJ.js.map
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-JKBFUAJK.js";
4
4
  import {
5
5
  expandHome
6
- } from "./chunk-2Z2XPUD5.js";
6
+ } from "./chunk-4LFDEW22.js";
7
7
 
8
8
  // src/core/setup.ts
9
9
  import { execFileSync } from "child_process";
@@ -21,8 +21,8 @@ var c = {
21
21
  var ok = (msg) => `${c.green}${c.bold}\u2713${c.reset} ${c.green}${msg}${c.reset}`;
22
22
  var warn = (msg) => `${c.yellow}\u26A0 ${msg}${c.reset}`;
23
23
  var fail = (msg) => `${c.red}\u2717 ${msg}${c.reset}`;
24
- var step = (n, title) => `
25
- ${c.cyan}${c.bold}[${n}/3]${c.reset} ${c.bold}${title}${c.reset}
24
+ var step = (n, total, title) => `
25
+ ${c.cyan}${c.bold}[${n}/${total}]${c.reset} ${c.bold}${title}${c.reset}
26
26
  `;
27
27
  var dim = (msg) => `${c.dim}${msg}${c.reset}`;
28
28
  async function validateBotToken(token) {
@@ -241,8 +241,8 @@ async function validateAgentCommand(command) {
241
241
  return false;
242
242
  }
243
243
  }
244
- async function setupTelegram() {
245
- console.log(step(1, "Telegram Bot"));
244
+ async function setupTelegram(stepNum = 1, totalSteps = 3) {
245
+ console.log(step(stepNum, totalSteps, "Telegram Bot"));
246
246
  let botToken = "";
247
247
  while (true) {
248
248
  botToken = await input({
@@ -308,6 +308,75 @@ async function setupTelegram() {
308
308
  assistantTopicId: null
309
309
  };
310
310
  }
311
+ async function validateDiscordToken(token) {
312
+ try {
313
+ const res = await fetch("https://discord.com/api/v10/users/@me", {
314
+ headers: { Authorization: `Bot ${token}` }
315
+ });
316
+ if (res.status === 200) {
317
+ const data = await res.json();
318
+ return { ok: true, username: data.username, id: data.id };
319
+ }
320
+ if (res.status === 401) {
321
+ return { ok: false, error: "Token rejected by Discord (401 Unauthorized)" };
322
+ }
323
+ return { ok: false, error: `Discord API returned ${res.status}` };
324
+ } catch (err) {
325
+ return { ok: false, error: err.message };
326
+ }
327
+ }
328
+ async function setupDiscord() {
329
+ console.log("\n\u{1F4F1} Discord Setup\n");
330
+ console.log(` ${c.bold}Quick setup:${c.reset}`);
331
+ console.log(dim(" 1. Create app at https://discord.com/developers/applications"));
332
+ console.log(dim(" 2. Go to Bot \u2192 Reset Token \u2192 copy it"));
333
+ console.log(dim(" 3. Enable Message Content Intent (Bot \u2192 Privileged Intents)"));
334
+ console.log(dim(" 4. OAuth2 \u2192 URL Generator \u2192 scopes: bot + applications.commands"));
335
+ console.log(dim(" 5. Bot Permissions: Manage Channels, Send Messages, Manage Threads, Attach Files"));
336
+ console.log(dim(" 6. Open generated URL \u2192 invite bot to your server"));
337
+ console.log("");
338
+ console.log(dim(` \u{1F4D6} Detailed guide: https://github.com/Open-ACP/OpenACP/blob/main/docs/guide/discord-setup.md`));
339
+ console.log("");
340
+ let botToken = "";
341
+ while (true) {
342
+ botToken = await input({
343
+ message: "Bot token (from Discord Developer Portal):",
344
+ validate: (val) => val.trim().length > 0 || "Token cannot be empty"
345
+ });
346
+ botToken = botToken.trim();
347
+ const result = await validateDiscordToken(botToken);
348
+ if (result.ok) {
349
+ console.log(ok(`Connected as @${result.username} (id: ${result.id})`));
350
+ break;
351
+ }
352
+ console.log(fail(result.error));
353
+ const action = await select({
354
+ message: "What to do?",
355
+ choices: [
356
+ { name: "Re-enter token", value: "retry" },
357
+ { name: "Use as-is (skip validation)", value: "skip" }
358
+ ]
359
+ });
360
+ if (action === "skip") break;
361
+ }
362
+ const guildId = await input({
363
+ message: "Guild (server) ID:",
364
+ validate: (val) => {
365
+ const trimmed = val.trim();
366
+ if (!trimmed) return "Guild ID cannot be empty";
367
+ if (!/^\d{17,20}$/.test(trimmed)) return "Guild ID must be a numeric Discord snowflake (17-20 digits)";
368
+ return true;
369
+ }
370
+ });
371
+ return {
372
+ enabled: true,
373
+ botToken,
374
+ guildId: guildId.trim(),
375
+ forumChannelId: null,
376
+ notificationChannelId: null,
377
+ assistantThreadId: null
378
+ };
379
+ }
311
380
  async function setupAgents() {
312
381
  const { AgentCatalog } = await import("./agent-catalog-T5ECPEDA.js");
313
382
  const { select: select2, checkbox } = await import("@inquirer/prompts");
@@ -386,8 +455,8 @@ async function setupAgents() {
386
455
  console.log(ok(`Default agent: ${c.bold}${defaultAgent}${c.reset}`));
387
456
  return { defaultAgent };
388
457
  }
389
- async function setupWorkspace() {
390
- console.log(step(2, "Workspace"));
458
+ async function setupWorkspace(stepNum = 2, totalSteps = 3) {
459
+ console.log(step(stepNum, totalSteps, "Workspace"));
391
460
  const baseDir = await input({
392
461
  message: "Base directory for workspaces:",
393
462
  default: "~/openacp-workspace",
@@ -395,8 +464,8 @@ async function setupWorkspace() {
395
464
  });
396
465
  return { baseDir: baseDir.trim().replace(/^['"]|['"]$/g, "") };
397
466
  }
398
- async function setupRunMode() {
399
- console.log(step(3, "Run Mode"));
467
+ async function setupRunMode(stepNum = 3, totalSteps = 3) {
468
+ console.log(step(stepNum, totalSteps, "Run Mode"));
400
469
  if (process.platform === "win32") {
401
470
  console.log(dim(" (Daemon mode not available on Windows)"));
402
471
  return { runMode: "foreground", autoStart: false };
@@ -417,7 +486,7 @@ async function setupRunMode() {
417
486
  ]
418
487
  });
419
488
  if (mode === "daemon") {
420
- const { installAutoStart, isAutoStartSupported } = await import("./autostart-K73RQZVV.js");
489
+ const { installAutoStart, isAutoStartSupported } = await import("./autostart-A7JRU4WJ.js");
421
490
  const autoStart = isAutoStartSupported();
422
491
  if (autoStart) {
423
492
  const result = installAutoStart(expandHome("~/.openacp/logs"));
@@ -441,7 +510,28 @@ ${c.cyan}${c.bold} \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
441
510
  async function runSetup(configManager) {
442
511
  printWelcomeBanner();
443
512
  try {
444
- const telegram = await setupTelegram();
513
+ const { select: selectChannel } = await import("@inquirer/prompts");
514
+ const channelChoice = await selectChannel({
515
+ message: "Which messaging platform do you want to use?",
516
+ choices: [
517
+ { name: "Telegram", value: "telegram" },
518
+ { name: "Discord", value: "discord" },
519
+ { name: "Both", value: "both" }
520
+ ]
521
+ });
522
+ let telegram;
523
+ let discord;
524
+ const channelSteps = channelChoice === "both" ? 2 : 1;
525
+ const totalSteps = channelSteps + 2;
526
+ let currentStep = 0;
527
+ if (channelChoice === "telegram" || channelChoice === "both") {
528
+ currentStep++;
529
+ telegram = await setupTelegram(currentStep, totalSteps);
530
+ }
531
+ if (channelChoice === "discord" || channelChoice === "both") {
532
+ currentStep++;
533
+ discord = await setupDiscord();
534
+ }
445
535
  const { defaultAgent } = await setupAgents();
446
536
  {
447
537
  const { confirm } = await import("@inquirer/prompts");
@@ -466,15 +556,20 @@ async function runSetup(configManager) {
466
556
  }
467
557
  }
468
558
  }
469
- const workspace = await setupWorkspace();
470
- const { runMode, autoStart } = await setupRunMode();
559
+ currentStep++;
560
+ const workspace = await setupWorkspace(currentStep, totalSteps);
561
+ currentStep++;
562
+ const { runMode, autoStart } = await setupRunMode(currentStep, totalSteps);
471
563
  const security = {
472
564
  allowedUserIds: [],
473
565
  maxConcurrentSessions: 20,
474
566
  sessionTimeoutMinutes: 60
475
567
  };
568
+ const channels = {};
569
+ if (telegram) channels.telegram = telegram;
570
+ if (discord) channels.discord = discord;
476
571
  const config = {
477
- channels: { telegram },
572
+ channels,
478
573
  agents: {},
479
574
  defaultAgent,
480
575
  workspace,
@@ -502,7 +597,17 @@ async function runSetup(configManager) {
502
597
  storeTtlMinutes: 60,
503
598
  auth: { enabled: false }
504
599
  },
505
- integrations: {}
600
+ usage: {
601
+ enabled: true,
602
+ warningThreshold: 0.8,
603
+ currency: "USD",
604
+ retentionDays: 90
605
+ },
606
+ integrations: {},
607
+ speech: {
608
+ stt: { provider: null, providers: {} },
609
+ tts: { provider: null, providers: {} }
610
+ }
506
611
  };
507
612
  try {
508
613
  await configManager.writeNew(config);
@@ -549,9 +654,11 @@ export {
549
654
  detectAgents,
550
655
  validateAgentCommand,
551
656
  setupTelegram,
657
+ validateDiscordToken,
658
+ setupDiscord,
552
659
  setupAgents,
553
660
  setupWorkspace,
554
661
  setupRunMode,
555
662
  runSetup
556
663
  };
557
- //# sourceMappingURL=chunk-ECBD5I5R.js.map
664
+ //# sourceMappingURL=chunk-2KJC3ILH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/setup.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\nimport { input, select } from \"@inquirer/prompts\";\nimport type { Config, ConfigManager } from \"./config.js\";\nimport { expandHome } from \"./config.js\";\nimport { commandExists } from \"./agent-dependencies.js\";\nimport type { DiscordChannelConfig } from \"../adapters/discord/types.js\";\n\n// --- ANSI colors ---\n\nconst c = {\n reset: \"\\x1b[0m\",\n bold: \"\\x1b[1m\",\n dim: \"\\x1b[2m\",\n green: \"\\x1b[32m\",\n yellow: \"\\x1b[33m\",\n red: \"\\x1b[31m\",\n cyan: \"\\x1b[36m\",\n white: \"\\x1b[37m\",\n};\n\nconst ok = (msg: string) =>\n `${c.green}${c.bold}✓${c.reset} ${c.green}${msg}${c.reset}`;\nconst warn = (msg: string) => `${c.yellow}⚠ ${msg}${c.reset}`;\nconst fail = (msg: string) => `${c.red}✗ ${msg}${c.reset}`;\nconst step = (n: number, total: number, title: string) =>\n `\\n${c.cyan}${c.bold}[${n}/${total}]${c.reset} ${c.bold}${title}${c.reset}\\n`;\nconst dim = (msg: string) => `${c.dim}${msg}${c.reset}`;\n\n// --- Telegram validation ---\n\nexport async function validateBotToken(\n token: string,\n): Promise<\n | { ok: true; botName: string; botUsername: string }\n | { ok: false; error: string }\n> {\n try {\n const res = await fetch(`https://api.telegram.org/bot${token}/getMe`);\n const data = (await res.json()) as {\n ok: boolean;\n result?: { first_name: string; username: string };\n description?: string;\n };\n if (data.ok && data.result) {\n return {\n ok: true,\n botName: data.result.first_name,\n botUsername: data.result.username,\n };\n }\n return { ok: false, error: data.description || \"Invalid token\" };\n } catch (err) {\n return { ok: false, error: (err as Error).message };\n }\n}\n\nexport async function validateChatId(\n token: string,\n chatId: number,\n): Promise<\n { ok: true; title: string; isForum: boolean } | { ok: false; error: string }\n> {\n try {\n const res = await fetch(`https://api.telegram.org/bot${token}/getChat`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ chat_id: chatId }),\n });\n const data = (await res.json()) as {\n ok: boolean;\n result?: { title: string; type: string; is_forum?: boolean };\n description?: string;\n };\n if (!data.ok || !data.result) {\n return { ok: false, error: data.description || \"Invalid chat ID\" };\n }\n if (data.result.type !== \"supergroup\") {\n return {\n ok: false,\n error: `Chat is \"${data.result.type}\", must be a supergroup`,\n };\n }\n return {\n ok: true,\n title: data.result.title,\n isForum: data.result.is_forum === true,\n };\n } catch (err) {\n return { ok: false, error: (err as Error).message };\n }\n}\n\nexport async function validateBotAdmin(\n token: string,\n chatId: number,\n): Promise<{ ok: true } | { ok: false; error: string }> {\n try {\n // Get bot's own user ID\n const meRes = await fetch(`https://api.telegram.org/bot${token}/getMe`);\n const meData = (await meRes.json()) as {\n ok: boolean;\n result?: { id: number };\n };\n if (!meData.ok || !meData.result) {\n return { ok: false, error: \"Could not retrieve bot info\" };\n }\n\n const res = await fetch(\n `https://api.telegram.org/bot${token}/getChatMember`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ chat_id: chatId, user_id: meData.result.id }),\n },\n );\n const data = (await res.json()) as {\n ok: boolean;\n result?: { status: string };\n description?: string;\n };\n if (!data.ok || !data.result) {\n return {\n ok: false,\n error: data.description || \"Could not check bot membership\",\n };\n }\n\n const { status } = data.result;\n if (status === \"administrator\" || status === \"creator\") {\n return { ok: true };\n }\n return {\n ok: false,\n error: `Bot is \"${status}\" in this group. It must be an admin. Please promote the bot to admin in group settings.`,\n };\n } catch (err) {\n return { ok: false, error: (err as Error).message };\n }\n}\n\n// --- Chat ID auto-detection ---\n\nfunction promptManualChatId(): Promise<number> {\n return input({\n message: \"Supergroup chat ID (e.g. -1001234567890):\",\n validate: (val) => {\n const n = Number(val.trim());\n if (isNaN(n) || !Number.isInteger(n)) return \"Chat ID must be an integer\";\n return true;\n },\n }).then((val) => Number(val.trim()));\n}\n\nasync function detectChatId(token: string): Promise<number> {\n // Clear old updates\n let lastUpdateId = 0;\n try {\n const clearRes = await fetch(\n `https://api.telegram.org/bot${token}/getUpdates?offset=-1`,\n );\n const clearData = (await clearRes.json()) as {\n ok: boolean;\n result?: Array<{ update_id: number }>;\n };\n if (clearData.ok && clearData.result?.length) {\n lastUpdateId = clearData.result[clearData.result.length - 1].update_id;\n }\n } catch {\n // ignore\n }\n\n console.log(\"\");\n console.log(` ${c.bold}If you don't have a supergroup yet:${c.reset}`);\n console.log(dim(\" 1. Open Telegram → New Group → add your bot\"));\n console.log(dim(\" 2. Group Settings → convert to Supergroup\"));\n console.log(dim(\" 3. Enable Topics in group settings\"));\n console.log(\"\");\n console.log(` ${c.bold}Then send \"hi\" in the group.${c.reset}`);\n console.log(\n dim(\n ` Listening... press ${c.reset}${c.yellow}m${c.reset}${c.dim} to enter ID manually`,\n ),\n );\n console.log(\"\");\n\n const MAX_ATTEMPTS = 120;\n const POLL_INTERVAL = 1000;\n\n // Listen for 'm' keypress to switch to manual\n let cancelled = false;\n const onKeypress = (data: Buffer) => {\n const key = data.toString();\n if (key === \"m\" || key === \"M\") {\n cancelled = true;\n }\n };\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(true);\n process.stdin.resume();\n process.stdin.on(\"data\", onKeypress);\n }\n\n const cleanup = () => {\n if (process.stdin.isTTY) {\n process.stdin.removeListener(\"data\", onKeypress);\n process.stdin.setRawMode(false);\n process.stdin.pause();\n }\n };\n\n try {\n for (let i = 0; i < MAX_ATTEMPTS; i++) {\n if (cancelled) {\n cleanup();\n return promptManualChatId();\n }\n\n try {\n const offset = lastUpdateId ? lastUpdateId + 1 : 0;\n const res = await fetch(\n `https://api.telegram.org/bot${token}/getUpdates?offset=${offset}&timeout=1`,\n );\n const data = (await res.json()) as {\n ok: boolean;\n result?: Array<{\n update_id: number;\n message?: {\n chat: { id: number; title?: string; type: string };\n };\n my_chat_member?: {\n chat: { id: number; title?: string; type: string };\n };\n }>;\n };\n\n if (!data.ok || !data.result?.length) {\n await new Promise((r) => setTimeout(r, POLL_INTERVAL));\n continue;\n }\n\n const groups = new Map<number, string>();\n for (const update of data.result) {\n lastUpdateId = update.update_id;\n const chat = update.message?.chat ?? update.my_chat_member?.chat;\n if (chat && (chat.type === \"supergroup\" || chat.type === \"group\")) {\n groups.set(chat.id, chat.title ?? String(chat.id));\n }\n }\n\n if (groups.size === 1) {\n const [id, title] = [...groups.entries()][0];\n console.log(\n ok(`Group detected: ${c.bold}${title}${c.reset}${c.green} (${id})`),\n );\n cleanup();\n return id;\n }\n\n if (groups.size > 1) {\n cleanup();\n const choices = [...groups.entries()].map(([id, title]) => ({\n name: `${title} (${id})`,\n value: id,\n }));\n return select({\n message: \"Multiple groups found. Pick one:\",\n choices,\n });\n }\n } catch {\n // Network error, retry\n }\n await new Promise((r) => setTimeout(r, POLL_INTERVAL));\n }\n\n console.log(warn(\"Timed out waiting for messages.\"));\n cleanup();\n return promptManualChatId();\n } catch (err) {\n cleanup();\n throw err;\n }\n}\n\n// --- Agent detection ---\n\nconst KNOWN_AGENTS: Array<{ name: string; commands: string[] }> = [\n // claude-agent-acp is bundled as a dependency — no detection needed, but\n // kept here so detectAgents() still returns it for display purposes.\n { name: \"claude\", commands: [\"claude-agent-acp\"] },\n { name: \"codex\", commands: [\"codex\"] },\n];\n\nexport async function detectAgents(): Promise<\n Array<{ name: string; command: string }>\n> {\n const found: Array<{ name: string; command: string }> = [];\n for (const agent of KNOWN_AGENTS) {\n // Find all available commands for this agent (PATH + node_modules/.bin)\n const available: string[] = [];\n for (const cmd of agent.commands) {\n if (commandExists(cmd)) {\n available.push(cmd);\n }\n }\n if (available.length > 0) {\n // Prefer claude-agent-acp over claude/claude-code (priority order)\n found.push({ name: agent.name, command: available[0] });\n }\n }\n return found;\n}\n\nexport async function validateAgentCommand(command: string): Promise<boolean> {\n try {\n execFileSync(\"which\", [command], { stdio: \"pipe\" });\n return true;\n } catch {\n return false;\n }\n}\n\n// --- Setup steps ---\n\nexport async function setupTelegram(stepNum = 1, totalSteps = 3): Promise<Config[\"channels\"][string]> {\n console.log(step(stepNum, totalSteps, \"Telegram Bot\"));\n\n let botToken = \"\";\n\n while (true) {\n botToken = await input({\n message: \"Bot token (from @BotFather):\",\n validate: (val) => val.trim().length > 0 || \"Token cannot be empty\",\n });\n botToken = botToken.trim();\n\n const result = await validateBotToken(botToken);\n if (result.ok) {\n console.log(ok(`Connected to @${result.botUsername}`));\n break;\n }\n console.log(fail(result.error));\n const action = await select({\n message: \"What to do?\",\n choices: [\n { name: \"Re-enter token\", value: \"retry\" },\n { name: \"Use as-is (skip validation)\", value: \"skip\" },\n ],\n });\n if (action === \"skip\") break;\n }\n\n let chatId: number;\n\n while (true) {\n chatId = await detectChatId(botToken);\n\n // Validate bot can access this chat and it's a supergroup\n const chatResult = await validateChatId(botToken, chatId);\n if (!chatResult.ok) {\n console.log(fail(chatResult.error));\n console.log(\"\");\n console.log(` ${c.bold}How to fix:${c.reset}`);\n console.log(dim(\" 1. Make sure the bot is added to the group\"));\n console.log(dim(\" 2. The group must be a Supergroup (Group Settings → convert)\"));\n console.log(dim(\" 3. Send a message in the group after adding the bot\"));\n console.log(\"\");\n await input({ message: \"Press Enter to try again...\" });\n continue;\n }\n console.log(\n ok(\n `Group: ${c.bold}${chatResult.title}${c.reset}${c.green}${chatResult.isForum ? \" (Topics enabled)\" : \"\"}`,\n ),\n );\n\n // Check bot has admin privileges\n const adminResult = await validateBotAdmin(botToken, chatId);\n if (!adminResult.ok) {\n console.log(fail(adminResult.error));\n console.log(\"\");\n console.log(` ${c.bold}How to fix:${c.reset}`);\n console.log(dim(\" 1. Open the group in Telegram\"));\n console.log(dim(\" 2. Go to Group Settings → Administrators\"));\n console.log(dim(\" 3. Add the bot as an administrator\"));\n console.log(\"\");\n await input({ message: \"Press Enter to check again...\" });\n continue;\n }\n console.log(ok(\"Bot has admin privileges\"));\n break;\n }\n\n return {\n enabled: true,\n botToken,\n chatId,\n notificationTopicId: null,\n assistantTopicId: null,\n };\n}\n\n// --- Discord validation ---\n\nexport async function validateDiscordToken(token: string): Promise<\n | { ok: true; username: string; id: string }\n | { ok: false; error: string }\n> {\n try {\n const res = await fetch(\"https://discord.com/api/v10/users/@me\", {\n headers: { Authorization: `Bot ${token}` },\n });\n if (res.status === 200) {\n const data = (await res.json()) as { username: string; id: string };\n return { ok: true, username: data.username, id: data.id };\n }\n if (res.status === 401) {\n return { ok: false, error: \"Token rejected by Discord (401 Unauthorized)\" };\n }\n return { ok: false, error: `Discord API returned ${res.status}` };\n } catch (err) {\n return { ok: false, error: (err as Error).message };\n }\n}\n\nexport async function setupDiscord(): Promise<DiscordChannelConfig> {\n console.log('\\n📱 Discord Setup\\n');\n\n console.log(` ${c.bold}Quick setup:${c.reset}`);\n console.log(dim(' 1. Create app at https://discord.com/developers/applications'));\n console.log(dim(' 2. Go to Bot → Reset Token → copy it'));\n console.log(dim(' 3. Enable Message Content Intent (Bot → Privileged Intents)'));\n console.log(dim(' 4. OAuth2 → URL Generator → scopes: bot + applications.commands'));\n console.log(dim(' 5. Bot Permissions: Manage Channels, Send Messages, Manage Threads, Attach Files'));\n console.log(dim(' 6. Open generated URL → invite bot to your server'));\n console.log('');\n console.log(dim(` 📖 Detailed guide: https://github.com/Open-ACP/OpenACP/blob/main/docs/guide/discord-setup.md`));\n console.log('');\n\n let botToken = '';\n\n while (true) {\n botToken = await input({\n message: 'Bot token (from Discord Developer Portal):',\n validate: (val) => val.trim().length > 0 || 'Token cannot be empty',\n });\n botToken = botToken.trim();\n\n const result = await validateDiscordToken(botToken);\n if (result.ok) {\n console.log(ok(`Connected as @${result.username} (id: ${result.id})`));\n break;\n }\n console.log(fail(result.error));\n const action = await select({\n message: 'What to do?',\n choices: [\n { name: 'Re-enter token', value: 'retry' },\n { name: 'Use as-is (skip validation)', value: 'skip' },\n ],\n });\n if (action === 'skip') break;\n }\n\n const guildId = await input({\n message: 'Guild (server) ID:',\n validate: (val) => {\n const trimmed = val.trim();\n if (!trimmed) return 'Guild ID cannot be empty';\n if (!/^\\d{17,20}$/.test(trimmed)) return 'Guild ID must be a numeric Discord snowflake (17-20 digits)';\n return true;\n },\n });\n\n return {\n enabled: true,\n botToken,\n guildId: guildId.trim(),\n forumChannelId: null,\n notificationChannelId: null,\n assistantThreadId: null,\n };\n}\n\nexport async function setupAgents(): Promise<{\n defaultAgent: string;\n}> {\n const { AgentCatalog } = await import(\"./agent-catalog.js\");\n const { select, checkbox } = await import(\"@inquirer/prompts\");\n\n const catalog = new AgentCatalog();\n catalog.load();\n\n console.log(dim(\" Checking available agents...\"));\n await catalog.refreshRegistryIfStale();\n\n // Claude is always pre-installed (bundled dependency)\n if (!catalog.getInstalledAgent(\"claude\")) {\n const claudeRegistry = catalog.findRegistryAgent(\"claude-acp\");\n if (claudeRegistry) {\n await catalog.install(\"claude-acp\");\n } else {\n // Fallback: register bundled claude-agent-acp directly\n const { AgentStore } = await import(\"./agent-store.js\");\n const store = new AgentStore();\n store.load();\n store.addAgent(\"claude\", {\n registryId: \"claude-acp\",\n name: \"Claude Agent\",\n version: \"bundled\",\n distribution: \"npx\",\n command: \"npx\",\n args: [\"@zed-industries/claude-agent-acp\"],\n env: {},\n installedAt: new Date().toISOString(),\n binaryPath: null,\n });\n }\n }\n console.log(ok(\"Claude Agent ready\"));\n\n const available = catalog.getAvailable();\n const installed = available.filter((a) => a.installed);\n const installable = available.filter((a) => !a.installed && a.available);\n\n // Offer agent selection — show installed agents as pre-checked + disabled\n if (installed.length > 0 || installable.length > 0) {\n const choices = [\n ...installed.map((a) => ({\n name: `${a.name} (installed)`,\n value: a.key,\n checked: true,\n disabled: \"(already installed)\",\n })),\n ...installable.slice(0, 10).map((a) => ({\n name: `${a.name} (${a.distribution})`,\n value: a.key,\n checked: false,\n })),\n ];\n\n const selected = await checkbox({\n message: \"Install additional agents? (Space to select, Enter to continue)\",\n choices,\n });\n\n for (const key of selected) {\n const regAgent = catalog.findRegistryAgent(key);\n if (regAgent) {\n process.stdout.write(` Installing ${regAgent.name}... `);\n const result = await catalog.install(key);\n if (result.ok) {\n console.log(ok(\"done\"));\n } else {\n console.log(warn(`skipped: ${result.error}`));\n }\n }\n }\n }\n\n // Choose default agent\n const installedAgents = Object.keys(catalog.getInstalledEntries());\n let defaultAgent = \"claude\";\n\n if (installedAgents.length > 1) {\n defaultAgent = await select({\n message: \"Which agent should be the default?\",\n choices: installedAgents.map((key) => {\n const agent = catalog.getInstalledAgent(key)!;\n return { name: `${agent.name} (${key})`, value: key };\n }),\n default: \"claude\",\n });\n }\n\n console.log(ok(`Default agent: ${c.bold}${defaultAgent}${c.reset}`));\n return { defaultAgent };\n}\n\nexport async function setupWorkspace(stepNum = 2, totalSteps = 3): Promise<{ baseDir: string }> {\n console.log(step(stepNum, totalSteps, \"Workspace\"));\n\n const baseDir = await input({\n message: \"Base directory for workspaces:\",\n default: \"~/openacp-workspace\",\n validate: (val) => val.trim().length > 0 || \"Path cannot be empty\",\n });\n\n return { baseDir: baseDir.trim().replace(/^['\"]|['\"]$/g, \"\") };\n}\n\nexport async function setupRunMode(stepNum = 3, totalSteps = 3): Promise<{ runMode: 'foreground' | 'daemon'; autoStart: boolean }> {\n console.log(step(stepNum, totalSteps, 'Run Mode'))\n\n // Don't show daemon option on Windows\n if (process.platform === 'win32') {\n console.log(dim(' (Daemon mode not available on Windows)'))\n return { runMode: 'foreground', autoStart: false }\n }\n\n const mode = await select({\n message: 'How would you like to run OpenACP?',\n choices: [\n {\n name: 'Background (daemon)',\n value: 'daemon' as const,\n description: 'Runs silently, auto-starts on boot. Manage with: openacp status | stop | logs',\n },\n {\n name: 'Foreground (terminal)',\n value: 'foreground' as const,\n description: 'Runs in current terminal session. Start with: openacp',\n },\n ],\n })\n\n if (mode === 'daemon') {\n const { installAutoStart, isAutoStartSupported } = await import('./autostart.js')\n const autoStart = isAutoStartSupported()\n if (autoStart) {\n const result = installAutoStart(expandHome('~/.openacp/logs'))\n if (result.success) {\n console.log(ok('Auto-start on boot enabled'))\n } else {\n console.log(warn(`Auto-start failed: ${result.error}`))\n }\n }\n return { runMode: 'daemon', autoStart }\n }\n\n return { runMode: 'foreground', autoStart: false }\n}\n\n// --- Orchestrator ---\n\nfunction printWelcomeBanner(): void {\n console.log(`\n${c.cyan}${c.bold} ╔══════════════════════════════╗\n ║ Welcome to OpenACP ║\n ╚══════════════════════════════╝${c.reset}\n`);\n}\n\nexport async function runSetup(configManager: ConfigManager): Promise<boolean> {\n printWelcomeBanner();\n\n try {\n const { select: selectChannel } = await import(\"@inquirer/prompts\");\n const channelChoice = await selectChannel({\n message: 'Which messaging platform do you want to use?',\n choices: [\n { name: 'Telegram', value: 'telegram' },\n { name: 'Discord', value: 'discord' },\n { name: 'Both', value: 'both' },\n ],\n });\n\n let telegram: Config[\"channels\"][string] | undefined;\n let discord: DiscordChannelConfig | undefined;\n\n // Calculate total steps dynamically: channel(s) + workspace + run mode\n const channelSteps = channelChoice === 'both' ? 2 : 1;\n const totalSteps = channelSteps + 2; // + workspace + run mode\n\n let currentStep = 0;\n\n if (channelChoice === 'telegram' || channelChoice === 'both') {\n currentStep++;\n telegram = await setupTelegram(currentStep, totalSteps);\n }\n if (channelChoice === 'discord' || channelChoice === 'both') {\n currentStep++;\n discord = await setupDiscord();\n }\n\n const { defaultAgent } = await setupAgents();\n\n // Offer Claude CLI integration\n {\n const { confirm } = await import(\"@inquirer/prompts\");\n const installClaude = await confirm({\n message: \"Install session transfer for Claude? (enables /openacp:handoff in your terminal)\",\n default: true,\n });\n\n if (installClaude) {\n try {\n const { getIntegration } = await import(\"../cli/integrate.js\");\n const integration = getIntegration(\"claude\");\n if (integration) {\n for (const item of integration.items) {\n const result = await item.install();\n for (const log of result.logs) console.log(` ${log}`);\n }\n }\n console.log(\"Claude CLI integration installed.\\n\");\n } catch (err) {\n console.log(`Could not install Claude CLI integration: ${err instanceof Error ? err.message : err}`);\n console.log(\" You can install it later with: openacp integrate claude\\n\");\n }\n }\n }\n\n currentStep++;\n const workspace = await setupWorkspace(currentStep, totalSteps);\n currentStep++;\n const { runMode, autoStart } = await setupRunMode(currentStep, totalSteps);\n const security = {\n allowedUserIds: [] as string[],\n maxConcurrentSessions: 20,\n sessionTimeoutMinutes: 60,\n };\n\n const channels: Config[\"channels\"] = {};\n if (telegram) channels.telegram = telegram;\n if (discord) channels.discord = discord as unknown as Config[\"channels\"][string];\n\n const config: Config = {\n channels,\n agents: {},\n defaultAgent,\n workspace,\n security,\n logging: {\n level: \"info\",\n logDir: \"~/.openacp/logs\",\n maxFileSize: \"10m\",\n maxFiles: 7,\n sessionLogRetentionDays: 30,\n },\n runMode,\n autoStart,\n api: {\n port: 21420,\n host: '127.0.0.1',\n },\n sessionStore: { ttlDays: 30 },\n tunnel: {\n enabled: true,\n port: 3100,\n provider: \"cloudflare\",\n options: {},\n maxUserTunnels: 5,\n storeTtlMinutes: 60,\n auth: { enabled: false },\n },\n usage: {\n enabled: true,\n warningThreshold: 0.8,\n currency: \"USD\",\n retentionDays: 90,\n },\n integrations: {},\n speech: {\n stt: { provider: null, providers: {} },\n tts: { provider: null, providers: {} },\n },\n };\n\n try {\n await configManager.writeNew(config);\n } catch (writeErr) {\n console.log(\n fail(`Could not save config: ${(writeErr as Error).message}`),\n );\n return false;\n }\n\n console.log(\"\");\n console.log(\n ok(`Config saved to ${c.bold}${configManager.getConfigPath()}`),\n );\n\n // Pre-download cloudflared if tunnel enabled\n if (config.tunnel.enabled && config.tunnel.provider === \"cloudflare\") {\n console.log(dim(\" Ensuring cloudflared is installed...\"));\n try {\n const { ensureCloudflared } = await import(\n \"../tunnel/providers/install-cloudflared.js\"\n );\n const binPath = await ensureCloudflared();\n console.log(ok(`cloudflared ready at ${dim(binPath)}`));\n } catch (err) {\n console.log(\n warn(\n `Could not install cloudflared: ${(err as Error).message}. Tunnel may not work.`,\n ),\n );\n }\n }\n\n console.log(ok(\"Starting OpenACP...\"));\n console.log(\"\");\n\n return true;\n } catch (err) {\n if ((err as Error).name === \"ExitPromptError\") {\n console.log(dim(\"\\nSetup cancelled.\"));\n return false;\n }\n throw err;\n }\n}\n"],"mappings":"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","names":["select"]}