@openacp/cli 0.2.15 → 0.2.17

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/dist/index.d.ts CHANGED
@@ -112,6 +112,44 @@ declare const LoggingSchema: z.ZodDefault<z.ZodObject<{
112
112
  sessionLogRetentionDays?: number | undefined;
113
113
  }>>;
114
114
  type LoggingConfig = z.infer<typeof LoggingSchema>;
115
+ declare const TunnelSchema: z.ZodDefault<z.ZodObject<{
116
+ enabled: z.ZodDefault<z.ZodBoolean>;
117
+ port: z.ZodDefault<z.ZodNumber>;
118
+ provider: z.ZodDefault<z.ZodEnum<["cloudflare", "ngrok", "bore"]>>;
119
+ options: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
120
+ storeTtlMinutes: z.ZodDefault<z.ZodNumber>;
121
+ auth: z.ZodDefault<z.ZodObject<{
122
+ enabled: z.ZodDefault<z.ZodBoolean>;
123
+ token: z.ZodOptional<z.ZodString>;
124
+ }, "strip", z.ZodTypeAny, {
125
+ enabled: boolean;
126
+ token?: string | undefined;
127
+ }, {
128
+ enabled?: boolean | undefined;
129
+ token?: string | undefined;
130
+ }>>;
131
+ }, "strip", z.ZodTypeAny, {
132
+ options: Record<string, unknown>;
133
+ enabled: boolean;
134
+ port: number;
135
+ provider: "cloudflare" | "ngrok" | "bore";
136
+ storeTtlMinutes: number;
137
+ auth: {
138
+ enabled: boolean;
139
+ token?: string | undefined;
140
+ };
141
+ }, {
142
+ options?: Record<string, unknown> | undefined;
143
+ enabled?: boolean | undefined;
144
+ port?: number | undefined;
145
+ provider?: "cloudflare" | "ngrok" | "bore" | undefined;
146
+ storeTtlMinutes?: number | undefined;
147
+ auth?: {
148
+ enabled?: boolean | undefined;
149
+ token?: string | undefined;
150
+ } | undefined;
151
+ }>>;
152
+ type TunnelConfig = z.infer<typeof TunnelSchema>;
115
153
  declare const ConfigSchema: z.ZodObject<{
116
154
  channels: z.ZodRecord<z.ZodString, z.ZodObject<{
117
155
  enabled: z.ZodDefault<z.ZodBoolean>;
@@ -179,6 +217,43 @@ declare const ConfigSchema: z.ZodObject<{
179
217
  maxFiles?: number | undefined;
180
218
  sessionLogRetentionDays?: number | undefined;
181
219
  }>>;
220
+ tunnel: z.ZodDefault<z.ZodObject<{
221
+ enabled: z.ZodDefault<z.ZodBoolean>;
222
+ port: z.ZodDefault<z.ZodNumber>;
223
+ provider: z.ZodDefault<z.ZodEnum<["cloudflare", "ngrok", "bore"]>>;
224
+ options: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
225
+ storeTtlMinutes: z.ZodDefault<z.ZodNumber>;
226
+ auth: z.ZodDefault<z.ZodObject<{
227
+ enabled: z.ZodDefault<z.ZodBoolean>;
228
+ token: z.ZodOptional<z.ZodString>;
229
+ }, "strip", z.ZodTypeAny, {
230
+ enabled: boolean;
231
+ token?: string | undefined;
232
+ }, {
233
+ enabled?: boolean | undefined;
234
+ token?: string | undefined;
235
+ }>>;
236
+ }, "strip", z.ZodTypeAny, {
237
+ options: Record<string, unknown>;
238
+ enabled: boolean;
239
+ port: number;
240
+ provider: "cloudflare" | "ngrok" | "bore";
241
+ storeTtlMinutes: number;
242
+ auth: {
243
+ enabled: boolean;
244
+ token?: string | undefined;
245
+ };
246
+ }, {
247
+ options?: Record<string, unknown> | undefined;
248
+ enabled?: boolean | undefined;
249
+ port?: number | undefined;
250
+ provider?: "cloudflare" | "ngrok" | "bore" | undefined;
251
+ storeTtlMinutes?: number | undefined;
252
+ auth?: {
253
+ enabled?: boolean | undefined;
254
+ token?: string | undefined;
255
+ } | undefined;
256
+ }>>;
182
257
  }, "strip", z.ZodTypeAny, {
183
258
  channels: Record<string, z.objectOutputType<{
184
259
  enabled: z.ZodDefault<z.ZodBoolean>;
@@ -206,6 +281,17 @@ declare const ConfigSchema: z.ZodObject<{
206
281
  maxFiles: number;
207
282
  sessionLogRetentionDays: number;
208
283
  };
284
+ tunnel: {
285
+ options: Record<string, unknown>;
286
+ enabled: boolean;
287
+ port: number;
288
+ provider: "cloudflare" | "ngrok" | "bore";
289
+ storeTtlMinutes: number;
290
+ auth: {
291
+ enabled: boolean;
292
+ token?: string | undefined;
293
+ };
294
+ };
209
295
  }, {
210
296
  channels: Record<string, z.objectInputType<{
211
297
  enabled: z.ZodDefault<z.ZodBoolean>;
@@ -233,6 +319,17 @@ declare const ConfigSchema: z.ZodObject<{
233
319
  maxFiles?: number | undefined;
234
320
  sessionLogRetentionDays?: number | undefined;
235
321
  } | undefined;
322
+ tunnel?: {
323
+ options?: Record<string, unknown> | undefined;
324
+ enabled?: boolean | undefined;
325
+ port?: number | undefined;
326
+ provider?: "cloudflare" | "ngrok" | "bore" | undefined;
327
+ storeTtlMinutes?: number | undefined;
328
+ auth?: {
329
+ enabled?: boolean | undefined;
330
+ token?: string | undefined;
331
+ } | undefined;
332
+ } | undefined;
236
333
  }>;
237
334
  type Config = z.infer<typeof ConfigSchema>;
238
335
  declare function expandHome(p: string): string;
@@ -375,12 +472,55 @@ declare class SessionManager {
375
472
  destroyAll(): Promise<void>;
376
473
  }
377
474
 
475
+ interface ViewerEntry {
476
+ id: string;
477
+ type: 'file' | 'diff';
478
+ filePath?: string;
479
+ content: string;
480
+ oldContent?: string;
481
+ language?: string;
482
+ sessionId: string;
483
+ workingDirectory: string;
484
+ createdAt: number;
485
+ expiresAt: number;
486
+ }
487
+ declare class ViewerStore {
488
+ private entries;
489
+ private cleanupTimer;
490
+ private ttlMs;
491
+ constructor(ttlMinutes?: number);
492
+ storeFile(sessionId: string, filePath: string, content: string, workingDirectory: string): string | null;
493
+ storeDiff(sessionId: string, filePath: string, oldContent: string, newContent: string, workingDirectory: string): string | null;
494
+ get(id: string): ViewerEntry | undefined;
495
+ private cleanup;
496
+ private isPathAllowed;
497
+ private detectLanguage;
498
+ destroy(): void;
499
+ }
500
+
501
+ declare class TunnelService {
502
+ private provider;
503
+ private store;
504
+ private server;
505
+ private publicUrl;
506
+ private config;
507
+ constructor(config: TunnelConfig);
508
+ start(): Promise<string>;
509
+ stop(): Promise<void>;
510
+ getPublicUrl(): string;
511
+ getStore(): ViewerStore;
512
+ fileUrl(entryId: string): string;
513
+ diffUrl(entryId: string): string;
514
+ private createProvider;
515
+ }
516
+
378
517
  declare class OpenACPCore {
379
518
  configManager: ConfigManager;
380
519
  agentManager: AgentManager;
381
520
  sessionManager: SessionManager;
382
521
  notificationManager: NotificationManager;
383
522
  adapters: Map<string, ChannelAdapter>;
523
+ tunnelService?: TunnelService;
384
524
  constructor(configManager: ConfigManager);
385
525
  registerAdapter(name: string, adapter: ChannelAdapter): void;
386
526
  start(): Promise<void>;
@@ -389,6 +529,7 @@ declare class OpenACPCore {
389
529
  handleNewSession(channelId: string, agentName?: string, workspacePath?: string): Promise<Session>;
390
530
  handleNewChat(channelId: string, currentThreadId: string): Promise<Session | null>;
391
531
  private toOutgoingMessage;
532
+ private enrichWithViewerLinks;
392
533
  wireSessionEvents(session: Session, adapter: ChannelAdapter): void;
393
534
  }
394
535
 
package/dist/index.js CHANGED
@@ -10,22 +10,24 @@ import {
10
10
  TelegramAdapter,
11
11
  nodeToWebReadable,
12
12
  nodeToWebWritable
13
- } from "./chunk-TKOYKVXH.js";
13
+ } from "./chunk-E6BM7RUB.js";
14
14
  import {
15
15
  ConfigManager,
16
16
  PLUGINS_DIR,
17
- cleanupOldSessionLogs,
18
- createChildLogger,
19
- createSessionLogger,
20
17
  expandHome,
21
- initLogger,
22
18
  installPlugin,
23
19
  listPlugins,
24
20
  loadAdapterFactory,
25
- log,
26
- shutdownLogger,
27
21
  uninstallPlugin
28
- } from "./chunk-HTUZOMIT.js";
22
+ } from "./chunk-QY32F5S7.js";
23
+ import {
24
+ cleanupOldSessionLogs,
25
+ createChildLogger,
26
+ createSessionLogger,
27
+ initLogger,
28
+ log,
29
+ shutdownLogger
30
+ } from "./chunk-ZATQZUJT.js";
29
31
  export {
30
32
  AgentInstance,
31
33
  AgentManager,
@@ -2,15 +2,17 @@
2
2
  import {
3
3
  OpenACPCore,
4
4
  TelegramAdapter
5
- } from "./chunk-TKOYKVXH.js";
5
+ } from "./chunk-E6BM7RUB.js";
6
6
  import {
7
7
  ConfigManager,
8
+ loadAdapterFactory
9
+ } from "./chunk-QY32F5S7.js";
10
+ import {
8
11
  cleanupOldSessionLogs,
9
12
  initLogger,
10
- loadAdapterFactory,
11
13
  log,
12
14
  shutdownLogger
13
- } from "./chunk-HTUZOMIT.js";
15
+ } from "./chunk-ZATQZUJT.js";
14
16
 
15
17
  // src/main.ts
16
18
  var shuttingDown = false;
@@ -18,7 +20,7 @@ async function startServer() {
18
20
  const configManager = new ConfigManager();
19
21
  const configExists = await configManager.exists();
20
22
  if (!configExists) {
21
- const { runSetup } = await import("./setup-4EBTX2NJ.js");
23
+ const { runSetup } = await import("./setup-GIUUMBDH.js");
22
24
  const shouldStart = await runSetup(configManager);
23
25
  if (!shouldStart) process.exit(0);
24
26
  }
@@ -30,6 +32,14 @@ async function startServer() {
30
32
  (err) => log.warn({ err }, "Session log cleanup failed")
31
33
  );
32
34
  const core = new OpenACPCore(configManager);
35
+ let tunnelService;
36
+ if (config.tunnel.enabled) {
37
+ const { TunnelService } = await import("./tunnel-service-FPRPBPQ5.js");
38
+ tunnelService = new TunnelService(config.tunnel);
39
+ const publicUrl = await tunnelService.start();
40
+ core.tunnelService = tunnelService;
41
+ log.info({ publicUrl }, "Tunnel started");
42
+ }
33
43
  for (const [channelName, channelConfig] of Object.entries(config.channels)) {
34
44
  if (!channelConfig.enabled) continue;
35
45
  if (channelName === "telegram") {
@@ -64,6 +74,7 @@ async function startServer() {
64
74
  log.info({ signal }, "Signal received, shutting down");
65
75
  try {
66
76
  await core.stop();
77
+ if (tunnelService) await tunnelService.stop();
67
78
  } catch (err) {
68
79
  log.error({ err }, "Error during shutdown");
69
80
  }
@@ -89,4 +100,4 @@ if (isDirectExecution) {
89
100
  export {
90
101
  startServer
91
102
  };
92
- //# sourceMappingURL=main-PB2TMO3M.js.map
103
+ //# sourceMappingURL=main-OAVE4LUW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/main.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { ConfigManager } from './core/config.js'\nimport { OpenACPCore } from './core/core.js'\nimport { loadAdapterFactory } from './core/plugin-manager.js'\nimport { initLogger, shutdownLogger, cleanupOldSessionLogs, log } from './core/log.js'\nimport { TelegramAdapter } from './adapters/telegram/index.js'\n\nlet shuttingDown = false\n\nexport async function startServer() {\n // 1. Check config exists, run setup if not\n const configManager = new ConfigManager()\n const configExists = await configManager.exists()\n\n if (!configExists) {\n const { runSetup } = await import('./core/setup.js')\n const shouldStart = await runSetup(configManager)\n if (!shouldStart) process.exit(0)\n }\n\n // 2. Load config (validates with Zod)\n await configManager.load()\n const config = configManager.get()\n initLogger(config.logging)\n log.info({ configPath: configManager.getConfigPath() }, 'Config loaded')\n\n // Async cleanup of old session logs (non-blocking)\n cleanupOldSessionLogs(config.logging.sessionLogRetentionDays).catch(err =>\n log.warn({ err }, 'Session log cleanup failed')\n )\n\n // 3. Create core\n const core = new OpenACPCore(configManager)\n\n // 3.5 Start tunnel if configured\n let tunnelService: import('./tunnel/tunnel-service.js').TunnelService | undefined\n if (config.tunnel.enabled) {\n const { TunnelService } = await import('./tunnel/tunnel-service.js')\n tunnelService = new TunnelService(config.tunnel)\n const publicUrl = await tunnelService.start()\n core.tunnelService = tunnelService\n log.info({ publicUrl }, 'Tunnel started')\n }\n\n // 4. Register adapters from config\n for (const [channelName, channelConfig] of Object.entries(config.channels)) {\n if (!channelConfig.enabled) continue\n\n if (channelName === 'telegram') {\n core.registerAdapter('telegram', new TelegramAdapter(core, channelConfig as any))\n log.info({ adapter: 'telegram' }, 'Adapter registered')\n } else if (channelConfig.adapter) {\n // Plugin adapter\n const factory = await loadAdapterFactory(channelConfig.adapter)\n if (factory) {\n const adapter = factory.createAdapter(core, channelConfig)\n core.registerAdapter(channelName, adapter)\n log.info({ adapter: channelName, plugin: channelConfig.adapter }, 'Adapter registered')\n } else {\n const name = channelName\n const err = channelConfig.adapter\n log.error({ adapter: name, err }, 'Failed to load adapter')\n }\n } else {\n log.error({ adapter: channelName }, 'Channel has no built-in adapter; set \"adapter\" field to a plugin package')\n }\n }\n\n if (core.adapters.size === 0) {\n log.error('No channels enabled. Enable at least one channel in config.')\n process.exit(1)\n }\n\n // 5. Start\n await core.start()\n\n // 6. Log ready\n const agents = Object.keys(config.agents)\n log.info({ agents }, 'OpenACP started')\n log.info('Press Ctrl+C to stop')\n\n // 7. Graceful shutdown\n const shutdown = async (signal: string) => {\n if (shuttingDown) return\n shuttingDown = true\n log.info({ signal }, 'Signal received, shutting down')\n\n try {\n await core.stop()\n if (tunnelService) await tunnelService.stop()\n } catch (err) {\n log.error({ err }, 'Error during shutdown')\n }\n\n await shutdownLogger()\n process.exit(0)\n }\n\n process.on('SIGINT', () => shutdown('SIGINT'))\n process.on('SIGTERM', () => shutdown('SIGTERM'))\n\n process.on('uncaughtException', (err) => {\n log.error({ err }, 'Uncaught exception')\n })\n\n process.on('unhandledRejection', (err) => {\n log.error({ err }, 'Unhandled rejection')\n })\n}\n\n// Direct execution for dev (node dist/main.js)\nconst isDirectExecution = process.argv[1]?.endsWith('main.js')\nif (isDirectExecution) {\n startServer().catch((err) => {\n log.error({ err }, 'Fatal error')\n process.exit(1)\n })\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,344 @@
1
+ // src/core/setup.ts
2
+ import { execFileSync } from "child_process";
3
+ import * as fs from "fs";
4
+ import * as path from "path";
5
+ import { input, select } from "@inquirer/prompts";
6
+ var c = {
7
+ reset: "\x1B[0m",
8
+ bold: "\x1B[1m",
9
+ dim: "\x1B[2m",
10
+ green: "\x1B[32m",
11
+ yellow: "\x1B[33m",
12
+ red: "\x1B[31m",
13
+ cyan: "\x1B[36m",
14
+ white: "\x1B[37m"
15
+ };
16
+ var ok = (msg) => `${c.green}${c.bold}\u2713${c.reset} ${c.green}${msg}${c.reset}`;
17
+ var warn = (msg) => `${c.yellow}\u26A0 ${msg}${c.reset}`;
18
+ var fail = (msg) => `${c.red}\u2717 ${msg}${c.reset}`;
19
+ var step = (n, title) => `
20
+ ${c.cyan}${c.bold}[${n}/3]${c.reset} ${c.bold}${title}${c.reset}
21
+ `;
22
+ var dim = (msg) => `${c.dim}${msg}${c.reset}`;
23
+ async function validateBotToken(token) {
24
+ try {
25
+ const res = await fetch(`https://api.telegram.org/bot${token}/getMe`);
26
+ const data = await res.json();
27
+ if (data.ok && data.result) {
28
+ return {
29
+ ok: true,
30
+ botName: data.result.first_name,
31
+ botUsername: data.result.username
32
+ };
33
+ }
34
+ return { ok: false, error: data.description || "Invalid token" };
35
+ } catch (err) {
36
+ return { ok: false, error: err.message };
37
+ }
38
+ }
39
+ async function validateChatId(token, chatId) {
40
+ try {
41
+ const res = await fetch(`https://api.telegram.org/bot${token}/getChat`, {
42
+ method: "POST",
43
+ headers: { "Content-Type": "application/json" },
44
+ body: JSON.stringify({ chat_id: chatId })
45
+ });
46
+ const data = await res.json();
47
+ if (!data.ok || !data.result) {
48
+ return { ok: false, error: data.description || "Invalid chat ID" };
49
+ }
50
+ if (data.result.type !== "supergroup") {
51
+ return {
52
+ ok: false,
53
+ error: `Chat is "${data.result.type}", must be a supergroup`
54
+ };
55
+ }
56
+ return {
57
+ ok: true,
58
+ title: data.result.title,
59
+ isForum: data.result.is_forum === true
60
+ };
61
+ } catch (err) {
62
+ return { ok: false, error: err.message };
63
+ }
64
+ }
65
+ function promptManualChatId() {
66
+ return input({
67
+ message: "Supergroup chat ID (e.g. -1001234567890):",
68
+ validate: (val) => {
69
+ const n = Number(val.trim());
70
+ if (isNaN(n) || !Number.isInteger(n)) return "Chat ID must be an integer";
71
+ return true;
72
+ }
73
+ }).then((val) => Number(val.trim()));
74
+ }
75
+ async function detectChatId(token) {
76
+ let lastUpdateId = 0;
77
+ try {
78
+ const clearRes = await fetch(
79
+ `https://api.telegram.org/bot${token}/getUpdates?offset=-1`
80
+ );
81
+ const clearData = await clearRes.json();
82
+ if (clearData.ok && clearData.result?.length) {
83
+ lastUpdateId = clearData.result[clearData.result.length - 1].update_id;
84
+ }
85
+ } catch {
86
+ }
87
+ console.log("");
88
+ console.log(` ${c.bold}If you don't have a supergroup yet:${c.reset}`);
89
+ console.log(dim(" 1. Open Telegram \u2192 New Group \u2192 add your bot"));
90
+ console.log(dim(" 2. Group Settings \u2192 convert to Supergroup"));
91
+ console.log(dim(" 3. Enable Topics in group settings"));
92
+ console.log("");
93
+ console.log(` ${c.bold}Then send "hi" in the group.${c.reset}`);
94
+ console.log(
95
+ dim(
96
+ ` Listening... press ${c.reset}${c.yellow}m${c.reset}${c.dim} to enter ID manually`
97
+ )
98
+ );
99
+ console.log("");
100
+ const MAX_ATTEMPTS = 120;
101
+ const POLL_INTERVAL = 1e3;
102
+ let cancelled = false;
103
+ const onKeypress = (data) => {
104
+ const key = data.toString();
105
+ if (key === "m" || key === "M") {
106
+ cancelled = true;
107
+ }
108
+ };
109
+ if (process.stdin.isTTY) {
110
+ process.stdin.setRawMode(true);
111
+ process.stdin.resume();
112
+ process.stdin.on("data", onKeypress);
113
+ }
114
+ const cleanup = () => {
115
+ if (process.stdin.isTTY) {
116
+ process.stdin.removeListener("data", onKeypress);
117
+ process.stdin.setRawMode(false);
118
+ process.stdin.pause();
119
+ }
120
+ };
121
+ try {
122
+ for (let i = 0; i < MAX_ATTEMPTS; i++) {
123
+ if (cancelled) {
124
+ cleanup();
125
+ return promptManualChatId();
126
+ }
127
+ try {
128
+ const offset = lastUpdateId ? lastUpdateId + 1 : 0;
129
+ const res = await fetch(
130
+ `https://api.telegram.org/bot${token}/getUpdates?offset=${offset}&timeout=1`
131
+ );
132
+ const data = await res.json();
133
+ if (!data.ok || !data.result?.length) {
134
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL));
135
+ continue;
136
+ }
137
+ const groups = /* @__PURE__ */ new Map();
138
+ for (const update of data.result) {
139
+ lastUpdateId = update.update_id;
140
+ const chat = update.message?.chat ?? update.my_chat_member?.chat;
141
+ if (chat && (chat.type === "supergroup" || chat.type === "group")) {
142
+ groups.set(chat.id, chat.title ?? String(chat.id));
143
+ }
144
+ }
145
+ if (groups.size === 1) {
146
+ const [id, title] = [...groups.entries()][0];
147
+ console.log(
148
+ ok(`Group detected: ${c.bold}${title}${c.reset}${c.green} (${id})`)
149
+ );
150
+ cleanup();
151
+ return id;
152
+ }
153
+ if (groups.size > 1) {
154
+ cleanup();
155
+ const choices = [...groups.entries()].map(([id, title]) => ({
156
+ name: `${title} (${id})`,
157
+ value: id
158
+ }));
159
+ return select({
160
+ message: "Multiple groups found. Pick one:",
161
+ choices
162
+ });
163
+ }
164
+ } catch {
165
+ }
166
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL));
167
+ }
168
+ console.log(warn("Timed out waiting for messages."));
169
+ cleanup();
170
+ return promptManualChatId();
171
+ } catch (err) {
172
+ cleanup();
173
+ throw err;
174
+ }
175
+ }
176
+ var KNOWN_AGENTS = [
177
+ { name: "claude", commands: ["claude-agent-acp"] }
178
+ ];
179
+ function commandExists(cmd) {
180
+ try {
181
+ execFileSync("which", [cmd], { stdio: "pipe" });
182
+ return true;
183
+ } catch {
184
+ }
185
+ let dir = process.cwd();
186
+ while (true) {
187
+ const binPath = path.join(dir, "node_modules", ".bin", cmd);
188
+ if (fs.existsSync(binPath)) return true;
189
+ const parent = path.dirname(dir);
190
+ if (parent === dir) break;
191
+ dir = parent;
192
+ }
193
+ return false;
194
+ }
195
+ async function detectAgents() {
196
+ const found = [];
197
+ for (const agent of KNOWN_AGENTS) {
198
+ const available = [];
199
+ for (const cmd of agent.commands) {
200
+ if (commandExists(cmd)) {
201
+ available.push(cmd);
202
+ }
203
+ }
204
+ if (available.length > 0) {
205
+ found.push({ name: agent.name, command: available[0] });
206
+ }
207
+ }
208
+ return found;
209
+ }
210
+ async function validateAgentCommand(command) {
211
+ try {
212
+ execFileSync("which", [command], { stdio: "pipe" });
213
+ return true;
214
+ } catch {
215
+ return false;
216
+ }
217
+ }
218
+ async function setupTelegram() {
219
+ console.log(step(1, "Telegram Bot"));
220
+ let botToken = "";
221
+ while (true) {
222
+ botToken = await input({
223
+ message: "Bot token (from @BotFather):",
224
+ validate: (val) => val.trim().length > 0 || "Token cannot be empty"
225
+ });
226
+ botToken = botToken.trim();
227
+ const result = await validateBotToken(botToken);
228
+ if (result.ok) {
229
+ console.log(ok(`Connected to @${result.botUsername}`));
230
+ break;
231
+ }
232
+ console.log(fail(result.error));
233
+ const action = await select({
234
+ message: "What to do?",
235
+ choices: [
236
+ { name: "Re-enter token", value: "retry" },
237
+ { name: "Use as-is (skip validation)", value: "skip" }
238
+ ]
239
+ });
240
+ if (action === "skip") break;
241
+ }
242
+ console.log(step(2, "Group Chat"));
243
+ const chatId = await detectChatId(botToken);
244
+ return {
245
+ enabled: true,
246
+ botToken,
247
+ chatId,
248
+ notificationTopicId: null,
249
+ assistantTopicId: null
250
+ };
251
+ }
252
+ async function setupAgents() {
253
+ const detected = await detectAgents();
254
+ const agents = {};
255
+ if (detected.length > 0) {
256
+ for (const agent of detected) {
257
+ agents[agent.name] = { command: agent.command, args: [], env: {} };
258
+ }
259
+ } else {
260
+ agents["claude"] = { command: "claude-agent-acp", args: [], env: {} };
261
+ }
262
+ const defaultAgent = Object.keys(agents)[0];
263
+ const agentCmd = agents[defaultAgent].command;
264
+ console.log(
265
+ ok(`Agent: ${c.bold}${defaultAgent}${c.reset}${c.green} (${agentCmd})`)
266
+ );
267
+ return { agents, defaultAgent };
268
+ }
269
+ async function setupWorkspace() {
270
+ console.log(step(3, "Workspace"));
271
+ const baseDir = await input({
272
+ message: "Base directory for workspaces:",
273
+ default: "~/openacp-workspace",
274
+ validate: (val) => val.trim().length > 0 || "Path cannot be empty"
275
+ });
276
+ return { baseDir: baseDir.trim().replace(/^['"]|['"]$/g, "") };
277
+ }
278
+ function printWelcomeBanner() {
279
+ console.log(`
280
+ ${c.cyan}${c.bold} \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
281
+ \u2551 Welcome to OpenACP \u2551
282
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D${c.reset}
283
+ `);
284
+ }
285
+ async function runSetup(configManager) {
286
+ printWelcomeBanner();
287
+ try {
288
+ const telegram = await setupTelegram();
289
+ const { agents, defaultAgent } = await setupAgents();
290
+ const workspace = await setupWorkspace();
291
+ const security = {
292
+ allowedUserIds: [],
293
+ maxConcurrentSessions: 5,
294
+ sessionTimeoutMinutes: 60
295
+ };
296
+ const config = {
297
+ channels: { telegram },
298
+ agents,
299
+ defaultAgent,
300
+ workspace,
301
+ security,
302
+ logging: {
303
+ level: "info",
304
+ logDir: "~/.openacp/logs",
305
+ maxFileSize: "10m",
306
+ maxFiles: 7,
307
+ sessionLogRetentionDays: 30
308
+ },
309
+ tunnel: { enabled: false, port: 3100, provider: "cloudflare", options: {}, storeTtlMinutes: 60, auth: { enabled: false } }
310
+ };
311
+ try {
312
+ await configManager.writeNew(config);
313
+ } catch (writeErr) {
314
+ console.log(
315
+ fail(`Could not save config: ${writeErr.message}`)
316
+ );
317
+ return false;
318
+ }
319
+ console.log("");
320
+ console.log(
321
+ ok(`Config saved to ${c.bold}${configManager.getConfigPath()}`)
322
+ );
323
+ console.log(ok("Starting OpenACP..."));
324
+ console.log("");
325
+ return true;
326
+ } catch (err) {
327
+ if (err.name === "ExitPromptError") {
328
+ console.log(dim("\nSetup cancelled."));
329
+ return false;
330
+ }
331
+ throw err;
332
+ }
333
+ }
334
+ export {
335
+ detectAgents,
336
+ runSetup,
337
+ setupAgents,
338
+ setupTelegram,
339
+ setupWorkspace,
340
+ validateAgentCommand,
341
+ validateBotToken,
342
+ validateChatId
343
+ };
344
+ //# sourceMappingURL=setup-GIUUMBDH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/setup.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { input, select } from \"@inquirer/prompts\";\nimport type { Config, ConfigManager } from \"./config.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, title: string) =>\n `\\n${c.cyan}${c.bold}[${n}/3]${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\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 { name: \"claude\", commands: [\"claude-agent-acp\"] },\n];\n\nfunction commandExists(cmd: string): boolean {\n try {\n execFileSync(\"which\", [cmd], { stdio: \"pipe\" });\n return true;\n } catch {\n // not in PATH\n }\n let dir = process.cwd();\n while (true) {\n const binPath = path.join(dir, \"node_modules\", \".bin\", cmd);\n if (fs.existsSync(binPath)) return true;\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return false;\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 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 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(): Promise<Config[\"channels\"][string]> {\n console.log(step(1, \"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 console.log(step(2, \"Group Chat\"));\n\n const chatId = await detectChatId(botToken);\n\n return {\n enabled: true,\n botToken,\n chatId,\n notificationTopicId: null,\n assistantTopicId: null,\n };\n}\n\nexport async function setupAgents(): Promise<{\n agents: Config[\"agents\"];\n defaultAgent: string;\n}> {\n const detected = await detectAgents();\n const agents: Config[\"agents\"] = {};\n\n if (detected.length > 0) {\n for (const agent of detected) {\n agents[agent.name] = { command: agent.command, args: [], env: {} };\n }\n } else {\n agents[\"claude\"] = { command: \"claude-agent-acp\", args: [], env: {} };\n }\n\n const defaultAgent = Object.keys(agents)[0];\n const agentCmd = agents[defaultAgent].command;\n console.log(\n ok(`Agent: ${c.bold}${defaultAgent}${c.reset}${c.green} (${agentCmd})`),\n );\n\n return { agents, defaultAgent };\n}\n\nexport async function setupWorkspace(): Promise<{ baseDir: string }> {\n console.log(step(3, \"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\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 telegram = await setupTelegram();\n const { agents, defaultAgent } = await setupAgents();\n const workspace = await setupWorkspace();\n const security = {\n allowedUserIds: [] as string[],\n maxConcurrentSessions: 5,\n sessionTimeoutMinutes: 60,\n };\n\n const config: Config = {\n channels: { telegram },\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 tunnel: { enabled: false, port: 3100, provider: 'cloudflare', options: {}, storeTtlMinutes: 60, auth: { enabled: false } },\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 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":[]}