@openacp/cli 2026.330.3 → 2026.331.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -1
- package/dist/cli.d.ts +11 -0
- package/dist/cli.js +25134 -278
- package/dist/cli.js.map +1 -1
- package/dist/data/registry-snapshot.json +1 -1
- package/dist/index.d.ts +259 -30
- package/dist/index.js +17632 -404
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/adapter-AWSI4GML.js +0 -13
- package/dist/adapter-AWSI4GML.js.map +0 -1
- package/dist/agent-catalog-SZQQERV7.js +0 -10
- package/dist/agent-catalog-SZQQERV7.js.map +0 -1
- package/dist/agent-dependencies-ED2ZTUHG.js +0 -23
- package/dist/agent-dependencies-ED2ZTUHG.js.map +0 -1
- package/dist/agent-registry-YOGP656W.js +0 -8
- package/dist/agent-registry-YOGP656W.js.map +0 -1
- package/dist/agent-store-5UHZH2XI.js +0 -8
- package/dist/agent-store-5UHZH2XI.js.map +0 -1
- package/dist/api-client-XTLRRFPX.js +0 -13
- package/dist/api-client-XTLRRFPX.js.map +0 -1
- package/dist/api-server-5VNYFWJE.js +0 -7
- package/dist/api-server-5VNYFWJE.js.map +0 -1
- package/dist/api-server-JLBDKCU4.js +0 -10
- package/dist/api-server-JLBDKCU4.js.map +0 -1
- package/dist/autostart-CUPZMKKC.js +0 -22
- package/dist/autostart-CUPZMKKC.js.map +0 -1
- package/dist/chunk-237WYH6H.js +0 -235
- package/dist/chunk-237WYH6H.js.map +0 -1
- package/dist/chunk-2HEFALTZ.js +0 -44
- package/dist/chunk-2HEFALTZ.js.map +0 -1
- package/dist/chunk-2KT6TROD.js +0 -129
- package/dist/chunk-2KT6TROD.js.map +0 -1
- package/dist/chunk-2R5XM3ES.js +0 -154
- package/dist/chunk-2R5XM3ES.js.map +0 -1
- package/dist/chunk-3EWTPOF7.js +0 -51
- package/dist/chunk-3EWTPOF7.js.map +0 -1
- package/dist/chunk-3NAFXVQM.js +0 -67
- package/dist/chunk-3NAFXVQM.js.map +0 -1
- package/dist/chunk-4WXALZA3.js +0 -45
- package/dist/chunk-4WXALZA3.js.map +0 -1
- package/dist/chunk-566W6INH.js +0 -83
- package/dist/chunk-566W6INH.js.map +0 -1
- package/dist/chunk-5HKQCYOI.js +0 -145
- package/dist/chunk-5HKQCYOI.js.map +0 -1
- package/dist/chunk-5OCGO27U.js +0 -125
- package/dist/chunk-5OCGO27U.js.map +0 -1
- package/dist/chunk-5WGVYX3C.js +0 -55
- package/dist/chunk-5WGVYX3C.js.map +0 -1
- package/dist/chunk-7ZCQF6QM.js +0 -27
- package/dist/chunk-7ZCQF6QM.js.map +0 -1
- package/dist/chunk-AFKX424Q.js +0 -92
- package/dist/chunk-AFKX424Q.js.map +0 -1
- package/dist/chunk-APS6UEFU.js +0 -259
- package/dist/chunk-APS6UEFU.js.map +0 -1
- package/dist/chunk-BTJHGSLM.js +0 -1116
- package/dist/chunk-BTJHGSLM.js.map +0 -1
- package/dist/chunk-CDAUYTVP.js +0 -41
- package/dist/chunk-CDAUYTVP.js.map +0 -1
- package/dist/chunk-FCTC7KDT.js +0 -101
- package/dist/chunk-FCTC7KDT.js.map +0 -1
- package/dist/chunk-FNRSWA2K.js +0 -1
- package/dist/chunk-FNRSWA2K.js.map +0 -1
- package/dist/chunk-GEOXPGCO.js +0 -650
- package/dist/chunk-GEOXPGCO.js.map +0 -1
- package/dist/chunk-IZ5UEZF7.js +0 -138
- package/dist/chunk-IZ5UEZF7.js.map +0 -1
- package/dist/chunk-KDU3ZEWT.js +0 -97
- package/dist/chunk-KDU3ZEWT.js.map +0 -1
- package/dist/chunk-LGFWH3AE.js +0 -26
- package/dist/chunk-LGFWH3AE.js.map +0 -1
- package/dist/chunk-MITTQMGZ.js +0 -543
- package/dist/chunk-MITTQMGZ.js.map +0 -1
- package/dist/chunk-MLF4W5R6.js +0 -101
- package/dist/chunk-MLF4W5R6.js.map +0 -1
- package/dist/chunk-MPGEHTGE.js +0 -679
- package/dist/chunk-MPGEHTGE.js.map +0 -1
- package/dist/chunk-OYSAN7UX.js +0 -15
- package/dist/chunk-OYSAN7UX.js.map +0 -1
- package/dist/chunk-PA6MNBG4.js +0 -190
- package/dist/chunk-PA6MNBG4.js.map +0 -1
- package/dist/chunk-QWVHCTCA.js +0 -172
- package/dist/chunk-QWVHCTCA.js.map +0 -1
- package/dist/chunk-R6KZYF7D.js +0 -231
- package/dist/chunk-R6KZYF7D.js.map +0 -1
- package/dist/chunk-S64CB6J3.js +0 -98
- package/dist/chunk-S64CB6J3.js.map +0 -1
- package/dist/chunk-TMVTSWVH.js +0 -228
- package/dist/chunk-TMVTSWVH.js.map +0 -1
- package/dist/chunk-UCIZM5SW.js +0 -3917
- package/dist/chunk-UCIZM5SW.js.map +0 -1
- package/dist/chunk-UWH7KIAA.js +0 -701
- package/dist/chunk-UWH7KIAA.js.map +0 -1
- package/dist/chunk-V2YZWYXT.js +0 -484
- package/dist/chunk-V2YZWYXT.js.map +0 -1
- package/dist/chunk-W26AUH5B.js +0 -61
- package/dist/chunk-W26AUH5B.js.map +0 -1
- package/dist/chunk-W4LK6WJP.js +0 -446
- package/dist/chunk-W4LK6WJP.js.map +0 -1
- package/dist/chunk-WQCJTU2C.js +0 -84
- package/dist/chunk-WQCJTU2C.js.map +0 -1
- package/dist/chunk-XBZIHNKV.js +0 -6410
- package/dist/chunk-XBZIHNKV.js.map +0 -1
- package/dist/chunk-ZSLHHQPQ.js +0 -282
- package/dist/chunk-ZSLHHQPQ.js.map +0 -1
- package/dist/config-KN6NKKPF.js +0 -20
- package/dist/config-KN6NKKPF.js.map +0 -1
- package/dist/config-editor-76RVZS4B.js +0 -10
- package/dist/config-editor-76RVZS4B.js.map +0 -1
- package/dist/config-registry-ZXAIJNYB.js +0 -17
- package/dist/config-registry-ZXAIJNYB.js.map +0 -1
- package/dist/context-NXXW62NJ.js +0 -9
- package/dist/context-NXXW62NJ.js.map +0 -1
- package/dist/core-plugins-BPZY7SEB.js +0 -22
- package/dist/core-plugins-BPZY7SEB.js.map +0 -1
- package/dist/daemon-XFEMMJSZ.js +0 -29
- package/dist/daemon-XFEMMJSZ.js.map +0 -1
- package/dist/dev-loader-7P3HZCIA.js +0 -37
- package/dist/dev-loader-7P3HZCIA.js.map +0 -1
- package/dist/doctor-AV6AUO22.js +0 -9
- package/dist/doctor-AV6AUO22.js.map +0 -1
- package/dist/file-service-HHB3JQIO.js +0 -8
- package/dist/file-service-HHB3JQIO.js.map +0 -1
- package/dist/install-cloudflared-JRJ4BSOM.js +0 -32
- package/dist/install-cloudflared-JRJ4BSOM.js.map +0 -1
- package/dist/install-context-EHYV5WRY.js +0 -77
- package/dist/install-context-EHYV5WRY.js.map +0 -1
- package/dist/install-jq-ISTGT263.js +0 -31
- package/dist/install-jq-ISTGT263.js.map +0 -1
- package/dist/integrate-JIEZYDOR.js +0 -371
- package/dist/integrate-JIEZYDOR.js.map +0 -1
- package/dist/log-YZ243M5G.js +0 -25
- package/dist/log-YZ243M5G.js.map +0 -1
- package/dist/main-VEJCG5PY.js +0 -654
- package/dist/main-VEJCG5PY.js.map +0 -1
- package/dist/menu-ALFN37IR.js +0 -15
- package/dist/menu-ALFN37IR.js.map +0 -1
- package/dist/notifications-MO23S7S3.js +0 -8
- package/dist/notifications-MO23S7S3.js.map +0 -1
- package/dist/plugin-create-EHL76ZZG.js +0 -966
- package/dist/plugin-create-EHL76ZZG.js.map +0 -1
- package/dist/plugin-installer-VSTYZSXC.js +0 -9
- package/dist/plugin-installer-VSTYZSXC.js.map +0 -1
- package/dist/plugin-registry-6J3YSFHF.js +0 -7
- package/dist/plugin-registry-6J3YSFHF.js.map +0 -1
- package/dist/plugin-search-MGKAL5JM.js +0 -39
- package/dist/plugin-search-MGKAL5JM.js.map +0 -1
- package/dist/post-upgrade-Y26S2ZQ7.js +0 -79
- package/dist/post-upgrade-Y26S2ZQ7.js.map +0 -1
- package/dist/read-text-file-DJBTITIB.js +0 -7
- package/dist/read-text-file-DJBTITIB.js.map +0 -1
- package/dist/registry-client-GTBWLXYU.js +0 -7
- package/dist/registry-client-GTBWLXYU.js.map +0 -1
- package/dist/security-2BA265LN.js +0 -8
- package/dist/security-2BA265LN.js.map +0 -1
- package/dist/settings-manager-B4UN2LAC.js +0 -7
- package/dist/settings-manager-B4UN2LAC.js.map +0 -1
- package/dist/setup-DISPNDEK.js +0 -802
- package/dist/setup-DISPNDEK.js.map +0 -1
- package/dist/speech-SG62JYIF.js +0 -9
- package/dist/speech-SG62JYIF.js.map +0 -1
- package/dist/suggest-RST5VOHB.js +0 -36
- package/dist/suggest-RST5VOHB.js.map +0 -1
- package/dist/telegram-L3YM6SQJ.js +0 -7
- package/dist/telegram-L3YM6SQJ.js.map +0 -1
- package/dist/tunnel-HWJ27WDH.js +0 -7
- package/dist/tunnel-HWJ27WDH.js.map +0 -1
- package/dist/tunnel-service-ZMO4THKE.js +0 -1261
- package/dist/tunnel-service-ZMO4THKE.js.map +0 -1
- package/dist/validators-GITLOFXC.js +0 -11
- package/dist/validators-GITLOFXC.js.map +0 -1
- package/dist/version-AXXV6IV2.js +0 -15
- package/dist/version-AXXV6IV2.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/config/config-editor.ts"],"sourcesContent":["import * as os from 'node:os'\nimport * as path from 'node:path'\nimport * as clack from '@clack/prompts'\nimport type { Config, ConfigManager } from './config.js'\n\n// Compatibility wrappers — convert @inquirer/prompts API to @clack/prompts\nasync function select<T extends string>(opts: { message: string; choices: Array<{ name: string; value: T; description?: string }>; default?: T }): Promise<T> {\n const result = await clack.select({\n message: opts.message,\n options: opts.choices.map(ch => ({ label: ch.name, value: ch.value, hint: ch.description })) as any,\n initialValue: opts.default,\n })\n if (clack.isCancel(result)) { clack.cancel('Cancelled.'); process.exit(0) }\n return result as T\n}\n\nasync function input(opts: { message: string; default?: string; validate?: (val: string) => string | boolean }): Promise<string> {\n const result = await clack.text({\n message: opts.message,\n initialValue: opts.default,\n validate: opts.validate ? (val) => {\n const r = opts.validate!((val ?? \"\") as string)\n if (r === true || r === undefined) return undefined\n if (typeof r === 'string') return r\n return undefined\n } : undefined,\n })\n if (clack.isCancel(result)) { clack.cancel('Cancelled.'); process.exit(0) }\n return result as string\n}\nimport { installAutoStart, uninstallAutoStart, isAutoStartInstalled, isAutoStartSupported } from '../../cli/autostart.js'\nimport { expandHome } from './config.js'\n\n// ANSI color helpers\nconst c = {\n reset: '\\x1b[0m',\n bold: '\\x1b[1m',\n dim: '\\x1b[2m',\n green: '\\x1b[32m',\n yellow: '\\x1b[33m',\n cyan: '\\x1b[36m',\n}\n\nconst ok = (msg: string) => `${c.green}${c.bold}✓${c.reset} ${c.green}${msg}${c.reset}`\nconst warn = (msg: string) => `${c.yellow}⚠ ${msg}${c.reset}`\nconst dim = (msg: string) => `${c.dim}${msg}${c.reset}`\nconst header = (title: string) => `\\n${c.cyan}${c.bold}[${title}]${c.reset}\\n`\n\ntype ConfigUpdates = Record<string, unknown>\n\n// --- Edit: Telegram ---\n\nasync function editTelegram(config: Config, updates: ConfigUpdates): Promise<void> {\n const tg = (config.channels?.telegram ?? {}) as Record<string, unknown>\n const currentToken = (tg.botToken as string) ?? ''\n const currentChatId = (tg.chatId as number) ?? 0\n const currentEnabled = (tg.enabled as boolean) ?? false\n\n console.log(header('Telegram'))\n console.log(` Enabled : ${currentEnabled ? ok('yes') : dim('no')}`)\n const tokenDisplay = currentToken.length > 12\n ? currentToken.slice(0, 6) + '...' + currentToken.slice(-6)\n : currentToken || dim('(not set)')\n console.log(` Bot Token : ${tokenDisplay}`)\n console.log(` Chat ID : ${currentChatId || dim('(not set)')}`)\n console.log('')\n\n const ensureTelegramUpdates = () => {\n if (!updates.channels) updates.channels = {}\n if (!(updates.channels as Record<string, unknown>).telegram) {\n (updates.channels as Record<string, unknown>).telegram = {}\n }\n return (updates.channels as Record<string, unknown>).telegram as Record<string, unknown>\n }\n\n while (true) {\n const isEnabled = (() => {\n const ch = updates.channels as Record<string, unknown> | undefined\n const tgUp = ch?.telegram as Record<string, unknown> | undefined\n if (tgUp && 'enabled' in tgUp) return tgUp.enabled as boolean\n return currentEnabled\n })()\n\n const choice = await select({\n message: 'Telegram settings:',\n choices: [\n { name: isEnabled ? 'Disable Telegram' : 'Enable Telegram', value: 'toggle' },\n { name: 'Change Bot Token', value: 'token' },\n { name: 'Change Chat ID', value: 'chatid' },\n { name: 'Back', value: 'back' },\n ],\n })\n\n if (choice === 'back') break\n\n if (choice === 'toggle') {\n const tgUp = ensureTelegramUpdates()\n tgUp.enabled = !isEnabled\n console.log(!isEnabled ? ok('Telegram enabled') : ok('Telegram disabled'))\n }\n\n if (choice === 'token') {\n const token = await input({\n message: 'New bot token:',\n default: currentToken,\n validate: (val) => val.trim().length > 0 || 'Token cannot be empty',\n })\n\n try {\n const { validateBotToken } = await import('../../plugins/telegram/validators.js')\n const result = await validateBotToken(token.trim())\n if (result.ok) {\n console.log(ok(`Connected to @${result.botUsername}`))\n } else {\n console.log(warn(`Validation failed: ${result.error} — saving anyway`))\n }\n } catch {\n console.log(warn('Telegram validator not available — skipping validation'))\n }\n\n const tgUp = ensureTelegramUpdates()\n tgUp.botToken = token.trim()\n tgUp.enabled = true\n }\n\n if (choice === 'chatid') {\n const chatIdStr = await input({\n message: 'New chat ID (e.g. -1001234567890):',\n default: String(currentChatId),\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 })\n\n const chatId = Number(chatIdStr.trim())\n\n // Use the current (or already-updated) token for validation\n const tokenForValidation = (() => {\n const ch = updates.channels as Record<string, unknown> | undefined\n const tgUp = ch?.telegram as Record<string, unknown> | undefined\n if (typeof tgUp?.botToken === 'string') return tgUp.botToken\n return currentToken\n })()\n\n try {\n const { validateChatId } = await import('../../plugins/telegram/validators.js')\n const result = await validateChatId(tokenForValidation, chatId)\n if (result.ok) {\n console.log(ok(`Group: ${result.title}${result.isForum ? '' : warn(' (topics not enabled)')}`))\n } else {\n console.log(warn(`Validation failed: ${result.error} — saving anyway`))\n }\n } catch {\n console.log(warn('Telegram validator not available — skipping validation'))\n }\n\n const tgUp = ensureTelegramUpdates()\n tgUp.chatId = chatId\n }\n }\n}\n\n// --- Edit: Discord (delegates to plugin's configure()) ---\n\nconst DISCORD_PACKAGE = '@openacp/adapter-discord'\n\nasync function ensureDiscordPlugin(): Promise<any | null> {\n try {\n return await import(DISCORD_PACKAGE)\n } catch {\n const shouldInstall = await select({\n message: `${DISCORD_PACKAGE} is not installed. Install it now?`,\n choices: [\n { name: 'Yes, install now', value: 'yes' },\n { name: 'No, skip', value: 'no' },\n ],\n })\n if (shouldInstall === 'no') {\n console.log(warn(`Install later with: openacp plugin add ${DISCORD_PACKAGE}`))\n return null\n }\n try {\n console.log(dim(`Installing ${DISCORD_PACKAGE}...`))\n const { installNpmPlugin } = await import('../plugin/plugin-installer.js')\n const mod = await installNpmPlugin(DISCORD_PACKAGE)\n console.log(ok(`${DISCORD_PACKAGE} installed`))\n return mod\n } catch (err) {\n console.log(warn(`Failed to install: ${(err as Error).message}`))\n return null\n }\n }\n}\n\nasync function editDiscord(_config: Config, _updates: ConfigUpdates): Promise<void> {\n const pluginModule = await ensureDiscordPlugin()\n if (!pluginModule) return\n\n const plugin = pluginModule.default\n if (plugin?.configure) {\n const { SettingsManager } = await import('../plugin/settings-manager.js')\n const { createInstallContext } = await import('../plugin/install-context.js')\n const basePath = path.join(os.homedir(), '.openacp', 'plugins')\n const settingsManager = new SettingsManager(basePath)\n const ctx = createInstallContext({\n pluginName: plugin.name,\n settingsManager,\n basePath,\n })\n await plugin.configure(ctx)\n } else {\n console.log(warn('This plugin does not have a configure() method yet.'))\n }\n}\n\n// --- Edit: Channels (parent menu) ---\n\nasync function editChannels(config: Config, updates: ConfigUpdates): Promise<void> {\n const tgEnabled = (config.channels?.telegram as Record<string, unknown>)?.enabled !== false && config.channels?.telegram\n const dcEnabled = (config.channels?.discord as Record<string, unknown>)?.enabled !== false && config.channels?.discord\n\n console.log(header('Channels'))\n console.log(` Telegram : ${tgEnabled ? ok('configured') : dim('not configured')}`)\n console.log(` Discord : ${dcEnabled ? ok('configured') : dim('not configured')}`)\n console.log('')\n\n while (true) {\n const choice = await select({\n message: 'Channel settings:',\n choices: [\n { name: 'Telegram', value: 'telegram' },\n { name: 'Discord', value: 'discord' },\n { name: 'Back', value: 'back' },\n ],\n })\n\n if (choice === 'back') break\n\n if (choice === 'telegram') await editTelegram(config, updates)\n if (choice === 'discord') await editDiscord(config, updates)\n }\n}\n\n// --- Edit: Agent ---\n\nasync function editAgent(config: Config, updates: ConfigUpdates): Promise<void> {\n const agentNames = Object.keys(config.agents ?? {})\n const currentDefault = config.defaultAgent\n\n console.log(header('Agent'))\n console.log(` Default agent : ${c.bold}${currentDefault}${c.reset}`)\n console.log(` Available : ${agentNames.join(', ') || dim('(none)')}`)\n console.log('')\n\n while (true) {\n const choice = await select({\n message: 'Agent settings:',\n choices: [\n { name: 'Change default agent', value: 'default' },\n { name: 'Back', value: 'back' },\n ],\n })\n\n if (choice === 'back') break\n\n if (choice === 'default') {\n if (agentNames.length === 0) {\n console.log(warn('No agents configured.'))\n continue\n }\n\n const chosen = await select({\n message: 'Select default agent:',\n choices: agentNames.map((name) => ({ name, value: name })),\n })\n\n updates.defaultAgent = chosen\n console.log(ok(`Default agent set to ${chosen}`))\n }\n }\n}\n\n// --- Edit: Workspace ---\n\nasync function editWorkspace(config: Config, updates: ConfigUpdates): Promise<void> {\n const currentDir = config.workspace?.baseDir ?? '~/openacp-workspace'\n\n console.log(header('Workspace'))\n console.log(` Base directory : ${currentDir}`)\n console.log('')\n\n const newDir = await input({\n message: 'Workspace base directory:',\n default: currentDir,\n validate: (val) => val.trim().length > 0 || 'Path cannot be empty',\n })\n\n updates.workspace = { baseDir: newDir.trim() }\n console.log(ok(`Workspace set to ${newDir.trim()}`))\n}\n\n// --- Edit: Security ---\n\nasync function editSecurity(config: Config, updates: ConfigUpdates): Promise<void> {\n const sec = config.security ?? { allowedUserIds: [], maxConcurrentSessions: 20, sessionTimeoutMinutes: 60 }\n\n console.log(header('Security'))\n console.log(` Allowed user IDs : ${sec.allowedUserIds?.length ? sec.allowedUserIds.join(', ') : dim('(all users allowed)')}`)\n console.log(` Max concurrent sessions : ${sec.maxConcurrentSessions}`)\n console.log(` Session timeout (min) : ${sec.sessionTimeoutMinutes}`)\n console.log('')\n\n while (true) {\n const choice = await select({\n message: 'Security settings:',\n choices: [\n { name: 'Max concurrent sessions', value: 'maxSessions' },\n { name: 'Session timeout (minutes)', value: 'timeout' },\n { name: 'Back', value: 'back' },\n ],\n })\n\n if (choice === 'back') break\n\n if (choice === 'maxSessions') {\n const val = await input({\n message: 'Max concurrent sessions:',\n default: String(sec.maxConcurrentSessions),\n validate: (v) => {\n const n = Number(v.trim())\n if (!Number.isInteger(n) || n < 1) return 'Must be a positive integer'\n return true\n },\n })\n\n if (!updates.security) updates.security = {}\n ;(updates.security as Record<string, unknown>).maxConcurrentSessions = Number(val.trim())\n console.log(ok(`Max concurrent sessions set to ${val.trim()}`))\n }\n\n if (choice === 'timeout') {\n const val = await input({\n message: 'Session timeout in minutes:',\n default: String(sec.sessionTimeoutMinutes),\n validate: (v) => {\n const n = Number(v.trim())\n if (!Number.isInteger(n) || n < 1) return 'Must be a positive integer'\n return true\n },\n })\n\n if (!updates.security) updates.security = {}\n ;(updates.security as Record<string, unknown>).sessionTimeoutMinutes = Number(val.trim())\n console.log(ok(`Session timeout set to ${val.trim()} minutes`))\n }\n }\n}\n\n// --- Edit: Logging ---\n\nasync function editLogging(config: Config, updates: ConfigUpdates): Promise<void> {\n const logging = config.logging ?? { level: 'info', logDir: '~/.openacp/logs', maxFileSize: '10m', maxFiles: 7, sessionLogRetentionDays: 30 }\n\n console.log(header('Logging'))\n console.log(` Log level : ${logging.level}`)\n console.log(` Log dir : ${logging.logDir}`)\n console.log('')\n\n while (true) {\n const choice = await select({\n message: 'Logging settings:',\n choices: [\n { name: 'Log level', value: 'level' },\n { name: 'Log directory', value: 'logDir' },\n { name: 'Back', value: 'back' },\n ],\n })\n\n if (choice === 'back') break\n\n if (choice === 'level') {\n const level = await select({\n message: 'Select log level:',\n choices: [\n { name: 'silent', value: 'silent' },\n { name: 'debug', value: 'debug' },\n { name: 'info', value: 'info' },\n { name: 'warn', value: 'warn' },\n { name: 'error', value: 'error' },\n { name: 'fatal', value: 'fatal' },\n ],\n })\n\n if (!updates.logging) updates.logging = {}\n ;(updates.logging as Record<string, unknown>).level = level\n console.log(ok(`Log level set to ${level}`))\n }\n\n if (choice === 'logDir') {\n const dir = await input({\n message: 'Log directory:',\n default: logging.logDir,\n validate: (val) => val.trim().length > 0 || 'Path cannot be empty',\n })\n\n if (!updates.logging) updates.logging = {}\n ;(updates.logging as Record<string, unknown>).logDir = dir.trim()\n console.log(ok(`Log directory set to ${dir.trim()}`))\n }\n }\n}\n\n// --- Edit: Run Mode ---\n\nasync function editRunMode(config: Config, updates: ConfigUpdates): Promise<void> {\n const currentMode = config.runMode ?? 'foreground'\n const currentAutoStart = config.autoStart ?? false\n const autoStartInstalled = isAutoStartInstalled()\n const autoStartSupported = isAutoStartSupported()\n\n console.log(header('Run Mode'))\n console.log(` Current mode : ${c.bold}${currentMode}${c.reset}`)\n console.log(` Auto-start : ${currentAutoStart ? ok('enabled') : dim('disabled')}${autoStartInstalled ? ` ${dim('(installed)')}` : ''}`)\n console.log('')\n\n while (true) {\n const isDaemon = (() => {\n if ('runMode' in updates) return updates.runMode === 'daemon'\n return currentMode === 'daemon'\n })()\n\n const choices = [\n isDaemon\n ? { name: 'Switch to foreground mode', value: 'foreground' }\n : { name: 'Switch to daemon mode', value: 'daemon' },\n ]\n\n if (autoStartSupported) {\n const autoStartCurrent = (() => {\n if ('autoStart' in updates) return updates.autoStart as boolean\n return currentAutoStart\n })()\n choices.push({\n name: autoStartCurrent ? 'Disable auto-start' : 'Enable auto-start',\n value: 'toggleAutoStart',\n })\n }\n\n choices.push({ name: 'Back', value: 'back' })\n\n const choice = await select({\n message: 'Run mode settings:',\n choices,\n })\n\n if (choice === 'back') break\n\n if (choice === 'daemon') {\n updates.runMode = 'daemon'\n const logDir = (config.logging?.logDir) ?? '~/.openacp/logs'\n const result = installAutoStart(expandHome(logDir))\n if (result.success) {\n updates.autoStart = true\n console.log(ok('Switched to daemon mode with auto-start'))\n } else {\n console.log(warn(`Switched to daemon mode (auto-start failed: ${result.error})`))\n }\n }\n\n if (choice === 'foreground') {\n updates.runMode = 'foreground'\n updates.autoStart = false\n uninstallAutoStart()\n console.log(ok('Switched to foreground mode'))\n }\n\n if (choice === 'toggleAutoStart') {\n const autoStartCurrent = (() => {\n if ('autoStart' in updates) return updates.autoStart as boolean\n return currentAutoStart\n })()\n\n if (autoStartCurrent) {\n const result = uninstallAutoStart()\n updates.autoStart = false\n if (result.success) {\n console.log(ok('Auto-start disabled'))\n } else {\n console.log(warn(`Auto-start uninstall failed: ${result.error}`))\n }\n } else {\n const logDir = (config.logging?.logDir) ?? '~/.openacp/logs'\n const result = installAutoStart(expandHome(logDir))\n updates.autoStart = result.success\n if (result.success) {\n console.log(ok('Auto-start enabled'))\n } else {\n console.log(warn(`Auto-start install failed: ${result.error}`))\n }\n }\n }\n }\n}\n\n// --- Edit: API ---\n\nasync function editApi(config: Config, updates: ConfigUpdates): Promise<void> {\n const api = config.api ?? { port: 21420, host: '127.0.0.1' }\n\n console.log(header('API'))\n console.log(` Port : ${api.port}`)\n console.log(` Host : ${api.host} ${dim('(localhost only)')}`)\n console.log('')\n\n const newPort = await input({\n message: 'API port:',\n default: String(api.port),\n validate: (v) => {\n const n = Number(v.trim())\n if (!Number.isInteger(n) || n < 1 || n > 65535) return 'Must be a valid port (1-65535)'\n return true\n },\n })\n\n updates.api = { port: Number(newPort.trim()) }\n console.log(ok(`API port set to ${newPort.trim()}`))\n}\n\n// --- Edit: Tunnel ---\n\nasync function editTunnel(config: Config, updates: ConfigUpdates): Promise<void> {\n const tunnel = config.tunnel ?? { enabled: false, port: 3100, provider: 'cloudflare', options: {}, storeTtlMinutes: 60, auth: { enabled: false } }\n const currentUpdates = (updates.tunnel ?? {}) as Record<string, unknown>\n\n const getVal = <T>(key: string, fallback: T): T =>\n (key in currentUpdates ? currentUpdates[key] : (tunnel as Record<string, unknown>)[key] ?? fallback) as T\n\n console.log(header('Tunnel'))\n console.log(` Enabled : ${getVal('enabled', false) ? ok('yes') : dim('no')}`)\n console.log(` Provider : ${c.bold}${getVal('provider', 'cloudflare')}${c.reset}`)\n console.log(` Port : ${getVal('port', 3100)}`)\n const authEnabled = (getVal('auth', { enabled: false }) as { enabled: boolean }).enabled\n console.log(` Auth : ${authEnabled ? ok('enabled') : dim('disabled')}`)\n console.log('')\n\n while (true) {\n const choice = await select({\n message: 'Tunnel settings:',\n choices: [\n { name: getVal('enabled', false) ? 'Disable tunnel' : 'Enable tunnel', value: 'toggle' },\n { name: 'Change provider', value: 'provider' },\n { name: 'Change port', value: 'port' },\n { name: 'Provider options', value: 'options' },\n { name: authEnabled ? 'Disable auth' : 'Enable auth', value: 'auth' },\n { name: 'Back', value: 'back' },\n ],\n })\n\n if (choice === 'back') break\n\n if (!updates.tunnel) updates.tunnel = { ...tunnel }\n const tun = updates.tunnel as Record<string, unknown>\n\n if (choice === 'toggle') {\n const current = getVal('enabled', false)\n tun.enabled = !current\n console.log(!current ? ok('Tunnel enabled') : ok('Tunnel disabled'))\n }\n\n if (choice === 'provider') {\n const provider = await select({\n message: 'Select tunnel provider:',\n choices: [\n { name: 'Cloudflare (default)', value: 'cloudflare' },\n { name: 'ngrok', value: 'ngrok' },\n { name: 'bore', value: 'bore' },\n { name: 'Tailscale Funnel', value: 'tailscale' },\n ],\n })\n tun.provider = provider\n tun.options = {} // reset options when switching provider\n console.log(ok(`Provider set to ${provider}`))\n }\n\n if (choice === 'port') {\n const val = await input({\n message: 'Tunnel port:',\n default: String(getVal('port', 3100)),\n validate: (v) => {\n const n = Number(v.trim())\n if (!Number.isInteger(n) || n < 1 || n > 65535) return 'Must be a valid port (1-65535)'\n return true\n },\n })\n tun.port = Number(val.trim())\n console.log(ok(`Tunnel port set to ${val.trim()}`))\n }\n\n if (choice === 'options') {\n const provider = getVal('provider', 'cloudflare')\n const currentOptions = getVal('options', {}) as Record<string, unknown>\n await editProviderOptions(provider, currentOptions, tun)\n }\n\n if (choice === 'auth') {\n const currentAuth = getVal('auth', { enabled: false }) as { enabled: boolean; token?: string }\n if (currentAuth.enabled) {\n tun.auth = { enabled: false }\n console.log(ok('Tunnel auth disabled'))\n } else {\n const token = await input({\n message: 'Auth token (leave empty to auto-generate):',\n default: '',\n })\n tun.auth = token.trim()\n ? { enabled: true, token: token.trim() }\n : { enabled: true }\n console.log(ok('Tunnel auth enabled'))\n }\n }\n }\n}\n\nasync function editProviderOptions(\n provider: string,\n currentOptions: Record<string, unknown>,\n tun: Record<string, unknown>,\n): Promise<void> {\n if (provider === 'cloudflare') {\n const domain = await input({\n message: 'Custom domain (leave empty for random):',\n default: (currentOptions.domain as string) ?? '',\n })\n tun.options = domain.trim() ? { domain: domain.trim() } : {}\n if (domain.trim()) console.log(ok(`Domain set to ${domain.trim()}`))\n else console.log(dim('Using random cloudflare domain'))\n } else if (provider === 'ngrok') {\n const authtoken = await input({\n message: 'ngrok authtoken (leave empty to skip):',\n default: (currentOptions.authtoken as string) ?? '',\n })\n const domain = await input({\n message: 'ngrok domain (leave empty for random):',\n default: (currentOptions.domain as string) ?? '',\n })\n const region = await input({\n message: 'ngrok region (us, eu, ap — leave empty for default):',\n default: (currentOptions.region as string) ?? '',\n })\n const opts: Record<string, string> = {}\n if (authtoken.trim()) opts.authtoken = authtoken.trim()\n if (domain.trim()) opts.domain = domain.trim()\n if (region.trim()) opts.region = region.trim()\n tun.options = opts\n console.log(ok('ngrok options saved'))\n } else if (provider === 'bore') {\n const server = await input({\n message: 'bore server:',\n default: (currentOptions.server as string) ?? 'bore.pub',\n })\n const port = await input({\n message: 'bore port (leave empty for auto):',\n default: currentOptions.port ? String(currentOptions.port) : '',\n })\n const secret = await input({\n message: 'bore secret (leave empty to skip):',\n default: (currentOptions.secret as string) ?? '',\n })\n const opts: Record<string, unknown> = { server: server.trim() }\n if (port.trim()) opts.port = Number(port.trim())\n if (secret.trim()) opts.secret = secret.trim()\n tun.options = opts\n console.log(ok('bore options saved'))\n } else if (provider === 'tailscale') {\n const bg = await select({\n message: 'Run Tailscale Funnel in background?',\n choices: [\n { name: 'No', value: 'no' },\n { name: 'Yes', value: 'yes' },\n ],\n })\n tun.options = bg === 'yes' ? { bg: true } : {}\n console.log(ok('Tailscale options saved'))\n } else {\n console.log(dim(`No configurable options for provider \"${provider}\"`))\n }\n}\n\n// --- Main Config Editor ---\n\nexport async function runConfigEditor(\n configManager: ConfigManager,\n mode: 'file' | 'api' = 'file',\n apiPort?: number,\n): Promise<void> {\n await configManager.load()\n const config = configManager.get()\n const updates: ConfigUpdates = {}\n\n console.log(`\\n${c.cyan}${c.bold}OpenACP Config Editor${c.reset}`)\n console.log(dim(`Config: ${configManager.getConfigPath()}`))\n console.log('')\n\n try {\n while (true) {\n const hasChanges = mode === 'file' ? Object.keys(updates).length > 0 : false\n const choice = await select({\n message: `What would you like to edit?${hasChanges ? ` ${c.yellow}(unsaved changes)${c.reset}` : ''}`,\n choices: [\n { name: 'Channels', value: 'channels' },\n { name: 'Agent', value: 'agent' },\n { name: 'Workspace', value: 'workspace' },\n { name: 'Security', value: 'security' },\n { name: 'Logging', value: 'logging' },\n { name: 'Run Mode', value: 'runMode' },\n { name: 'API', value: 'api' },\n { name: 'Tunnel', value: 'tunnel' },\n { name: hasChanges ? 'Save & Exit' : 'Exit', value: 'exit' },\n ],\n })\n\n if (choice === 'exit') {\n if (mode === 'file' && hasChanges) {\n await configManager.save(updates)\n console.log(ok(`Config saved to ${configManager.getConfigPath()}`))\n } else if (mode === 'file') {\n console.log(dim('No changes made.'))\n }\n break\n }\n\n const sectionUpdates: ConfigUpdates = {}\n\n if (choice === 'channels') await editChannels(config, sectionUpdates)\n else if (choice === 'agent') await editAgent(config, sectionUpdates)\n else if (choice === 'workspace') await editWorkspace(config, sectionUpdates)\n else if (choice === 'security') await editSecurity(config, sectionUpdates)\n else if (choice === 'logging') await editLogging(config, sectionUpdates)\n else if (choice === 'runMode') await editRunMode(config, sectionUpdates)\n else if (choice === 'api') await editApi(config, sectionUpdates)\n else if (choice === 'tunnel') await editTunnel(config, sectionUpdates)\n\n if (mode === 'api' && Object.keys(sectionUpdates).length > 0) {\n await sendConfigViaApi(apiPort!, sectionUpdates)\n // Refresh in-memory config\n await configManager.load()\n Object.assign(config, configManager.get())\n } else {\n // Accumulate for file mode\n Object.assign(updates, sectionUpdates)\n }\n }\n } catch (err) {\n if ((err as Error).name === 'ExitPromptError') {\n console.log(dim('\\nConfig editor cancelled. Changes discarded.'))\n return\n }\n throw err\n }\n}\n\nasync function sendConfigViaApi(port: number, updates: ConfigUpdates): Promise<void> {\n const { apiCall: call } = await import('../../cli/api-client.js')\n\n const paths = flattenToPaths(updates)\n for (const { path, value } of paths) {\n const res = await call(port, '/api/config', {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ path, value }),\n })\n const data = await res.json() as Record<string, unknown>\n if (!res.ok) {\n console.log(warn(`Failed to update ${path}: ${data.error}`))\n } else if (data.needsRestart) {\n console.log(warn(`${path} updated — restart required`))\n }\n }\n}\n\nfunction flattenToPaths(obj: Record<string, unknown>, prefix = ''): Array<{ path: string; value: unknown }> {\n const result: Array<{ path: string; value: unknown }> = []\n for (const [key, val] of Object.entries(obj)) {\n const fullPath = prefix ? `${prefix}.${key}` : key\n if (val && typeof val === 'object' && !Array.isArray(val)) {\n result.push(...flattenToPaths(val as Record<string, unknown>, fullPath))\n } else {\n result.push({ path: fullPath, value: val })\n }\n }\n return result\n}\n"],"mappings":"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","names":["select","tgUp","path"]}
|
package/dist/chunk-OYSAN7UX.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
// src/core/utils/read-text-file.ts
|
|
2
|
-
import fs from "fs";
|
|
3
|
-
async function readTextFileWithRange(filePath, options) {
|
|
4
|
-
const content = await fs.promises.readFile(filePath, "utf-8");
|
|
5
|
-
if (!options?.line && !options?.limit) return content;
|
|
6
|
-
const lines = content.split("\n");
|
|
7
|
-
const start = Math.max(0, (options.line ?? 1) - 1);
|
|
8
|
-
const end = options.limit ? start + options.limit : lines.length;
|
|
9
|
-
return lines.slice(start, end).join("\n");
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export {
|
|
13
|
-
readTextFileWithRange
|
|
14
|
-
};
|
|
15
|
-
//# sourceMappingURL=chunk-OYSAN7UX.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/utils/read-text-file.ts"],"sourcesContent":["import fs from \"node:fs\";\n\n/**\n * Read a text file, optionally returning only a range of lines.\n * Pure utility — no dependencies on plugin code.\n */\nexport async function readTextFileWithRange(\n filePath: string,\n options?: { line?: number; limit?: number },\n): Promise<string> {\n const content = await fs.promises.readFile(filePath, \"utf-8\");\n if (!options?.line && !options?.limit) return content;\n const lines = content.split(\"\\n\");\n const start = Math.max(0, (options.line ?? 1) - 1);\n const end = options.limit ? start + options.limit : lines.length;\n return lines.slice(start, end).join(\"\\n\");\n}\n"],"mappings":";AAAA,OAAO,QAAQ;AAMf,eAAsB,sBACpB,UACA,SACiB;AACjB,QAAM,UAAU,MAAM,GAAG,SAAS,SAAS,UAAU,OAAO;AAC5D,MAAI,CAAC,SAAS,QAAQ,CAAC,SAAS,MAAO,QAAO;AAC9C,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAM,QAAQ,KAAK,IAAI,IAAI,QAAQ,QAAQ,KAAK,CAAC;AACjD,QAAM,MAAM,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,MAAM;AAC1D,SAAO,MAAM,MAAM,OAAO,GAAG,EAAE,KAAK,IAAI;AAC1C;","names":[]}
|
package/dist/chunk-PA6MNBG4.js
DELETED
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
// src/plugins/tunnel/index.ts
|
|
2
|
-
function createTunnelPlugin() {
|
|
3
|
-
let service = null;
|
|
4
|
-
return {
|
|
5
|
-
name: "@openacp/tunnel",
|
|
6
|
-
version: "1.0.0",
|
|
7
|
-
description: "Expose local services to internet via tunnel providers",
|
|
8
|
-
essential: false,
|
|
9
|
-
permissions: ["services:register", "kernel:access", "commands:register"],
|
|
10
|
-
async install(ctx) {
|
|
11
|
-
const { terminal, settings, legacyConfig } = ctx;
|
|
12
|
-
if (legacyConfig) {
|
|
13
|
-
const tunnelCfg = legacyConfig.tunnel;
|
|
14
|
-
if (tunnelCfg) {
|
|
15
|
-
await settings.setAll({
|
|
16
|
-
enabled: tunnelCfg.enabled ?? true,
|
|
17
|
-
provider: tunnelCfg.provider ?? "cloudflare",
|
|
18
|
-
port: tunnelCfg.port ?? 3100,
|
|
19
|
-
options: tunnelCfg.options ?? {},
|
|
20
|
-
maxUserTunnels: tunnelCfg.maxUserTunnels ?? 5,
|
|
21
|
-
storeTtlMinutes: tunnelCfg.storeTtlMinutes ?? 60,
|
|
22
|
-
auth: tunnelCfg.auth ?? { enabled: false }
|
|
23
|
-
});
|
|
24
|
-
terminal.log.success("Tunnel settings migrated from legacy config");
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
const provider = await terminal.select({
|
|
29
|
-
message: "Tunnel provider:",
|
|
30
|
-
options: [
|
|
31
|
-
{ value: "cloudflare", label: "Cloudflare (cloudflared)", hint: "Free, no account needed" },
|
|
32
|
-
{ value: "ngrok", label: "ngrok", hint: "Requires auth token" },
|
|
33
|
-
{ value: "bore", label: "bore", hint: "Self-hostable" },
|
|
34
|
-
{ value: "tailscale", label: "Tailscale Funnel" }
|
|
35
|
-
]
|
|
36
|
-
});
|
|
37
|
-
const portStr = await terminal.text({
|
|
38
|
-
message: "Local port to expose:",
|
|
39
|
-
defaultValue: "3100",
|
|
40
|
-
validate: (v) => {
|
|
41
|
-
const n = Number(v.trim());
|
|
42
|
-
if (isNaN(n) || n < 1 || n > 65535) return "Port must be 1-65535";
|
|
43
|
-
return void 0;
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
let authToken = "";
|
|
47
|
-
if (provider === "ngrok") {
|
|
48
|
-
authToken = await terminal.text({
|
|
49
|
-
message: "ngrok auth token:",
|
|
50
|
-
validate: (v) => !v.trim() ? "Auth token cannot be empty" : void 0
|
|
51
|
-
});
|
|
52
|
-
authToken = authToken.trim();
|
|
53
|
-
}
|
|
54
|
-
await settings.setAll({
|
|
55
|
-
enabled: true,
|
|
56
|
-
provider,
|
|
57
|
-
port: Number(portStr.trim()),
|
|
58
|
-
options: authToken ? { authtoken: authToken } : {},
|
|
59
|
-
maxUserTunnels: 5,
|
|
60
|
-
storeTtlMinutes: 60,
|
|
61
|
-
auth: { enabled: false }
|
|
62
|
-
});
|
|
63
|
-
terminal.log.success("Tunnel settings saved");
|
|
64
|
-
},
|
|
65
|
-
async configure(ctx) {
|
|
66
|
-
const { terminal, settings } = ctx;
|
|
67
|
-
const current = await settings.getAll();
|
|
68
|
-
const choice = await terminal.select({
|
|
69
|
-
message: "What to configure?",
|
|
70
|
-
options: [
|
|
71
|
-
{ value: "provider", label: `Change provider (current: ${current.provider ?? "none"})` },
|
|
72
|
-
{ value: "port", label: `Change port (current: ${current.port ?? 3100})` },
|
|
73
|
-
{ value: "toggle", label: `${current.enabled ? "Disable" : "Enable"} tunnel` },
|
|
74
|
-
{ value: "done", label: "Done" }
|
|
75
|
-
]
|
|
76
|
-
});
|
|
77
|
-
if (choice === "provider") {
|
|
78
|
-
const provider = await terminal.select({
|
|
79
|
-
message: "Tunnel provider:",
|
|
80
|
-
options: [
|
|
81
|
-
{ value: "cloudflare", label: "Cloudflare" },
|
|
82
|
-
{ value: "ngrok", label: "ngrok" },
|
|
83
|
-
{ value: "bore", label: "bore" },
|
|
84
|
-
{ value: "tailscale", label: "Tailscale" }
|
|
85
|
-
]
|
|
86
|
-
});
|
|
87
|
-
await settings.set("provider", provider);
|
|
88
|
-
terminal.log.success("Provider updated");
|
|
89
|
-
} else if (choice === "port") {
|
|
90
|
-
const val = await terminal.text({
|
|
91
|
-
message: "New port:",
|
|
92
|
-
defaultValue: String(current.port ?? 3100),
|
|
93
|
-
validate: (v) => {
|
|
94
|
-
const n = Number(v.trim());
|
|
95
|
-
if (isNaN(n) || n < 1 || n > 65535) return "Port must be 1-65535";
|
|
96
|
-
return void 0;
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
await settings.set("port", Number(val.trim()));
|
|
100
|
-
terminal.log.success("Port updated");
|
|
101
|
-
} else if (choice === "toggle") {
|
|
102
|
-
const newState = !current.enabled;
|
|
103
|
-
await settings.set("enabled", newState);
|
|
104
|
-
terminal.log.success(`Tunnel ${newState ? "enabled" : "disabled"}`);
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
async uninstall(ctx, opts) {
|
|
108
|
-
if (opts.purge) {
|
|
109
|
-
await ctx.settings.clear();
|
|
110
|
-
ctx.terminal.log.success("Tunnel settings cleared");
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
async setup(ctx) {
|
|
114
|
-
const config = ctx.pluginConfig;
|
|
115
|
-
if (!config.enabled) {
|
|
116
|
-
ctx.log.info("Tunnel disabled");
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
if (!config.provider) {
|
|
120
|
-
ctx.log.info("Tunnel disabled (no provider configured)");
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
const { TunnelService } = await import("./tunnel-service-ZMO4THKE.js");
|
|
124
|
-
const tunnelSvc = new TunnelService(config);
|
|
125
|
-
const publicUrl = await tunnelSvc.start();
|
|
126
|
-
service = tunnelSvc;
|
|
127
|
-
ctx.registerService("tunnel", tunnelSvc);
|
|
128
|
-
ctx.registerCommand({
|
|
129
|
-
name: "tunnel",
|
|
130
|
-
description: "Manage tunnels: /tunnel <port> [label] | /tunnel stop <port>",
|
|
131
|
-
category: "plugin",
|
|
132
|
-
handler: async (args) => {
|
|
133
|
-
const parts = args.raw.trim().split(/\s+/);
|
|
134
|
-
if (parts[0] === "stop" && parts[1]) {
|
|
135
|
-
const port = parseInt(parts[1], 10);
|
|
136
|
-
if (isNaN(port)) return { type: "error", message: "Invalid port number" };
|
|
137
|
-
try {
|
|
138
|
-
await tunnelSvc.stopTunnel(port);
|
|
139
|
-
return { type: "text", text: `Tunnel on port ${port} stopped.` };
|
|
140
|
-
} catch (err) {
|
|
141
|
-
return { type: "error", message: err.message };
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
if (parts[0] && parts[0] !== "") {
|
|
145
|
-
const port = parseInt(parts[0], 10);
|
|
146
|
-
if (isNaN(port)) return { type: "error", message: "Invalid port number" };
|
|
147
|
-
const label = parts.slice(1).join(" ") || void 0;
|
|
148
|
-
try {
|
|
149
|
-
const entry = await tunnelSvc.addTunnel(port, { label });
|
|
150
|
-
return { type: "text", text: `Tunnel created: ${entry.publicUrl ?? "starting..."}` };
|
|
151
|
-
} catch (err) {
|
|
152
|
-
return { type: "error", message: err.message };
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
const url = tunnelSvc.getPublicUrl();
|
|
156
|
-
return { type: "text", text: url ? `Tunnel: ${url}` : "No tunnel active." };
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
ctx.registerCommand({
|
|
160
|
-
name: "tunnels",
|
|
161
|
-
description: "List active tunnels",
|
|
162
|
-
category: "plugin",
|
|
163
|
-
handler: async () => {
|
|
164
|
-
const userTunnels = tunnelSvc.listTunnels();
|
|
165
|
-
const systemUrl = tunnelSvc.getPublicUrl();
|
|
166
|
-
const items = [
|
|
167
|
-
{ label: "System", detail: systemUrl },
|
|
168
|
-
...userTunnels.map((t) => ({
|
|
169
|
-
label: t.label ?? `Port ${t.port}`,
|
|
170
|
-
detail: `${t.publicUrl ?? t.status} (${t.provider})`
|
|
171
|
-
}))
|
|
172
|
-
];
|
|
173
|
-
return { type: "list", title: "Active Tunnels", items };
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
ctx.log.info(`Tunnel ready: ${publicUrl}`);
|
|
177
|
-
},
|
|
178
|
-
async teardown() {
|
|
179
|
-
if (service) {
|
|
180
|
-
await service.stop();
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
var tunnel_default = createTunnelPlugin();
|
|
186
|
-
|
|
187
|
-
export {
|
|
188
|
-
tunnel_default
|
|
189
|
-
};
|
|
190
|
-
//# sourceMappingURL=chunk-PA6MNBG4.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/plugins/tunnel/index.ts"],"sourcesContent":["import type { OpenACPPlugin, InstallContext } from '../../core/plugin/types.js'\nimport type { TunnelConfig } from '../../core/config/config.js'\n\nfunction createTunnelPlugin(): OpenACPPlugin {\n let service: { stop(): Promise<void> } | null = null\n\n return {\n name: '@openacp/tunnel',\n version: '1.0.0',\n description: 'Expose local services to internet via tunnel providers',\n essential: false,\n permissions: ['services:register', 'kernel:access', 'commands:register'],\n\n async install(ctx: InstallContext) {\n const { terminal, settings, legacyConfig } = ctx\n\n // Migrate from legacy config if present\n if (legacyConfig) {\n const tunnelCfg = legacyConfig.tunnel as Record<string, unknown> | undefined\n if (tunnelCfg) {\n await settings.setAll({\n enabled: tunnelCfg.enabled ?? true,\n provider: tunnelCfg.provider ?? 'cloudflare',\n port: tunnelCfg.port ?? 3100,\n options: tunnelCfg.options ?? {},\n maxUserTunnels: tunnelCfg.maxUserTunnels ?? 5,\n storeTtlMinutes: tunnelCfg.storeTtlMinutes ?? 60,\n auth: tunnelCfg.auth ?? { enabled: false },\n })\n terminal.log.success('Tunnel settings migrated from legacy config')\n return\n }\n }\n\n // Interactive setup\n const provider = await terminal.select({\n message: 'Tunnel provider:',\n options: [\n { value: 'cloudflare', label: 'Cloudflare (cloudflared)', hint: 'Free, no account needed' },\n { value: 'ngrok', label: 'ngrok', hint: 'Requires auth token' },\n { value: 'bore', label: 'bore', hint: 'Self-hostable' },\n { value: 'tailscale', label: 'Tailscale Funnel' },\n ],\n })\n\n const portStr = await terminal.text({\n message: 'Local port to expose:',\n defaultValue: '3100',\n validate: (v) => {\n const n = Number(v.trim())\n if (isNaN(n) || n < 1 || n > 65535) return 'Port must be 1-65535'\n return undefined\n },\n })\n\n let authToken = ''\n if (provider === 'ngrok') {\n authToken = await terminal.text({\n message: 'ngrok auth token:',\n validate: (v) => (!v.trim() ? 'Auth token cannot be empty' : undefined),\n })\n authToken = authToken.trim()\n }\n\n await settings.setAll({\n enabled: true,\n provider,\n port: Number(portStr.trim()),\n options: authToken ? { authtoken: authToken } : {},\n maxUserTunnels: 5,\n storeTtlMinutes: 60,\n auth: { enabled: false },\n })\n terminal.log.success('Tunnel settings saved')\n },\n\n async configure(ctx: InstallContext) {\n const { terminal, settings } = ctx\n const current = await settings.getAll()\n\n const choice = await terminal.select({\n message: 'What to configure?',\n options: [\n { value: 'provider', label: `Change provider (current: ${current.provider ?? 'none'})` },\n { value: 'port', label: `Change port (current: ${current.port ?? 3100})` },\n { value: 'toggle', label: `${current.enabled ? 'Disable' : 'Enable'} tunnel` },\n { value: 'done', label: 'Done' },\n ],\n })\n\n if (choice === 'provider') {\n const provider = await terminal.select({\n message: 'Tunnel provider:',\n options: [\n { value: 'cloudflare', label: 'Cloudflare' },\n { value: 'ngrok', label: 'ngrok' },\n { value: 'bore', label: 'bore' },\n { value: 'tailscale', label: 'Tailscale' },\n ],\n })\n await settings.set('provider', provider)\n terminal.log.success('Provider updated')\n } else if (choice === 'port') {\n const val = await terminal.text({\n message: 'New port:',\n defaultValue: String(current.port ?? 3100),\n validate: (v) => {\n const n = Number(v.trim())\n if (isNaN(n) || n < 1 || n > 65535) return 'Port must be 1-65535'\n return undefined\n },\n })\n await settings.set('port', Number(val.trim()))\n terminal.log.success('Port updated')\n } else if (choice === 'toggle') {\n const newState = !current.enabled\n await settings.set('enabled', newState)\n terminal.log.success(`Tunnel ${newState ? 'enabled' : 'disabled'}`)\n }\n },\n\n async uninstall(ctx: InstallContext, opts: { purge: boolean }) {\n if (opts.purge) {\n await ctx.settings.clear()\n ctx.terminal.log.success('Tunnel settings cleared')\n }\n },\n\n async setup(ctx) {\n const config = ctx.pluginConfig as Record<string, unknown>\n if (!config.enabled) {\n ctx.log.info('Tunnel disabled')\n return\n }\n if (!config.provider) {\n ctx.log.info('Tunnel disabled (no provider configured)')\n return\n }\n\n const { TunnelService } = await import('./tunnel-service.js')\n const tunnelSvc = new TunnelService(config as unknown as TunnelConfig)\n const publicUrl = await tunnelSvc.start()\n service = tunnelSvc\n\n ctx.registerService('tunnel', tunnelSvc)\n\n ctx.registerCommand({\n name: 'tunnel',\n description: 'Manage tunnels: /tunnel <port> [label] | /tunnel stop <port>',\n category: 'plugin',\n handler: async (args) => {\n const parts = args.raw.trim().split(/\\s+/)\n\n // /tunnel stop <port>\n if (parts[0] === 'stop' && parts[1]) {\n const port = parseInt(parts[1], 10)\n if (isNaN(port)) return { type: 'error', message: 'Invalid port number' }\n try {\n await tunnelSvc.stopTunnel(port)\n return { type: 'text', text: `Tunnel on port ${port} stopped.` }\n } catch (err) {\n return { type: 'error', message: (err as Error).message }\n }\n }\n\n // /tunnel <port> [label]\n if (parts[0] && parts[0] !== '') {\n const port = parseInt(parts[0], 10)\n if (isNaN(port)) return { type: 'error', message: 'Invalid port number' }\n const label = parts.slice(1).join(' ') || undefined\n try {\n const entry = await tunnelSvc.addTunnel(port, { label })\n return { type: 'text', text: `Tunnel created: ${entry.publicUrl ?? 'starting...'}` }\n } catch (err) {\n return { type: 'error', message: (err as Error).message }\n }\n }\n\n // /tunnel (no args) — show current tunnel URL\n const url = tunnelSvc.getPublicUrl()\n return { type: 'text', text: url ? `Tunnel: ${url}` : 'No tunnel active.' }\n },\n })\n\n ctx.registerCommand({\n name: 'tunnels',\n description: 'List active tunnels',\n category: 'plugin',\n handler: async () => {\n const userTunnels = tunnelSvc.listTunnels()\n const systemUrl = tunnelSvc.getPublicUrl()\n const items = [\n { label: 'System', detail: systemUrl },\n ...userTunnels.map(t => ({\n label: t.label ?? `Port ${t.port}`,\n detail: `${t.publicUrl ?? t.status} (${t.provider})`,\n })),\n ]\n return { type: 'list', title: 'Active Tunnels', items }\n },\n })\n\n ctx.log.info(`Tunnel ready: ${publicUrl}`)\n },\n\n async teardown() {\n if (service) {\n await service.stop()\n }\n },\n }\n}\n\nexport default createTunnelPlugin()\n"],"mappings":"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","names":[]}
|
package/dist/chunk-QWVHCTCA.js
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
expandHome
|
|
3
|
-
} from "./chunk-W4LK6WJP.js";
|
|
4
|
-
|
|
5
|
-
// src/cli/daemon.ts
|
|
6
|
-
import { spawn } from "child_process";
|
|
7
|
-
import * as fs from "fs";
|
|
8
|
-
import * as path from "path";
|
|
9
|
-
import * as os from "os";
|
|
10
|
-
var DEFAULT_PID_PATH = path.join(os.homedir(), ".openacp", "openacp.pid");
|
|
11
|
-
var DEFAULT_LOG_DIR = path.join(os.homedir(), ".openacp", "logs");
|
|
12
|
-
var RUNNING_MARKER = path.join(os.homedir(), ".openacp", "running");
|
|
13
|
-
function writePidFile(pidPath, pid) {
|
|
14
|
-
const dir = path.dirname(pidPath);
|
|
15
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
16
|
-
fs.writeFileSync(pidPath, String(pid));
|
|
17
|
-
}
|
|
18
|
-
function readPidFile(pidPath) {
|
|
19
|
-
try {
|
|
20
|
-
const content = fs.readFileSync(pidPath, "utf-8").trim();
|
|
21
|
-
const pid = parseInt(content, 10);
|
|
22
|
-
return isNaN(pid) ? null : pid;
|
|
23
|
-
} catch {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
function removePidFile(pidPath) {
|
|
28
|
-
try {
|
|
29
|
-
fs.unlinkSync(pidPath);
|
|
30
|
-
} catch {
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
function isProcessRunning(pidPath) {
|
|
34
|
-
const pid = readPidFile(pidPath);
|
|
35
|
-
if (pid === null) return false;
|
|
36
|
-
try {
|
|
37
|
-
process.kill(pid, 0);
|
|
38
|
-
return true;
|
|
39
|
-
} catch {
|
|
40
|
-
removePidFile(pidPath);
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
function getStatus(pidPath = DEFAULT_PID_PATH) {
|
|
45
|
-
const pid = readPidFile(pidPath);
|
|
46
|
-
if (pid === null) return { running: false };
|
|
47
|
-
try {
|
|
48
|
-
process.kill(pid, 0);
|
|
49
|
-
return { running: true, pid };
|
|
50
|
-
} catch {
|
|
51
|
-
removePidFile(pidPath);
|
|
52
|
-
return { running: false };
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
function startDaemon(pidPath = DEFAULT_PID_PATH, logDir) {
|
|
56
|
-
markRunning();
|
|
57
|
-
if (isProcessRunning(pidPath)) {
|
|
58
|
-
const pid = readPidFile(pidPath);
|
|
59
|
-
return { error: `Already running (PID ${pid})` };
|
|
60
|
-
}
|
|
61
|
-
const resolvedLogDir = logDir ? expandHome(logDir) : DEFAULT_LOG_DIR;
|
|
62
|
-
fs.mkdirSync(resolvedLogDir, { recursive: true });
|
|
63
|
-
const logFile = path.join(resolvedLogDir, "openacp.log");
|
|
64
|
-
const cliPath = path.resolve(process.argv[1]);
|
|
65
|
-
const nodePath = process.execPath;
|
|
66
|
-
const out = fs.openSync(logFile, "a");
|
|
67
|
-
const err = fs.openSync(logFile, "a");
|
|
68
|
-
const child = spawn(nodePath, [cliPath, "--daemon-child"], {
|
|
69
|
-
detached: true,
|
|
70
|
-
stdio: ["ignore", out, err]
|
|
71
|
-
});
|
|
72
|
-
fs.closeSync(out);
|
|
73
|
-
fs.closeSync(err);
|
|
74
|
-
if (!child.pid) {
|
|
75
|
-
return { error: "Failed to spawn daemon process" };
|
|
76
|
-
}
|
|
77
|
-
writePidFile(pidPath, child.pid);
|
|
78
|
-
child.unref();
|
|
79
|
-
return { pid: child.pid };
|
|
80
|
-
}
|
|
81
|
-
function sleep(ms) {
|
|
82
|
-
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
83
|
-
}
|
|
84
|
-
function isProcessAlive(pid) {
|
|
85
|
-
try {
|
|
86
|
-
process.kill(pid, 0);
|
|
87
|
-
return "alive";
|
|
88
|
-
} catch (e) {
|
|
89
|
-
const err = e;
|
|
90
|
-
if (err.code === "EPERM") return "eperm";
|
|
91
|
-
return "dead";
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
async function stopDaemon(pidPath = DEFAULT_PID_PATH) {
|
|
95
|
-
const pid = readPidFile(pidPath);
|
|
96
|
-
if (pid === null) return { stopped: false, error: "Not running (no PID file)" };
|
|
97
|
-
const status = isProcessAlive(pid);
|
|
98
|
-
if (status === "dead") {
|
|
99
|
-
removePidFile(pidPath);
|
|
100
|
-
return { stopped: false, error: "Not running (stale PID file removed)" };
|
|
101
|
-
}
|
|
102
|
-
if (status === "eperm") {
|
|
103
|
-
removePidFile(pidPath);
|
|
104
|
-
return { stopped: false, error: "PID belongs to another process (stale PID file removed)" };
|
|
105
|
-
}
|
|
106
|
-
try {
|
|
107
|
-
process.kill(pid, "SIGTERM");
|
|
108
|
-
} catch (e) {
|
|
109
|
-
return { stopped: false, error: `Failed to stop: ${e.message}` };
|
|
110
|
-
}
|
|
111
|
-
clearRunning();
|
|
112
|
-
const POLL_INTERVAL = 100;
|
|
113
|
-
const TIMEOUT = 5e3;
|
|
114
|
-
const start = Date.now();
|
|
115
|
-
while (Date.now() - start < TIMEOUT) {
|
|
116
|
-
await sleep(POLL_INTERVAL);
|
|
117
|
-
const s = isProcessAlive(pid);
|
|
118
|
-
if (s === "dead" || s === "eperm") {
|
|
119
|
-
removePidFile(pidPath);
|
|
120
|
-
return { stopped: true, pid };
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
try {
|
|
124
|
-
process.kill(pid, "SIGKILL");
|
|
125
|
-
} catch (e) {
|
|
126
|
-
const err = e;
|
|
127
|
-
if (err.code === "EPERM") {
|
|
128
|
-
return { stopped: false, pid, error: "PID may have been reused by another process. Run `openacp status` to verify, or manually delete the PID file." };
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
const killStart = Date.now();
|
|
132
|
-
while (Date.now() - killStart < 1e3) {
|
|
133
|
-
await sleep(POLL_INTERVAL);
|
|
134
|
-
const s = isProcessAlive(pid);
|
|
135
|
-
if (s === "dead" || s === "eperm") {
|
|
136
|
-
removePidFile(pidPath);
|
|
137
|
-
return { stopped: true, pid };
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return { stopped: false, pid, error: "Process did not exit after SIGKILL (possible uninterruptible I/O). PID file retained." };
|
|
141
|
-
}
|
|
142
|
-
function getPidPath() {
|
|
143
|
-
return DEFAULT_PID_PATH;
|
|
144
|
-
}
|
|
145
|
-
function markRunning() {
|
|
146
|
-
fs.mkdirSync(path.dirname(RUNNING_MARKER), { recursive: true });
|
|
147
|
-
fs.writeFileSync(RUNNING_MARKER, "");
|
|
148
|
-
}
|
|
149
|
-
function clearRunning() {
|
|
150
|
-
try {
|
|
151
|
-
fs.unlinkSync(RUNNING_MARKER);
|
|
152
|
-
} catch {
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
function shouldAutoStart() {
|
|
156
|
-
return fs.existsSync(RUNNING_MARKER);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export {
|
|
160
|
-
writePidFile,
|
|
161
|
-
readPidFile,
|
|
162
|
-
removePidFile,
|
|
163
|
-
isProcessRunning,
|
|
164
|
-
getStatus,
|
|
165
|
-
startDaemon,
|
|
166
|
-
stopDaemon,
|
|
167
|
-
getPidPath,
|
|
168
|
-
markRunning,
|
|
169
|
-
clearRunning,
|
|
170
|
-
shouldAutoStart
|
|
171
|
-
};
|
|
172
|
-
//# sourceMappingURL=chunk-QWVHCTCA.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/daemon.ts"],"sourcesContent":["import { spawn } from 'node:child_process'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport * as os from 'node:os'\nimport { expandHome } from '../core/config/config.js'\n\nconst DEFAULT_PID_PATH = path.join(os.homedir(), '.openacp', 'openacp.pid')\nconst DEFAULT_LOG_DIR = path.join(os.homedir(), '.openacp', 'logs')\nconst RUNNING_MARKER = path.join(os.homedir(), '.openacp', 'running')\n\nexport function writePidFile(pidPath: string, pid: number): void {\n const dir = path.dirname(pidPath)\n fs.mkdirSync(dir, { recursive: true })\n fs.writeFileSync(pidPath, String(pid))\n}\n\nexport function readPidFile(pidPath: string): number | null {\n try {\n const content = fs.readFileSync(pidPath, 'utf-8').trim()\n const pid = parseInt(content, 10)\n return isNaN(pid) ? null : pid\n } catch {\n return null\n }\n}\n\nexport function removePidFile(pidPath: string): void {\n try {\n fs.unlinkSync(pidPath)\n } catch {\n // ignore if already gone\n }\n}\n\nexport function isProcessRunning(pidPath: string): boolean {\n const pid = readPidFile(pidPath)\n if (pid === null) return false\n try {\n process.kill(pid, 0)\n return true\n } catch {\n // Process not running, clean up stale PID file\n removePidFile(pidPath)\n return false\n }\n}\n\nexport function getStatus(pidPath: string = DEFAULT_PID_PATH): { running: boolean; pid?: number } {\n const pid = readPidFile(pidPath)\n if (pid === null) return { running: false }\n try {\n process.kill(pid, 0)\n return { running: true, pid }\n } catch {\n removePidFile(pidPath)\n return { running: false }\n }\n}\n\nexport function startDaemon(pidPath: string = DEFAULT_PID_PATH, logDir?: string): { pid: number } | { error: string } {\n // Mark as running so auto-start works on next boot\n markRunning()\n\n // Check if already running\n if (isProcessRunning(pidPath)) {\n const pid = readPidFile(pidPath)!\n return { error: `Already running (PID ${pid})` }\n }\n\n const resolvedLogDir = logDir ? expandHome(logDir) : DEFAULT_LOG_DIR\n fs.mkdirSync(resolvedLogDir, { recursive: true })\n const logFile = path.join(resolvedLogDir, 'openacp.log')\n\n // Find the CLI entry point\n const cliPath = path.resolve(process.argv[1])\n const nodePath = process.execPath\n\n const out = fs.openSync(logFile, 'a')\n const err = fs.openSync(logFile, 'a')\n\n const child = spawn(nodePath, [cliPath, '--daemon-child'], {\n detached: true,\n stdio: ['ignore', out, err],\n })\n\n // Close file descriptors in parent — child has its own copies\n fs.closeSync(out)\n fs.closeSync(err)\n\n if (!child.pid) {\n return { error: 'Failed to spawn daemon process' }\n }\n\n // PID file is written by the child process itself (in main.ts startServer)\n // to avoid race conditions and ensure consistency with LaunchAgent/systemd starts.\n // We still write it here as a fallback in case the child hasn't written it yet\n // when the parent needs to report the PID.\n writePidFile(pidPath, child.pid)\n child.unref()\n\n return { pid: child.pid }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\nfunction isProcessAlive(pid: number): 'alive' | 'dead' | 'eperm' {\n try {\n process.kill(pid, 0)\n return 'alive'\n } catch (e) {\n const err = e as NodeJS.ErrnoException\n if (err.code === 'EPERM') return 'eperm'\n return 'dead'\n }\n}\n\nexport async function stopDaemon(pidPath: string = DEFAULT_PID_PATH): Promise<{ stopped: boolean; pid?: number; error?: string }> {\n const pid = readPidFile(pidPath)\n if (pid === null) return { stopped: false, error: 'Not running (no PID file)' }\n\n const status = isProcessAlive(pid)\n if (status === 'dead') {\n removePidFile(pidPath)\n return { stopped: false, error: 'Not running (stale PID file removed)' }\n }\n if (status === 'eperm') {\n removePidFile(pidPath)\n return { stopped: false, error: 'PID belongs to another process (stale PID file removed)' }\n }\n\n try {\n process.kill(pid, 'SIGTERM')\n } catch (e) {\n return { stopped: false, error: `Failed to stop: ${(e as Error).message}` }\n }\n\n clearRunning()\n\n const POLL_INTERVAL = 100\n const TIMEOUT = 5000\n const start = Date.now()\n\n while (Date.now() - start < TIMEOUT) {\n await sleep(POLL_INTERVAL)\n const s = isProcessAlive(pid)\n if (s === 'dead' || s === 'eperm') {\n removePidFile(pidPath)\n return { stopped: true, pid }\n }\n }\n\n try {\n process.kill(pid, 'SIGKILL')\n } catch (e) {\n const err = e as NodeJS.ErrnoException\n if (err.code === 'EPERM') {\n return { stopped: false, pid, error: 'PID may have been reused by another process. Run `openacp status` to verify, or manually delete the PID file.' }\n }\n }\n\n const killStart = Date.now()\n while (Date.now() - killStart < 1000) {\n await sleep(POLL_INTERVAL)\n const s = isProcessAlive(pid)\n if (s === 'dead' || s === 'eperm') {\n removePidFile(pidPath)\n return { stopped: true, pid }\n }\n }\n\n // SIGKILL sent but process still alive after 1s — extremely rare (uninterruptible I/O).\n return { stopped: false, pid, error: 'Process did not exit after SIGKILL (possible uninterruptible I/O). PID file retained.' }\n}\n\nexport function getPidPath(): string {\n return DEFAULT_PID_PATH\n}\n\n/** Mark that the daemon should auto-start on boot */\nexport function markRunning(): void {\n fs.mkdirSync(path.dirname(RUNNING_MARKER), { recursive: true })\n fs.writeFileSync(RUNNING_MARKER, '')\n}\n\n/** Remove running marker — daemon won't auto-start on boot */\nexport function clearRunning(): void {\n try { fs.unlinkSync(RUNNING_MARKER) } catch { /* ignore */ }\n}\n\n/** Check if the daemon was running before (should auto-start on boot) */\nexport function shouldAutoStart(): boolean {\n return fs.existsSync(RUNNING_MARKER)\n}\n"],"mappings":"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","names":["resolve"]}
|