@openacp/cli 0.2.23 → 0.2.25

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 (44) hide show
  1. package/dist/autostart-YBYXQA77.js +18 -0
  2. package/dist/autostart-YBYXQA77.js.map +1 -0
  3. package/dist/{setup-XQBEZZQB.js → chunk-4BN7NSKB.js} +140 -9
  4. package/dist/chunk-4BN7NSKB.js.map +1 -0
  5. package/dist/chunk-CQMS5U7Z.js +63 -0
  6. package/dist/chunk-CQMS5U7Z.js.map +1 -0
  7. package/dist/{chunk-XOVJLTEC.js → chunk-FGXG3H3F.js} +14 -58
  8. package/dist/chunk-FGXG3H3F.js.map +1 -0
  9. package/dist/{chunk-ZATQZUJT.js → chunk-MNJDYDGH.js} +9 -1
  10. package/dist/{chunk-ZATQZUJT.js.map → chunk-MNJDYDGH.js.map} +1 -1
  11. package/dist/chunk-PQRVTUNH.js +145 -0
  12. package/dist/chunk-PQRVTUNH.js.map +1 -0
  13. package/dist/chunk-QWUJIKTX.js +527 -0
  14. package/dist/chunk-QWUJIKTX.js.map +1 -0
  15. package/dist/{chunk-EIBLQU3H.js → chunk-S5MPFOR3.js} +632 -136
  16. package/dist/chunk-S5MPFOR3.js.map +1 -0
  17. package/dist/chunk-S6O7SM6A.js +129 -0
  18. package/dist/chunk-S6O7SM6A.js.map +1 -0
  19. package/dist/chunk-WXS6ONOD.js +103 -0
  20. package/dist/chunk-WXS6ONOD.js.map +1 -0
  21. package/dist/cli.js +354 -4
  22. package/dist/cli.js.map +1 -1
  23. package/dist/config-2XALNLAA.js +14 -0
  24. package/dist/config-2XALNLAA.js.map +1 -0
  25. package/dist/config-editor-56B6YU7B.js +11 -0
  26. package/dist/config-editor-56B6YU7B.js.map +1 -0
  27. package/dist/daemon-3E5OMLT3.js +29 -0
  28. package/dist/daemon-3E5OMLT3.js.map +1 -0
  29. package/dist/index.d.ts +99 -18
  30. package/dist/index.js +35 -6
  31. package/dist/install-cloudflared-57NRTI4E.js +8 -0
  32. package/dist/install-cloudflared-57NRTI4E.js.map +1 -0
  33. package/dist/{main-VJUX7RUY.js → main-TGYT34IJ.js} +43 -11
  34. package/dist/main-TGYT34IJ.js.map +1 -0
  35. package/dist/setup-FTNJACSC.js +27 -0
  36. package/dist/setup-FTNJACSC.js.map +1 -0
  37. package/dist/{tunnel-service-I6NUMBT4.js → tunnel-service-I6WM6USB.js} +10 -10
  38. package/dist/tunnel-service-I6WM6USB.js.map +1 -0
  39. package/package.json +2 -2
  40. package/dist/chunk-EIBLQU3H.js.map +0 -1
  41. package/dist/chunk-XOVJLTEC.js.map +0 -1
  42. package/dist/main-VJUX7RUY.js.map +0 -1
  43. package/dist/setup-XQBEZZQB.js.map +0 -1
  44. package/dist/tunnel-service-I6NUMBT4.js.map +0 -1
@@ -0,0 +1,145 @@
1
+ import {
2
+ createChildLogger
3
+ } from "./chunk-MNJDYDGH.js";
4
+
5
+ // src/core/autostart.ts
6
+ import { execFileSync } from "child_process";
7
+ import * as fs from "fs";
8
+ import * as path from "path";
9
+ import * as os from "os";
10
+ var log = createChildLogger({ module: "autostart" });
11
+ var LAUNCHD_LABEL = "com.openacp.daemon";
12
+ var LAUNCHD_PLIST_PATH = path.join(os.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
13
+ var SYSTEMD_SERVICE_PATH = path.join(os.homedir(), ".config", "systemd", "user", "openacp.service");
14
+ function isAutoStartSupported() {
15
+ return process.platform === "darwin" || process.platform === "linux";
16
+ }
17
+ function generateLaunchdPlist(nodePath, cliPath, logDir) {
18
+ const logFile = path.join(logDir, "openacp.log");
19
+ return `<?xml version="1.0" encoding="UTF-8"?>
20
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
21
+ <plist version="1.0">
22
+ <dict>
23
+ <key>Label</key>
24
+ <string>${LAUNCHD_LABEL}</string>
25
+ <key>ProgramArguments</key>
26
+ <array>
27
+ <string>${nodePath}</string>
28
+ <string>${cliPath}</string>
29
+ <string>--daemon-child</string>
30
+ </array>
31
+ <key>RunAtLoad</key>
32
+ <true/>
33
+ <key>KeepAlive</key>
34
+ <dict>
35
+ <key>SuccessfulExit</key>
36
+ <false/>
37
+ </dict>
38
+ <key>StandardOutPath</key>
39
+ <string>${logFile}</string>
40
+ <key>StandardErrorPath</key>
41
+ <string>${logFile}</string>
42
+ </dict>
43
+ </plist>
44
+ `;
45
+ }
46
+ function generateSystemdUnit(nodePath, cliPath) {
47
+ return `[Unit]
48
+ Description=OpenACP Daemon
49
+
50
+ [Service]
51
+ ExecStart=${nodePath} ${cliPath} --daemon-child
52
+ Restart=on-failure
53
+
54
+ [Install]
55
+ WantedBy=default.target
56
+ `;
57
+ }
58
+ function installAutoStart(logDir) {
59
+ if (!isAutoStartSupported()) {
60
+ return { success: false, error: "Auto-start not supported on this platform" };
61
+ }
62
+ const nodePath = process.execPath;
63
+ const cliPath = path.resolve(process.argv[1]);
64
+ const resolvedLogDir = logDir.startsWith("~") ? path.join(os.homedir(), logDir.slice(1)) : logDir;
65
+ try {
66
+ if (process.platform === "darwin") {
67
+ const plist = generateLaunchdPlist(nodePath, cliPath, resolvedLogDir);
68
+ const dir = path.dirname(LAUNCHD_PLIST_PATH);
69
+ fs.mkdirSync(dir, { recursive: true });
70
+ fs.writeFileSync(LAUNCHD_PLIST_PATH, plist);
71
+ execFileSync("launchctl", ["load", LAUNCHD_PLIST_PATH], { stdio: "pipe" });
72
+ log.info("LaunchAgent installed");
73
+ return { success: true };
74
+ }
75
+ if (process.platform === "linux") {
76
+ const unit = generateSystemdUnit(nodePath, cliPath);
77
+ const dir = path.dirname(SYSTEMD_SERVICE_PATH);
78
+ fs.mkdirSync(dir, { recursive: true });
79
+ fs.writeFileSync(SYSTEMD_SERVICE_PATH, unit);
80
+ execFileSync("systemctl", ["--user", "daemon-reload"], { stdio: "pipe" });
81
+ execFileSync("systemctl", ["--user", "enable", "openacp"], { stdio: "pipe" });
82
+ log.info("systemd user service installed");
83
+ return { success: true };
84
+ }
85
+ return { success: false, error: "Unsupported platform" };
86
+ } catch (e) {
87
+ const msg = e.message;
88
+ log.error({ err: msg }, "Failed to install auto-start");
89
+ return { success: false, error: msg };
90
+ }
91
+ }
92
+ function uninstallAutoStart() {
93
+ if (!isAutoStartSupported()) {
94
+ return { success: false, error: "Auto-start not supported on this platform" };
95
+ }
96
+ try {
97
+ if (process.platform === "darwin") {
98
+ if (fs.existsSync(LAUNCHD_PLIST_PATH)) {
99
+ try {
100
+ execFileSync("launchctl", ["unload", LAUNCHD_PLIST_PATH], { stdio: "pipe" });
101
+ } catch {
102
+ }
103
+ fs.unlinkSync(LAUNCHD_PLIST_PATH);
104
+ log.info("LaunchAgent removed");
105
+ }
106
+ return { success: true };
107
+ }
108
+ if (process.platform === "linux") {
109
+ if (fs.existsSync(SYSTEMD_SERVICE_PATH)) {
110
+ try {
111
+ execFileSync("systemctl", ["--user", "disable", "openacp"], { stdio: "pipe" });
112
+ } catch {
113
+ }
114
+ fs.unlinkSync(SYSTEMD_SERVICE_PATH);
115
+ execFileSync("systemctl", ["--user", "daemon-reload"], { stdio: "pipe" });
116
+ log.info("systemd user service removed");
117
+ }
118
+ return { success: true };
119
+ }
120
+ return { success: false, error: "Unsupported platform" };
121
+ } catch (e) {
122
+ const msg = e.message;
123
+ log.error({ err: msg }, "Failed to uninstall auto-start");
124
+ return { success: false, error: msg };
125
+ }
126
+ }
127
+ function isAutoStartInstalled() {
128
+ if (process.platform === "darwin") {
129
+ return fs.existsSync(LAUNCHD_PLIST_PATH);
130
+ }
131
+ if (process.platform === "linux") {
132
+ return fs.existsSync(SYSTEMD_SERVICE_PATH);
133
+ }
134
+ return false;
135
+ }
136
+
137
+ export {
138
+ isAutoStartSupported,
139
+ generateLaunchdPlist,
140
+ generateSystemdUnit,
141
+ installAutoStart,
142
+ uninstallAutoStart,
143
+ isAutoStartInstalled
144
+ };
145
+ //# sourceMappingURL=chunk-PQRVTUNH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/autostart.ts"],"sourcesContent":["import { execFileSync } from 'node:child_process'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport * as os from 'node:os'\nimport { createChildLogger } from './log.js'\n\nconst log = createChildLogger({ module: 'autostart' })\n\nconst LAUNCHD_LABEL = 'com.openacp.daemon'\nconst LAUNCHD_PLIST_PATH = path.join(os.homedir(), 'Library', 'LaunchAgents', `${LAUNCHD_LABEL}.plist`)\nconst SYSTEMD_SERVICE_PATH = path.join(os.homedir(), '.config', 'systemd', 'user', 'openacp.service')\n\nexport function isAutoStartSupported(): boolean {\n return process.platform === 'darwin' || process.platform === 'linux'\n}\n\nexport function generateLaunchdPlist(nodePath: string, cliPath: string, logDir: string): string {\n const logFile = path.join(logDir, 'openacp.log')\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${LAUNCHD_LABEL}</string>\n <key>ProgramArguments</key>\n <array>\n <string>${nodePath}</string>\n <string>${cliPath}</string>\n <string>--daemon-child</string>\n </array>\n <key>RunAtLoad</key>\n <true/>\n <key>KeepAlive</key>\n <dict>\n <key>SuccessfulExit</key>\n <false/>\n </dict>\n <key>StandardOutPath</key>\n <string>${logFile}</string>\n <key>StandardErrorPath</key>\n <string>${logFile}</string>\n</dict>\n</plist>\n`\n}\n\nexport function generateSystemdUnit(nodePath: string, cliPath: string): string {\n return `[Unit]\nDescription=OpenACP Daemon\n\n[Service]\nExecStart=${nodePath} ${cliPath} --daemon-child\nRestart=on-failure\n\n[Install]\nWantedBy=default.target\n`\n}\n\nexport function installAutoStart(logDir: string): { success: boolean; error?: string } {\n if (!isAutoStartSupported()) {\n return { success: false, error: 'Auto-start not supported on this platform' }\n }\n\n const nodePath = process.execPath\n const cliPath = path.resolve(process.argv[1])\n const resolvedLogDir = logDir.startsWith('~')\n ? path.join(os.homedir(), logDir.slice(1))\n : logDir\n\n try {\n if (process.platform === 'darwin') {\n const plist = generateLaunchdPlist(nodePath, cliPath, resolvedLogDir)\n const dir = path.dirname(LAUNCHD_PLIST_PATH)\n fs.mkdirSync(dir, { recursive: true })\n fs.writeFileSync(LAUNCHD_PLIST_PATH, plist)\n execFileSync('launchctl', ['load', LAUNCHD_PLIST_PATH], { stdio: 'pipe' })\n log.info('LaunchAgent installed')\n return { success: true }\n }\n\n if (process.platform === 'linux') {\n const unit = generateSystemdUnit(nodePath, cliPath)\n const dir = path.dirname(SYSTEMD_SERVICE_PATH)\n fs.mkdirSync(dir, { recursive: true })\n fs.writeFileSync(SYSTEMD_SERVICE_PATH, unit)\n execFileSync('systemctl', ['--user', 'daemon-reload'], { stdio: 'pipe' })\n execFileSync('systemctl', ['--user', 'enable', 'openacp'], { stdio: 'pipe' })\n log.info('systemd user service installed')\n return { success: true }\n }\n\n return { success: false, error: 'Unsupported platform' }\n } catch (e) {\n const msg = (e as Error).message\n log.error({ err: msg }, 'Failed to install auto-start')\n return { success: false, error: msg }\n }\n}\n\nexport function uninstallAutoStart(): { success: boolean; error?: string } {\n if (!isAutoStartSupported()) {\n return { success: false, error: 'Auto-start not supported on this platform' }\n }\n\n try {\n if (process.platform === 'darwin') {\n if (fs.existsSync(LAUNCHD_PLIST_PATH)) {\n try {\n execFileSync('launchctl', ['unload', LAUNCHD_PLIST_PATH], { stdio: 'pipe' })\n } catch {\n // may already be unloaded\n }\n fs.unlinkSync(LAUNCHD_PLIST_PATH)\n log.info('LaunchAgent removed')\n }\n return { success: true }\n }\n\n if (process.platform === 'linux') {\n if (fs.existsSync(SYSTEMD_SERVICE_PATH)) {\n try {\n execFileSync('systemctl', ['--user', 'disable', 'openacp'], { stdio: 'pipe' })\n } catch {\n // may already be disabled\n }\n fs.unlinkSync(SYSTEMD_SERVICE_PATH)\n execFileSync('systemctl', ['--user', 'daemon-reload'], { stdio: 'pipe' })\n log.info('systemd user service removed')\n }\n return { success: true }\n }\n\n return { success: false, error: 'Unsupported platform' }\n } catch (e) {\n const msg = (e as Error).message\n log.error({ err: msg }, 'Failed to uninstall auto-start')\n return { success: false, error: msg }\n }\n}\n\nexport function isAutoStartInstalled(): boolean {\n if (process.platform === 'darwin') {\n return fs.existsSync(LAUNCHD_PLIST_PATH)\n }\n if (process.platform === 'linux') {\n return fs.existsSync(SYSTEMD_SERVICE_PATH)\n }\n return false\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,527 @@
1
+ import {
2
+ installAutoStart,
3
+ isAutoStartInstalled,
4
+ isAutoStartSupported,
5
+ uninstallAutoStart
6
+ } from "./chunk-PQRVTUNH.js";
7
+ import {
8
+ validateBotToken,
9
+ validateChatId
10
+ } from "./chunk-4BN7NSKB.js";
11
+ import {
12
+ expandHome
13
+ } from "./chunk-FGXG3H3F.js";
14
+
15
+ // src/core/config-editor.ts
16
+ import { select, input } from "@inquirer/prompts";
17
+ var c = {
18
+ reset: "\x1B[0m",
19
+ bold: "\x1B[1m",
20
+ dim: "\x1B[2m",
21
+ green: "\x1B[32m",
22
+ yellow: "\x1B[33m",
23
+ cyan: "\x1B[36m"
24
+ };
25
+ var ok = (msg) => `${c.green}${c.bold}\u2713${c.reset} ${c.green}${msg}${c.reset}`;
26
+ var warn = (msg) => `${c.yellow}\u26A0 ${msg}${c.reset}`;
27
+ var dim = (msg) => `${c.dim}${msg}${c.reset}`;
28
+ var header = (title) => `
29
+ ${c.cyan}${c.bold}[${title}]${c.reset}
30
+ `;
31
+ async function editTelegram(config, updates) {
32
+ const tg = config.channels?.telegram ?? {};
33
+ const currentToken = tg.botToken ?? "";
34
+ const currentChatId = tg.chatId ?? 0;
35
+ console.log(header("Telegram"));
36
+ const tokenDisplay = currentToken.length > 12 ? currentToken.slice(0, 6) + "..." + currentToken.slice(-6) : currentToken || dim("(not set)");
37
+ console.log(` Bot Token : ${tokenDisplay}`);
38
+ console.log(` Chat ID : ${currentChatId || dim("(not set)")}`);
39
+ console.log("");
40
+ while (true) {
41
+ const choice = await select({
42
+ message: "Telegram settings:",
43
+ choices: [
44
+ { name: "Change Bot Token", value: "token" },
45
+ { name: "Change Chat ID", value: "chatid" },
46
+ { name: "Back", value: "back" }
47
+ ]
48
+ });
49
+ if (choice === "back") break;
50
+ if (choice === "token") {
51
+ const token = await input({
52
+ message: "New bot token:",
53
+ default: currentToken,
54
+ validate: (val) => val.trim().length > 0 || "Token cannot be empty"
55
+ });
56
+ const result = await validateBotToken(token.trim());
57
+ if (result.ok) {
58
+ console.log(ok(`Connected to @${result.botUsername}`));
59
+ } else {
60
+ console.log(warn(`Validation failed: ${result.error} \u2014 saving anyway`));
61
+ }
62
+ if (!updates.channels) updates.channels = {};
63
+ if (!updates.channels.telegram) {
64
+ updates.channels.telegram = {};
65
+ }
66
+ ;
67
+ updates.channels.telegram.botToken = token.trim();
68
+ }
69
+ if (choice === "chatid") {
70
+ const chatIdStr = await input({
71
+ message: "New chat ID (e.g. -1001234567890):",
72
+ default: String(currentChatId),
73
+ validate: (val) => {
74
+ const n = Number(val.trim());
75
+ if (isNaN(n) || !Number.isInteger(n)) return "Chat ID must be an integer";
76
+ return true;
77
+ }
78
+ });
79
+ const chatId = Number(chatIdStr.trim());
80
+ const tokenForValidation = (() => {
81
+ if (updates.channels) {
82
+ const ch = updates.channels;
83
+ if (ch.telegram) {
84
+ const tgUp = ch.telegram;
85
+ if (typeof tgUp.botToken === "string") return tgUp.botToken;
86
+ }
87
+ }
88
+ return currentToken;
89
+ })();
90
+ const result = await validateChatId(tokenForValidation, chatId);
91
+ if (result.ok) {
92
+ console.log(ok(`Group: ${result.title}${result.isForum ? "" : warn(" (topics not enabled)")}`));
93
+ } else {
94
+ console.log(warn(`Validation failed: ${result.error} \u2014 saving anyway`));
95
+ }
96
+ if (!updates.channels) updates.channels = {};
97
+ if (!updates.channels.telegram) {
98
+ updates.channels.telegram = {};
99
+ }
100
+ ;
101
+ updates.channels.telegram.chatId = chatId;
102
+ }
103
+ }
104
+ }
105
+ async function editAgent(config, updates) {
106
+ const agentNames = Object.keys(config.agents ?? {});
107
+ const currentDefault = config.defaultAgent;
108
+ console.log(header("Agent"));
109
+ console.log(` Default agent : ${c.bold}${currentDefault}${c.reset}`);
110
+ console.log(` Available : ${agentNames.join(", ") || dim("(none)")}`);
111
+ console.log("");
112
+ while (true) {
113
+ const choice = await select({
114
+ message: "Agent settings:",
115
+ choices: [
116
+ { name: "Change default agent", value: "default" },
117
+ { name: "Back", value: "back" }
118
+ ]
119
+ });
120
+ if (choice === "back") break;
121
+ if (choice === "default") {
122
+ if (agentNames.length === 0) {
123
+ console.log(warn("No agents configured."));
124
+ continue;
125
+ }
126
+ const chosen = await select({
127
+ message: "Select default agent:",
128
+ choices: agentNames.map((name) => ({ name, value: name }))
129
+ });
130
+ updates.defaultAgent = chosen;
131
+ console.log(ok(`Default agent set to ${chosen}`));
132
+ }
133
+ }
134
+ }
135
+ async function editWorkspace(config, updates) {
136
+ const currentDir = config.workspace?.baseDir ?? "~/openacp-workspace";
137
+ console.log(header("Workspace"));
138
+ console.log(` Base directory : ${currentDir}`);
139
+ console.log("");
140
+ const newDir = await input({
141
+ message: "Workspace base directory:",
142
+ default: currentDir,
143
+ validate: (val) => val.trim().length > 0 || "Path cannot be empty"
144
+ });
145
+ updates.workspace = { baseDir: newDir.trim() };
146
+ console.log(ok(`Workspace set to ${newDir.trim()}`));
147
+ }
148
+ async function editSecurity(config, updates) {
149
+ const sec = config.security ?? { allowedUserIds: [], maxConcurrentSessions: 5, sessionTimeoutMinutes: 60 };
150
+ console.log(header("Security"));
151
+ console.log(` Allowed user IDs : ${sec.allowedUserIds?.length ? sec.allowedUserIds.join(", ") : dim("(all users allowed)")}`);
152
+ console.log(` Max concurrent sessions : ${sec.maxConcurrentSessions}`);
153
+ console.log(` Session timeout (min) : ${sec.sessionTimeoutMinutes}`);
154
+ console.log("");
155
+ while (true) {
156
+ const choice = await select({
157
+ message: "Security settings:",
158
+ choices: [
159
+ { name: "Max concurrent sessions", value: "maxSessions" },
160
+ { name: "Session timeout (minutes)", value: "timeout" },
161
+ { name: "Back", value: "back" }
162
+ ]
163
+ });
164
+ if (choice === "back") break;
165
+ if (choice === "maxSessions") {
166
+ const val = await input({
167
+ message: "Max concurrent sessions:",
168
+ default: String(sec.maxConcurrentSessions),
169
+ validate: (v) => {
170
+ const n = Number(v.trim());
171
+ if (!Number.isInteger(n) || n < 1) return "Must be a positive integer";
172
+ return true;
173
+ }
174
+ });
175
+ if (!updates.security) updates.security = {};
176
+ updates.security.maxConcurrentSessions = Number(val.trim());
177
+ console.log(ok(`Max concurrent sessions set to ${val.trim()}`));
178
+ }
179
+ if (choice === "timeout") {
180
+ const val = await input({
181
+ message: "Session timeout in minutes:",
182
+ default: String(sec.sessionTimeoutMinutes),
183
+ validate: (v) => {
184
+ const n = Number(v.trim());
185
+ if (!Number.isInteger(n) || n < 1) return "Must be a positive integer";
186
+ return true;
187
+ }
188
+ });
189
+ if (!updates.security) updates.security = {};
190
+ updates.security.sessionTimeoutMinutes = Number(val.trim());
191
+ console.log(ok(`Session timeout set to ${val.trim()} minutes`));
192
+ }
193
+ }
194
+ }
195
+ async function editLogging(config, updates) {
196
+ const logging = config.logging ?? { level: "info", logDir: "~/.openacp/logs", maxFileSize: "10m", maxFiles: 7, sessionLogRetentionDays: 30 };
197
+ console.log(header("Logging"));
198
+ console.log(` Log level : ${logging.level}`);
199
+ console.log(` Log dir : ${logging.logDir}`);
200
+ console.log("");
201
+ while (true) {
202
+ const choice = await select({
203
+ message: "Logging settings:",
204
+ choices: [
205
+ { name: "Log level", value: "level" },
206
+ { name: "Log directory", value: "logDir" },
207
+ { name: "Back", value: "back" }
208
+ ]
209
+ });
210
+ if (choice === "back") break;
211
+ if (choice === "level") {
212
+ const level = await select({
213
+ message: "Select log level:",
214
+ choices: [
215
+ { name: "silent", value: "silent" },
216
+ { name: "debug", value: "debug" },
217
+ { name: "info", value: "info" },
218
+ { name: "warn", value: "warn" },
219
+ { name: "error", value: "error" },
220
+ { name: "fatal", value: "fatal" }
221
+ ]
222
+ });
223
+ if (!updates.logging) updates.logging = {};
224
+ updates.logging.level = level;
225
+ console.log(ok(`Log level set to ${level}`));
226
+ }
227
+ if (choice === "logDir") {
228
+ const dir = await input({
229
+ message: "Log directory:",
230
+ default: logging.logDir,
231
+ validate: (val) => val.trim().length > 0 || "Path cannot be empty"
232
+ });
233
+ if (!updates.logging) updates.logging = {};
234
+ updates.logging.logDir = dir.trim();
235
+ console.log(ok(`Log directory set to ${dir.trim()}`));
236
+ }
237
+ }
238
+ }
239
+ async function editRunMode(config, updates) {
240
+ const currentMode = config.runMode ?? "foreground";
241
+ const currentAutoStart = config.autoStart ?? false;
242
+ const autoStartInstalled = isAutoStartInstalled();
243
+ const autoStartSupported = isAutoStartSupported();
244
+ console.log(header("Run Mode"));
245
+ console.log(` Current mode : ${c.bold}${currentMode}${c.reset}`);
246
+ console.log(` Auto-start : ${currentAutoStart ? ok("enabled") : dim("disabled")}${autoStartInstalled ? ` ${dim("(installed)")}` : ""}`);
247
+ console.log("");
248
+ while (true) {
249
+ const isDaemon = (() => {
250
+ if ("runMode" in updates) return updates.runMode === "daemon";
251
+ return currentMode === "daemon";
252
+ })();
253
+ const choices = [
254
+ isDaemon ? { name: "Switch to foreground mode", value: "foreground" } : { name: "Switch to daemon mode", value: "daemon" }
255
+ ];
256
+ if (autoStartSupported) {
257
+ const autoStartCurrent = (() => {
258
+ if ("autoStart" in updates) return updates.autoStart;
259
+ return currentAutoStart;
260
+ })();
261
+ choices.push({
262
+ name: autoStartCurrent ? "Disable auto-start" : "Enable auto-start",
263
+ value: "toggleAutoStart"
264
+ });
265
+ }
266
+ choices.push({ name: "Back", value: "back" });
267
+ const choice = await select({
268
+ message: "Run mode settings:",
269
+ choices
270
+ });
271
+ if (choice === "back") break;
272
+ if (choice === "daemon") {
273
+ updates.runMode = "daemon";
274
+ const logDir = config.logging?.logDir ?? "~/.openacp/logs";
275
+ const result = installAutoStart(expandHome(logDir));
276
+ if (result.success) {
277
+ updates.autoStart = true;
278
+ console.log(ok("Switched to daemon mode with auto-start"));
279
+ } else {
280
+ console.log(warn(`Switched to daemon mode (auto-start failed: ${result.error})`));
281
+ }
282
+ }
283
+ if (choice === "foreground") {
284
+ updates.runMode = "foreground";
285
+ updates.autoStart = false;
286
+ uninstallAutoStart();
287
+ console.log(ok("Switched to foreground mode"));
288
+ }
289
+ if (choice === "toggleAutoStart") {
290
+ const autoStartCurrent = (() => {
291
+ if ("autoStart" in updates) return updates.autoStart;
292
+ return currentAutoStart;
293
+ })();
294
+ if (autoStartCurrent) {
295
+ const result = uninstallAutoStart();
296
+ updates.autoStart = false;
297
+ if (result.success) {
298
+ console.log(ok("Auto-start disabled"));
299
+ } else {
300
+ console.log(warn(`Auto-start uninstall failed: ${result.error}`));
301
+ }
302
+ } else {
303
+ const logDir = config.logging?.logDir ?? "~/.openacp/logs";
304
+ const result = installAutoStart(expandHome(logDir));
305
+ updates.autoStart = result.success;
306
+ if (result.success) {
307
+ console.log(ok("Auto-start enabled"));
308
+ } else {
309
+ console.log(warn(`Auto-start install failed: ${result.error}`));
310
+ }
311
+ }
312
+ }
313
+ }
314
+ }
315
+ async function editApi(config, updates) {
316
+ const api = config.api ?? { port: 21420, host: "127.0.0.1" };
317
+ console.log(header("API"));
318
+ console.log(` Port : ${api.port}`);
319
+ console.log(` Host : ${api.host} ${dim("(localhost only)")}`);
320
+ console.log("");
321
+ const newPort = await input({
322
+ message: "API port:",
323
+ default: String(api.port),
324
+ validate: (v) => {
325
+ const n = Number(v.trim());
326
+ if (!Number.isInteger(n) || n < 1 || n > 65535) return "Must be a valid port (1-65535)";
327
+ return true;
328
+ }
329
+ });
330
+ updates.api = { port: Number(newPort.trim()) };
331
+ console.log(ok(`API port set to ${newPort.trim()}`));
332
+ }
333
+ async function editTunnel(config, updates) {
334
+ const tunnel = config.tunnel ?? { enabled: false, port: 3100, provider: "cloudflare", options: {}, storeTtlMinutes: 60, auth: { enabled: false } };
335
+ const currentUpdates = updates.tunnel ?? {};
336
+ const getVal = (key, fallback) => key in currentUpdates ? currentUpdates[key] : tunnel[key] ?? fallback;
337
+ console.log(header("Tunnel"));
338
+ console.log(` Enabled : ${getVal("enabled", false) ? ok("yes") : dim("no")}`);
339
+ console.log(` Provider : ${c.bold}${getVal("provider", "cloudflare")}${c.reset}`);
340
+ console.log(` Port : ${getVal("port", 3100)}`);
341
+ const authEnabled = getVal("auth", { enabled: false }).enabled;
342
+ console.log(` Auth : ${authEnabled ? ok("enabled") : dim("disabled")}`);
343
+ console.log("");
344
+ while (true) {
345
+ const choice = await select({
346
+ message: "Tunnel settings:",
347
+ choices: [
348
+ { name: getVal("enabled", false) ? "Disable tunnel" : "Enable tunnel", value: "toggle" },
349
+ { name: "Change provider", value: "provider" },
350
+ { name: "Change port", value: "port" },
351
+ { name: "Provider options", value: "options" },
352
+ { name: authEnabled ? "Disable auth" : "Enable auth", value: "auth" },
353
+ { name: "Back", value: "back" }
354
+ ]
355
+ });
356
+ if (choice === "back") break;
357
+ if (!updates.tunnel) updates.tunnel = { ...tunnel };
358
+ const tun = updates.tunnel;
359
+ if (choice === "toggle") {
360
+ const current = getVal("enabled", false);
361
+ tun.enabled = !current;
362
+ console.log(!current ? ok("Tunnel enabled") : ok("Tunnel disabled"));
363
+ }
364
+ if (choice === "provider") {
365
+ const provider = await select({
366
+ message: "Select tunnel provider:",
367
+ choices: [
368
+ { name: "Cloudflare (default)", value: "cloudflare" },
369
+ { name: "ngrok", value: "ngrok" },
370
+ { name: "bore", value: "bore" },
371
+ { name: "Tailscale Funnel", value: "tailscale" }
372
+ ]
373
+ });
374
+ tun.provider = provider;
375
+ tun.options = {};
376
+ console.log(ok(`Provider set to ${provider}`));
377
+ }
378
+ if (choice === "port") {
379
+ const val = await input({
380
+ message: "Tunnel port:",
381
+ default: String(getVal("port", 3100)),
382
+ validate: (v) => {
383
+ const n = Number(v.trim());
384
+ if (!Number.isInteger(n) || n < 1 || n > 65535) return "Must be a valid port (1-65535)";
385
+ return true;
386
+ }
387
+ });
388
+ tun.port = Number(val.trim());
389
+ console.log(ok(`Tunnel port set to ${val.trim()}`));
390
+ }
391
+ if (choice === "options") {
392
+ const provider = getVal("provider", "cloudflare");
393
+ const currentOptions = getVal("options", {});
394
+ await editProviderOptions(provider, currentOptions, tun);
395
+ }
396
+ if (choice === "auth") {
397
+ const currentAuth = getVal("auth", { enabled: false });
398
+ if (currentAuth.enabled) {
399
+ tun.auth = { enabled: false };
400
+ console.log(ok("Tunnel auth disabled"));
401
+ } else {
402
+ const token = await input({
403
+ message: "Auth token (leave empty to auto-generate):",
404
+ default: ""
405
+ });
406
+ tun.auth = token.trim() ? { enabled: true, token: token.trim() } : { enabled: true };
407
+ console.log(ok("Tunnel auth enabled"));
408
+ }
409
+ }
410
+ }
411
+ }
412
+ async function editProviderOptions(provider, currentOptions, tun) {
413
+ if (provider === "cloudflare") {
414
+ const domain = await input({
415
+ message: "Custom domain (leave empty for random):",
416
+ default: currentOptions.domain ?? ""
417
+ });
418
+ tun.options = domain.trim() ? { domain: domain.trim() } : {};
419
+ if (domain.trim()) console.log(ok(`Domain set to ${domain.trim()}`));
420
+ else console.log(dim("Using random cloudflare domain"));
421
+ } else if (provider === "ngrok") {
422
+ const authtoken = await input({
423
+ message: "ngrok authtoken (leave empty to skip):",
424
+ default: currentOptions.authtoken ?? ""
425
+ });
426
+ const domain = await input({
427
+ message: "ngrok domain (leave empty for random):",
428
+ default: currentOptions.domain ?? ""
429
+ });
430
+ const region = await input({
431
+ message: "ngrok region (us, eu, ap \u2014 leave empty for default):",
432
+ default: currentOptions.region ?? ""
433
+ });
434
+ const opts = {};
435
+ if (authtoken.trim()) opts.authtoken = authtoken.trim();
436
+ if (domain.trim()) opts.domain = domain.trim();
437
+ if (region.trim()) opts.region = region.trim();
438
+ tun.options = opts;
439
+ console.log(ok("ngrok options saved"));
440
+ } else if (provider === "bore") {
441
+ const server = await input({
442
+ message: "bore server:",
443
+ default: currentOptions.server ?? "bore.pub"
444
+ });
445
+ const port = await input({
446
+ message: "bore port (leave empty for auto):",
447
+ default: currentOptions.port ? String(currentOptions.port) : ""
448
+ });
449
+ const secret = await input({
450
+ message: "bore secret (leave empty to skip):",
451
+ default: currentOptions.secret ?? ""
452
+ });
453
+ const opts = { server: server.trim() };
454
+ if (port.trim()) opts.port = Number(port.trim());
455
+ if (secret.trim()) opts.secret = secret.trim();
456
+ tun.options = opts;
457
+ console.log(ok("bore options saved"));
458
+ } else if (provider === "tailscale") {
459
+ const bg = await select({
460
+ message: "Run Tailscale Funnel in background?",
461
+ choices: [
462
+ { name: "No", value: "no" },
463
+ { name: "Yes", value: "yes" }
464
+ ]
465
+ });
466
+ tun.options = bg === "yes" ? { bg: true } : {};
467
+ console.log(ok("Tailscale options saved"));
468
+ } else {
469
+ console.log(dim(`No configurable options for provider "${provider}"`));
470
+ }
471
+ }
472
+ async function runConfigEditor(configManager) {
473
+ await configManager.load();
474
+ const config = configManager.get();
475
+ const updates = {};
476
+ console.log(`
477
+ ${c.cyan}${c.bold}OpenACP Config Editor${c.reset}`);
478
+ console.log(dim(`Config: ${configManager.getConfigPath()}`));
479
+ console.log("");
480
+ try {
481
+ while (true) {
482
+ const hasChanges = Object.keys(updates).length > 0;
483
+ const choice = await select({
484
+ message: `What would you like to edit?${hasChanges ? ` ${c.yellow}(unsaved changes)${c.reset}` : ""}`,
485
+ choices: [
486
+ { name: "Telegram", value: "telegram" },
487
+ { name: "Agent", value: "agent" },
488
+ { name: "Workspace", value: "workspace" },
489
+ { name: "Security", value: "security" },
490
+ { name: "Logging", value: "logging" },
491
+ { name: "Run Mode", value: "runMode" },
492
+ { name: "API", value: "api" },
493
+ { name: "Tunnel", value: "tunnel" },
494
+ { name: hasChanges ? "Save & Exit" : "Exit", value: "exit" }
495
+ ]
496
+ });
497
+ if (choice === "exit") {
498
+ if (hasChanges) {
499
+ await configManager.save(updates);
500
+ console.log(ok(`Config saved to ${configManager.getConfigPath()}`));
501
+ } else {
502
+ console.log(dim("No changes made."));
503
+ }
504
+ break;
505
+ }
506
+ if (choice === "telegram") await editTelegram(config, updates);
507
+ else if (choice === "agent") await editAgent(config, updates);
508
+ else if (choice === "workspace") await editWorkspace(config, updates);
509
+ else if (choice === "security") await editSecurity(config, updates);
510
+ else if (choice === "logging") await editLogging(config, updates);
511
+ else if (choice === "runMode") await editRunMode(config, updates);
512
+ else if (choice === "api") await editApi(config, updates);
513
+ else if (choice === "tunnel") await editTunnel(config, updates);
514
+ }
515
+ } catch (err) {
516
+ if (err.name === "ExitPromptError") {
517
+ console.log(dim("\nConfig editor cancelled. Changes discarded."));
518
+ return;
519
+ }
520
+ throw err;
521
+ }
522
+ }
523
+
524
+ export {
525
+ runConfigEditor
526
+ };
527
+ //# sourceMappingURL=chunk-QWUJIKTX.js.map