@openacp/cli 2026.330.2 → 2026.331.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -0
- package/dist/adapter-ELG3VRZ3.js +14 -0
- package/dist/{agent-catalog-SZQQERV7.js → agent-catalog-UYD26QDK.js} +3 -3
- package/dist/{api-client-XTLRRFPX.js → api-client-PEMHYL5U.js} +2 -2
- package/dist/{api-server-JLBDKCU4.js → api-server-DATG2KBR.js} +3 -3
- package/dist/api-server-L5Z7XACW.js +7 -0
- package/dist/chunk-23SRIVG4.js +50 -0
- package/dist/chunk-23SRIVG4.js.map +1 -0
- package/dist/{chunk-YIGBJFJL.js → chunk-7GXEMMEV.js} +15 -15
- package/dist/{chunk-QWVHCTCA.js → chunk-7U6IZIJP.js} +37 -23
- package/dist/chunk-7U6IZIJP.js.map +1 -0
- package/dist/{chunk-FCTC7KDT.js → chunk-7YIKTRSM.js} +14 -10
- package/dist/chunk-7YIKTRSM.js.map +1 -0
- package/dist/{chunk-MITTQMGZ.js → chunk-BYCJQPMN.js} +5 -5
- package/dist/chunk-BYCJQPMN.js.map +1 -0
- package/dist/{chunk-5ZNBNIK3.js → chunk-EWVXSTQK.js} +193 -53
- package/dist/chunk-EWVXSTQK.js.map +1 -0
- package/dist/{chunk-UWH7KIAA.js → chunk-FPKQYCQS.js} +88 -13
- package/dist/chunk-FPKQYCQS.js.map +1 -0
- package/dist/{chunk-GEOXPGCO.js → chunk-K6UY5M75.js} +12 -9
- package/dist/chunk-K6UY5M75.js.map +1 -0
- package/dist/{chunk-KDU3ZEWT.js → chunk-KGAQW6F4.js} +12 -3
- package/dist/chunk-KGAQW6F4.js.map +1 -0
- package/dist/{chunk-7RKPIM3E.js → chunk-LRV56K2M.js} +205 -16
- package/dist/chunk-LRV56K2M.js.map +1 -0
- package/dist/{chunk-V2YZWYXT.js → chunk-MDJHCCFS.js} +18 -17
- package/dist/chunk-MDJHCCFS.js.map +1 -0
- package/dist/chunk-NHD5XDD2.js +686 -0
- package/dist/chunk-NHD5XDD2.js.map +1 -0
- package/dist/{chunk-APS6UEFU.js → chunk-NJX75BLK.js} +1 -1
- package/dist/chunk-NJX75BLK.js.map +1 -0
- package/dist/{chunk-5HKQCYOI.js → chunk-NOEAJNTK.js} +14 -3
- package/dist/chunk-NOEAJNTK.js.map +1 -0
- package/dist/chunk-ON7HB5O7.js +58 -0
- package/dist/chunk-ON7HB5O7.js.map +1 -0
- package/dist/{chunk-5OCGO27U.js → chunk-OSBZXY2W.js} +2 -1
- package/dist/chunk-OSBZXY2W.js.map +1 -0
- package/dist/{chunk-PA6MNBG4.js → chunk-P3HHJANC.js} +32 -13
- package/dist/chunk-P3HHJANC.js.map +1 -0
- package/dist/{chunk-BTJHGSLM.js → chunk-R2YLDQLI.js} +9 -10
- package/dist/chunk-R2YLDQLI.js.map +1 -0
- package/dist/{chunk-CFUJGWOP.js → chunk-SSLVNCEA.js} +27 -3
- package/dist/chunk-SSLVNCEA.js.map +1 -0
- package/dist/{chunk-MPGEHTGE.js → chunk-TGP34LQN.js} +9 -7
- package/dist/chunk-TGP34LQN.js.map +1 -0
- package/dist/{chunk-TMVTSWVH.js → chunk-VUSCVRJL.js} +2 -1
- package/dist/chunk-VUSCVRJL.js.map +1 -0
- package/dist/chunk-XRJUS6FE.js +53 -0
- package/dist/chunk-XRJUS6FE.js.map +1 -0
- package/dist/{chunk-W4LK6WJP.js → chunk-YZCKSNRN.js} +24 -17
- package/dist/chunk-YZCKSNRN.js.map +1 -0
- package/dist/{chunk-3NAFXVQM.js → chunk-ZIRH6QWW.js} +7 -5
- package/dist/chunk-ZIRH6QWW.js.map +1 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.js +334 -140
- package/dist/cli.js.map +1 -1
- package/dist/config-X4UP7H6R.js +13 -0
- package/dist/config-editor-7BENRVG5.js +11 -0
- package/dist/{config-registry-ZXAIJNYB.js → config-registry-M3FFWEVM.js} +3 -2
- package/dist/context-FVGCU5TI.js +9 -0
- package/dist/core-plugins-JSY2I44L.js +25 -0
- package/dist/{daemon-XFEMMJSZ.js → daemon-UOSRDEXW.js} +8 -3
- package/dist/doctor-6DLACBR4.js +10 -0
- package/dist/{file-service-HHB3JQIO.js → file-service-FQQYME7M.js} +2 -2
- package/dist/index.d.ts +265 -32
- package/dist/index.js +44 -33
- package/dist/index.js.map +1 -1
- package/dist/{install-cloudflared-JRJ4BSOM.js → install-cloudflared-LNS5L5FR.js} +5 -4
- package/dist/install-cloudflared-LNS5L5FR.js.map +1 -0
- package/dist/{install-context-EHYV5WRY.js → install-context-KZO5FR4D.js} +4 -3
- package/dist/install-context-KZO5FR4D.js.map +1 -0
- package/dist/{install-jq-ISTGT263.js → install-jq-SN4IA5K4.js} +3 -3
- package/dist/instance-context-FLCE7VZ4.js +13 -0
- package/dist/instance-registry-SW5FWKHO.js +7 -0
- package/dist/{main-L2M4NTJY.js → main-D7M2AKRM.js} +91 -48
- package/dist/main-D7M2AKRM.js.map +1 -0
- package/dist/{plugin-create-EHL76ZZG.js → plugin-create-HFKS23JY.js} +4 -2
- package/dist/{plugin-create-EHL76ZZG.js.map → plugin-create-HFKS23JY.js.map} +1 -1
- package/dist/{post-upgrade-Y26S2ZQ7.js → post-upgrade-F4YPMTUT.js} +6 -6
- package/dist/{security-2BA265LN.js → security-O4XGN2CM.js} +2 -2
- package/dist/{setup-E6BNEYCS.js → setup-44WLBIOT.js} +209 -22
- package/dist/setup-44WLBIOT.js.map +1 -0
- package/dist/{speech-SG62JYIF.js → speech-GHTSWDAN.js} +2 -2
- package/dist/telegram-D7ASLVEB.js +7 -0
- package/dist/telegram-D7ASLVEB.js.map +1 -0
- package/dist/tunnel-ALJDPFDQ.js +10 -0
- package/dist/tunnel-ALJDPFDQ.js.map +1 -0
- package/dist/{tunnel-service-ZMO4THKE.js → tunnel-service-TBAHDXMF.js} +41 -547
- package/dist/tunnel-service-TBAHDXMF.js.map +1 -0
- package/package.json +1 -1
- package/dist/adapter-4U6MC5ZS.js +0 -13
- package/dist/api-server-5VNYFWJE.js +0 -7
- 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-5HKQCYOI.js.map +0 -1
- package/dist/chunk-5OCGO27U.js.map +0 -1
- package/dist/chunk-5ZNBNIK3.js.map +0 -1
- package/dist/chunk-7RKPIM3E.js.map +0 -1
- package/dist/chunk-APS6UEFU.js.map +0 -1
- package/dist/chunk-BTJHGSLM.js.map +0 -1
- package/dist/chunk-CFUJGWOP.js.map +0 -1
- package/dist/chunk-FCTC7KDT.js.map +0 -1
- package/dist/chunk-GEOXPGCO.js.map +0 -1
- package/dist/chunk-KDU3ZEWT.js.map +0 -1
- package/dist/chunk-MITTQMGZ.js.map +0 -1
- package/dist/chunk-MPGEHTGE.js.map +0 -1
- package/dist/chunk-PA6MNBG4.js.map +0 -1
- package/dist/chunk-QWVHCTCA.js.map +0 -1
- package/dist/chunk-TMVTSWVH.js.map +0 -1
- package/dist/chunk-UWH7KIAA.js.map +0 -1
- package/dist/chunk-V2YZWYXT.js.map +0 -1
- package/dist/chunk-W4LK6WJP.js.map +0 -1
- package/dist/config-KN6NKKPF.js +0 -20
- package/dist/config-editor-76RVZS4B.js +0 -10
- package/dist/context-NXXW62NJ.js +0 -9
- package/dist/core-plugins-OCHKGCIZ.js +0 -22
- package/dist/doctor-AV6AUO22.js +0 -9
- package/dist/install-cloudflared-JRJ4BSOM.js.map +0 -1
- package/dist/install-context-EHYV5WRY.js.map +0 -1
- package/dist/main-L2M4NTJY.js.map +0 -1
- package/dist/setup-E6BNEYCS.js.map +0 -1
- package/dist/telegram-EAVRDNFU.js +0 -7
- package/dist/tunnel-HWJ27WDH.js +0 -7
- package/dist/tunnel-service-ZMO4THKE.js.map +0 -1
- /package/dist/{adapter-4U6MC5ZS.js.map → adapter-ELG3VRZ3.js.map} +0 -0
- /package/dist/{agent-catalog-SZQQERV7.js.map → agent-catalog-UYD26QDK.js.map} +0 -0
- /package/dist/{api-client-XTLRRFPX.js.map → api-client-PEMHYL5U.js.map} +0 -0
- /package/dist/{api-server-5VNYFWJE.js.map → api-server-DATG2KBR.js.map} +0 -0
- /package/dist/{api-server-JLBDKCU4.js.map → api-server-L5Z7XACW.js.map} +0 -0
- /package/dist/{chunk-YIGBJFJL.js.map → chunk-7GXEMMEV.js.map} +0 -0
- /package/dist/{config-KN6NKKPF.js.map → config-X4UP7H6R.js.map} +0 -0
- /package/dist/{config-editor-76RVZS4B.js.map → config-editor-7BENRVG5.js.map} +0 -0
- /package/dist/{config-registry-ZXAIJNYB.js.map → config-registry-M3FFWEVM.js.map} +0 -0
- /package/dist/{context-NXXW62NJ.js.map → context-FVGCU5TI.js.map} +0 -0
- /package/dist/{core-plugins-OCHKGCIZ.js.map → core-plugins-JSY2I44L.js.map} +0 -0
- /package/dist/{daemon-XFEMMJSZ.js.map → daemon-UOSRDEXW.js.map} +0 -0
- /package/dist/{doctor-AV6AUO22.js.map → doctor-6DLACBR4.js.map} +0 -0
- /package/dist/{file-service-HHB3JQIO.js.map → file-service-FQQYME7M.js.map} +0 -0
- /package/dist/{install-jq-ISTGT263.js.map → install-jq-SN4IA5K4.js.map} +0 -0
- /package/dist/{security-2BA265LN.js.map → instance-context-FLCE7VZ4.js.map} +0 -0
- /package/dist/{speech-SG62JYIF.js.map → instance-registry-SW5FWKHO.js.map} +0 -0
- /package/dist/{post-upgrade-Y26S2ZQ7.js.map → post-upgrade-F4YPMTUT.js.map} +0 -0
- /package/dist/{telegram-EAVRDNFU.js.map → security-O4XGN2CM.js.map} +0 -0
- /package/dist/{tunnel-HWJ27WDH.js.map → speech-GHTSWDAN.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/plugins/tunnel/tunnel-registry.ts","../../src/plugins/tunnel/providers/cloudflare.ts","../../src/plugins/tunnel/providers/ngrok.ts","../../src/plugins/tunnel/providers/bore.ts","../../src/plugins/tunnel/providers/tailscale.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport os from 'node:os'\nimport { createChildLogger } from '../../core/utils/log.js'\nimport type { TunnelProvider } from './provider.js'\nimport { CloudflareTunnelProvider } from './providers/cloudflare.js'\nimport { NgrokTunnelProvider } from './providers/ngrok.js'\nimport { BoreTunnelProvider } from './providers/bore.js'\nimport { TailscaleTunnelProvider } from './providers/tailscale.js'\n\nconst log = createChildLogger({ module: 'tunnel-registry' })\n\nexport const MAX_RETRIES = 5\nconst BASE_RETRY_DELAY_MS = 2_000\n\nexport interface TunnelEntry {\n port: number\n type: 'system' | 'user'\n provider: string\n label?: string\n publicUrl?: string\n sessionId?: string\n status: 'stopped' | 'starting' | 'active' | 'failed'\n retryCount: number\n createdAt: string\n}\n\ninterface PersistedEntry {\n port: number\n type: 'system' | 'user'\n provider: string\n label?: string\n sessionId?: string\n createdAt: string\n}\n\ninterface LiveEntry {\n entry: TunnelEntry\n process: TunnelProvider | null\n spawnPromise: Promise<string> | null\n retryTimer: ReturnType<typeof setTimeout> | null\n}\n\nexport class TunnelRegistry {\n private entries: Map<number, LiveEntry> = new Map()\n private saveTimeout: ReturnType<typeof setTimeout> | null = null\n private maxUserTunnels: number\n private providerOptions: Record<string, unknown>\n private registryPath: string\n private shuttingDown = false\n\n constructor(opts: { maxUserTunnels?: number; providerOptions?: Record<string, unknown>; registryPath?: string } = {}) {\n this.maxUserTunnels = opts.maxUserTunnels ?? 5\n this.providerOptions = opts.providerOptions ?? {}\n this.registryPath = opts.registryPath ?? path.join(os.homedir(), '.openacp', 'tunnels.json')\n }\n\n async add(port: number, opts: {\n type: 'system' | 'user'\n provider: string\n label?: string\n sessionId?: string\n }): Promise<TunnelEntry> {\n // Check if port already registered\n if (this.entries.has(port)) {\n const existing = this.entries.get(port)!\n if (existing.entry.status === 'active' || existing.entry.status === 'starting') {\n throw new Error(`Port ${port} is already tunneled → ${existing.entry.publicUrl || 'starting...'}`)\n }\n // Stopped/failed entry — clean up retry timer and re-add\n if (existing.retryTimer) clearTimeout(existing.retryTimer)\n this.entries.delete(port)\n }\n\n // Check max user tunnels\n if (opts.type === 'user') {\n const userCount = this.list(false).filter(e => e.status === 'active' || e.status === 'starting').length\n if (userCount >= this.maxUserTunnels) {\n throw new Error(`Max user tunnels (${this.maxUserTunnels}) reached. Stop a tunnel first.`)\n }\n }\n\n const entry: TunnelEntry = {\n port,\n type: opts.type,\n provider: opts.provider,\n label: opts.label,\n sessionId: opts.sessionId,\n status: 'starting',\n retryCount: 0,\n createdAt: new Date().toISOString(),\n }\n\n const provider = this.createProvider(opts.provider)\n\n // Wire up post-establishment crash detection with auto-retry\n provider.onExit((code) => {\n if (this.shuttingDown) return\n const live = this.entries.get(port)\n if (!live) return\n\n live.entry.status = 'failed'\n live.process = null\n this.scheduleSave()\n\n if (live.entry.retryCount < MAX_RETRIES) {\n const delay = BASE_RETRY_DELAY_MS * Math.pow(2, live.entry.retryCount)\n log.warn({ port, code, retry: live.entry.retryCount + 1, maxRetries: MAX_RETRIES, delayMs: delay },\n 'Tunnel crashed, scheduling retry')\n live.retryTimer = setTimeout(() => this.retry(port, opts), delay)\n } else {\n log.error({ port, code }, `Tunnel crashed and exhausted all ${MAX_RETRIES} retries`)\n }\n })\n\n const spawnPromise = provider.start(port).then(url => {\n entry.publicUrl = url\n entry.status = 'active'\n log.info({ port, url, label: opts.label }, 'Tunnel active')\n this.scheduleSave()\n return url\n }).catch(err => {\n entry.status = 'failed'\n log.error({ port, err: (err as Error).message }, 'Tunnel failed to start')\n this.scheduleSave()\n throw err\n })\n\n this.entries.set(port, { entry, process: provider, spawnPromise, retryTimer: null })\n this.scheduleSave()\n\n // Await spawn — caller gets the URL or error\n await spawnPromise\n return entry\n }\n\n private async retry(port: number, opts: {\n type: 'system' | 'user'\n provider: string\n label?: string\n sessionId?: string\n }): Promise<void> {\n if (this.shuttingDown) return\n const live = this.entries.get(port)\n if (!live) return\n\n const retryCount = live.entry.retryCount + 1\n log.info({ port, retry: retryCount, maxRetries: MAX_RETRIES }, 'Retrying tunnel')\n\n // Remove old entry so add() doesn't reject\n if (live.retryTimer) clearTimeout(live.retryTimer)\n this.entries.delete(port)\n\n try {\n const entry = await this.add(port, opts)\n entry.retryCount = retryCount\n } catch (err) {\n log.error({ port, err: (err as Error).message, retry: retryCount }, 'Tunnel retry failed')\n\n // Re-insert as failed with incremented retry count for next onExit cycle\n const failedEntry: TunnelEntry = {\n port,\n type: opts.type,\n provider: opts.provider,\n label: opts.label,\n sessionId: opts.sessionId,\n status: 'failed',\n retryCount,\n createdAt: live.entry.createdAt,\n }\n\n if (retryCount < MAX_RETRIES) {\n const delay = BASE_RETRY_DELAY_MS * Math.pow(2, retryCount)\n const retryTimer = setTimeout(() => this.retry(port, opts), delay)\n this.entries.set(port, { entry: failedEntry, process: null, spawnPromise: null, retryTimer })\n log.warn({ port, retry: retryCount + 1, delayMs: delay }, 'Scheduling next retry')\n } else {\n this.entries.set(port, { entry: failedEntry, process: null, spawnPromise: null, retryTimer: null })\n log.error({ port }, `Tunnel exhausted all ${MAX_RETRIES} retries`)\n }\n this.scheduleSave()\n }\n }\n\n async stop(port: number): Promise<void> {\n const live = this.entries.get(port)\n if (!live) return\n\n if (live.entry.type === 'system') {\n throw new Error('Cannot stop system tunnel')\n }\n\n // Cancel any pending retry\n if (live.retryTimer) clearTimeout(live.retryTimer)\n\n // Wait for spawn to finish if still starting\n if (live.spawnPromise) {\n try { await live.spawnPromise } catch { /* ignore spawn error */ }\n }\n\n if (live.process) {\n await live.process.stop()\n }\n\n this.entries.delete(port)\n this.scheduleSave()\n log.info({ port, label: live.entry.label }, 'Tunnel stopped')\n }\n\n async stopBySession(sessionId: string): Promise<TunnelEntry[]> {\n const stopped: TunnelEntry[] = []\n const toStop = this.getBySession(sessionId)\n for (const entry of toStop) {\n try {\n await this.stop(entry.port)\n stopped.push(entry)\n } catch { /* ignore */ }\n }\n return stopped\n }\n\n async stopAllUser(): Promise<void> {\n const userEntries = this.list(false)\n for (const entry of userEntries) {\n try { await this.stop(entry.port) } catch { /* ignore */ }\n }\n }\n\n async shutdown(): Promise<void> {\n this.shuttingDown = true\n\n for (const [, live] of this.entries) {\n if (live.retryTimer) clearTimeout(live.retryTimer)\n if (live.spawnPromise) {\n try { await live.spawnPromise } catch { /* ignore */ }\n }\n if (live.process) {\n await live.process.stop()\n }\n }\n this.entries.clear()\n this.scheduleSave()\n }\n\n list(includeSystem = false): TunnelEntry[] {\n const entries = Array.from(this.entries.values()).map(l => l.entry)\n if (includeSystem) return entries\n return entries.filter(e => e.type === 'user')\n }\n\n get(port: number): TunnelEntry | null {\n return this.entries.get(port)?.entry ?? null\n }\n\n getBySession(sessionId: string): TunnelEntry[] {\n return this.list(false).filter(e => e.sessionId === sessionId)\n }\n\n getSystemEntry(): TunnelEntry | null {\n for (const live of this.entries.values()) {\n if (live.entry.type === 'system') return live.entry\n }\n return null\n }\n\n async restore(): Promise<void> {\n if (!fs.existsSync(this.registryPath)) return\n\n try {\n const raw = JSON.parse(fs.readFileSync(this.registryPath, 'utf-8')) as PersistedEntry[]\n log.info({ count: raw.length }, 'Restoring tunnels')\n\n // Only restore user tunnels — system tunnel is registered separately by TunnelService.start()\n const userEntries = raw.filter(e => e.type === 'user')\n for (const persisted of userEntries) {\n try {\n await this.add(persisted.port, {\n type: persisted.type,\n provider: persisted.provider,\n label: persisted.label,\n sessionId: persisted.sessionId,\n })\n } catch (err) {\n log.warn({ port: persisted.port, err: (err as Error).message }, 'Failed to restore tunnel')\n }\n }\n } catch (err) {\n log.warn({ err: (err as Error).message }, 'Failed to read tunnels.json')\n }\n }\n\n private createProvider(name: string): TunnelProvider {\n switch (name) {\n case 'cloudflare':\n return new CloudflareTunnelProvider(this.providerOptions)\n case 'ngrok':\n return new NgrokTunnelProvider(this.providerOptions)\n case 'bore':\n return new BoreTunnelProvider(this.providerOptions)\n case 'tailscale':\n return new TailscaleTunnelProvider(this.providerOptions)\n default:\n log.warn({ provider: name }, 'Unknown provider, falling back to cloudflare')\n return new CloudflareTunnelProvider(this.providerOptions)\n }\n }\n\n private scheduleSave(): void {\n if (this.saveTimeout) clearTimeout(this.saveTimeout)\n this.saveTimeout = setTimeout(() => this.save(), 2000)\n }\n\n private save(): void {\n const data: PersistedEntry[] = Array.from(this.entries.values()).map(l => ({\n port: l.entry.port,\n type: l.entry.type,\n provider: l.entry.provider,\n label: l.entry.label,\n sessionId: l.entry.sessionId,\n createdAt: l.entry.createdAt,\n }))\n\n try {\n const dir = path.dirname(this.registryPath)\n fs.mkdirSync(dir, { recursive: true })\n fs.writeFileSync(this.registryPath, JSON.stringify(data, null, 2))\n } catch (err) {\n log.error({ err: (err as Error).message }, 'Failed to save tunnels.json')\n }\n }\n\n flush(): void {\n if (this.saveTimeout) {\n clearTimeout(this.saveTimeout)\n this.saveTimeout = null\n }\n this.save()\n }\n}\n","import { spawn, type ChildProcess } from 'node:child_process'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport os from 'node:os'\nimport { createChildLogger } from '../../../core/utils/log.js'\nimport { commandExists } from '../../../core/agents/agent-dependencies.js'\nimport type { TunnelProvider } from '../provider.js'\n\nconst log = createChildLogger({ module: 'cloudflare-tunnel' })\n\nconst SIGKILL_TIMEOUT_MS = 5_000\n\nexport class CloudflareTunnelProvider implements TunnelProvider {\n private child: ChildProcess | null = null\n private publicUrl = ''\n private options: Record<string, unknown>\n private binDir: string\n private exitCallback: ((code: number | null) => void) | null = null\n\n constructor(options: Record<string, unknown> = {}, binDir?: string) {\n this.options = options\n this.binDir = binDir ?? path.join(os.homedir(), '.openacp', 'bin')\n }\n\n onExit(callback: (code: number | null) => void): void {\n this.exitCallback = callback\n }\n\n async start(localPort: number): Promise<string> {\n // Find binary — post-upgrade should have installed it, but fallback to ensureCloudflared() as safety net\n let binaryPath = this.findBinary()\n if (!binaryPath) {\n log.warn('cloudflared not found locally, attempting auto-install as fallback...')\n try {\n const { ensureCloudflared } = await import('./install-cloudflared.js')\n binaryPath = await ensureCloudflared()\n } catch (err) {\n throw new Error(`cloudflared is not installed and auto-install failed: ${(err as Error).message}`)\n }\n }\n\n const args = ['tunnel', '--url', `http://localhost:${localPort}`]\n if (this.options.domain) {\n args.push('--hostname', String(this.options.domain))\n }\n\n return new Promise<string>((resolve, reject) => {\n let settled = false\n const settle = (fn: () => void) => { if (!settled) { settled = true; fn() } }\n\n const timeout = setTimeout(() => {\n this.stop()\n settle(() => reject(new Error('Cloudflare tunnel timed out after 30s')))\n }, 30_000)\n\n try {\n this.child = spawn(binaryPath, args, { stdio: ['ignore', 'pipe', 'pipe'] })\n } catch {\n clearTimeout(timeout)\n settle(() => reject(new Error(`Failed to start cloudflared at ${binaryPath}`)))\n return\n }\n\n const urlPattern = /https:\\/\\/[a-zA-Z0-9-]+\\.trycloudflare\\.com/\n\n const onData = (data: Buffer) => {\n const line = data.toString()\n log.debug(line.trim())\n const match = line.match(urlPattern)\n if (match) {\n clearTimeout(timeout)\n this.publicUrl = match[0]\n log.info({ url: this.publicUrl }, 'Cloudflare tunnel ready')\n settle(() => resolve(this.publicUrl))\n }\n }\n\n this.child.stdout?.on('data', onData)\n this.child.stderr?.on('data', onData)\n\n this.child.on('error', (err) => {\n clearTimeout(timeout)\n settle(() => reject(new Error(`cloudflared failed to start: ${err.message}`)))\n })\n\n this.child.on('exit', (code) => {\n if (!this.publicUrl) {\n clearTimeout(timeout)\n settle(() => reject(new Error(`cloudflared exited with code ${code} before establishing tunnel`)))\n } else {\n // Post-establishment crash\n log.error({ code }, 'cloudflared exited unexpectedly after establishment')\n this.child = null\n this.exitCallback?.(code)\n }\n })\n })\n }\n\n async stop(): Promise<void> {\n const child = this.child\n if (!child) return\n this.child = null\n\n child.kill('SIGTERM')\n\n // Wait for graceful exit, then SIGKILL if still alive\n const exited = await Promise.race([\n new Promise<boolean>((resolve) => child.on('exit', () => resolve(true))),\n new Promise<boolean>((resolve) => setTimeout(() => resolve(false), SIGKILL_TIMEOUT_MS)),\n ])\n\n if (!exited) {\n log.warn('cloudflared did not exit after SIGTERM, sending SIGKILL')\n child.kill('SIGKILL')\n }\n\n log.info('Cloudflare tunnel stopped')\n }\n\n getPublicUrl(): string {\n return this.publicUrl\n }\n\n private findBinary(): string | null {\n // 1. Check PATH first (respects user's system install)\n if (commandExists('cloudflared')) return 'cloudflared'\n\n // 2. Check binDir (installed by post-upgrade)\n const binPath = path.join(this.binDir, 'cloudflared')\n if (fs.existsSync(binPath)) return binPath\n\n // 3. Not found\n return null\n }\n}\n","import { spawn, type ChildProcess } from 'node:child_process'\nimport { createChildLogger } from '../../../core/utils/log.js'\nimport type { TunnelProvider } from '../provider.js'\n\nconst log = createChildLogger({ module: 'ngrok-tunnel' })\n\nconst SIGKILL_TIMEOUT_MS = 5_000\n\nexport class NgrokTunnelProvider implements TunnelProvider {\n private child: ChildProcess | null = null\n private publicUrl = ''\n private options: Record<string, unknown>\n private exitCallback: ((code: number | null) => void) | null = null\n\n constructor(options: Record<string, unknown> = {}) {\n this.options = options\n }\n\n onExit(callback: (code: number | null) => void): void {\n this.exitCallback = callback\n }\n\n async start(localPort: number): Promise<string> {\n const args = ['http', String(localPort), '--log', 'stdout', '--log-format', 'json']\n if (this.options.authtoken) {\n args.push('--authtoken', String(this.options.authtoken))\n }\n if (this.options.domain) {\n args.push('--domain', String(this.options.domain))\n }\n if (this.options.region) {\n args.push('--region', String(this.options.region))\n }\n\n return new Promise<string>((resolve, reject) => {\n let settled = false\n const settle = (fn: () => void) => { if (!settled) { settled = true; fn() } }\n\n const timeout = setTimeout(() => {\n this.stop()\n settle(() => reject(new Error('ngrok tunnel timed out after 30s. Is ngrok installed?')))\n }, 30_000)\n\n try {\n this.child = spawn('ngrok', args, { stdio: ['ignore', 'pipe', 'pipe'] })\n } catch {\n clearTimeout(timeout)\n settle(() => reject(new Error(\n 'Failed to start ngrok. Install it from https://ngrok.com/download'\n )))\n return\n }\n\n // Match both v2 (*.ngrok.io) and v3 (*.ngrok-free.app, *.ngrok.app) domains\n const urlPattern = /https:\\/\\/[a-zA-Z0-9-]+\\.(?:ngrok(?:-free)?\\.app|ngrok\\.io)/\n\n const onData = (data: Buffer) => {\n const line = data.toString()\n log.debug(line.trim())\n const match = line.match(urlPattern)\n if (match) {\n clearTimeout(timeout)\n this.publicUrl = match[0]\n log.info({ url: this.publicUrl }, 'ngrok tunnel ready')\n settle(() => resolve(this.publicUrl))\n }\n }\n\n this.child.stdout?.on('data', onData)\n this.child.stderr?.on('data', onData)\n\n this.child.on('error', (err) => {\n clearTimeout(timeout)\n settle(() => reject(new Error(\n `ngrok failed to start: ${err.message}. Install it from https://ngrok.com/download`\n )))\n })\n\n this.child.on('exit', (code) => {\n if (!this.publicUrl) {\n clearTimeout(timeout)\n settle(() => reject(new Error(`ngrok exited with code ${code} before establishing tunnel`)))\n } else {\n log.error({ code }, 'ngrok exited unexpectedly after establishment')\n this.child = null\n this.exitCallback?.(code)\n }\n })\n })\n }\n\n async stop(): Promise<void> {\n const child = this.child\n if (!child) return\n this.child = null\n\n child.kill('SIGTERM')\n\n const exited = await Promise.race([\n new Promise<boolean>((resolve) => child.on('exit', () => resolve(true))),\n new Promise<boolean>((resolve) => setTimeout(() => resolve(false), SIGKILL_TIMEOUT_MS)),\n ])\n\n if (!exited) {\n log.warn('ngrok did not exit after SIGTERM, sending SIGKILL')\n child.kill('SIGKILL')\n }\n\n log.info('ngrok tunnel stopped')\n }\n\n getPublicUrl(): string {\n return this.publicUrl\n }\n}\n","import { spawn, type ChildProcess } from 'node:child_process'\nimport { createChildLogger } from '../../../core/utils/log.js'\nimport type { TunnelProvider } from '../provider.js'\n\nconst log = createChildLogger({ module: 'bore-tunnel' })\n\nconst SIGKILL_TIMEOUT_MS = 5_000\n\nexport class BoreTunnelProvider implements TunnelProvider {\n private child: ChildProcess | null = null\n private publicUrl = ''\n private options: Record<string, unknown>\n private exitCallback: ((code: number | null) => void) | null = null\n\n constructor(options: Record<string, unknown> = {}) {\n this.options = options\n }\n\n onExit(callback: (code: number | null) => void): void {\n this.exitCallback = callback\n }\n\n async start(localPort: number): Promise<string> {\n const server = String(this.options.server || 'bore.pub')\n const args = ['local', String(localPort), '--to', server]\n if (this.options.port) {\n args.push('--port', String(this.options.port))\n }\n if (this.options.secret) {\n args.push('--secret', String(this.options.secret))\n }\n\n return new Promise<string>((resolve, reject) => {\n let settled = false\n const settle = (fn: () => void) => { if (!settled) { settled = true; fn() } }\n\n const timeout = setTimeout(() => {\n this.stop()\n settle(() => reject(new Error('Bore tunnel timed out after 30s. Is bore installed?')))\n }, 30_000)\n\n try {\n this.child = spawn('bore', args, { stdio: ['ignore', 'pipe', 'pipe'] })\n } catch {\n clearTimeout(timeout)\n settle(() => reject(new Error(\n 'Failed to start bore. Install it from https://github.com/ekzhang/bore'\n )))\n return\n }\n\n const urlPattern = /listening at ([^\\s]+):(\\d+)/\n\n const onData = (data: Buffer) => {\n const line = data.toString()\n log.debug(line.trim())\n const match = line.match(urlPattern)\n if (match) {\n clearTimeout(timeout)\n this.publicUrl = `http://${match[1]}:${match[2]}`\n log.info({ url: this.publicUrl }, 'Bore tunnel ready')\n settle(() => resolve(this.publicUrl))\n }\n }\n\n this.child.stdout?.on('data', onData)\n this.child.stderr?.on('data', onData)\n\n this.child.on('error', (err) => {\n clearTimeout(timeout)\n settle(() => reject(new Error(\n `bore failed to start: ${err.message}. Install it from https://github.com/ekzhang/bore`\n )))\n })\n\n this.child.on('exit', (code) => {\n if (!this.publicUrl) {\n clearTimeout(timeout)\n settle(() => reject(new Error(`bore exited with code ${code} before establishing tunnel`)))\n } else {\n log.error({ code }, 'bore exited unexpectedly after establishment')\n this.child = null\n this.exitCallback?.(code)\n }\n })\n })\n }\n\n async stop(): Promise<void> {\n const child = this.child\n if (!child) return\n this.child = null\n\n child.kill('SIGTERM')\n\n const exited = await Promise.race([\n new Promise<boolean>((resolve) => child.on('exit', () => resolve(true))),\n new Promise<boolean>((resolve) => setTimeout(() => resolve(false), SIGKILL_TIMEOUT_MS)),\n ])\n\n if (!exited) {\n log.warn('bore did not exit after SIGTERM, sending SIGKILL')\n child.kill('SIGKILL')\n }\n\n log.info('Bore tunnel stopped')\n }\n\n getPublicUrl(): string {\n return this.publicUrl\n }\n}\n","import { spawn, execSync, type ChildProcess } from 'node:child_process'\nimport { createChildLogger } from '../../../core/utils/log.js'\nimport type { TunnelProvider } from '../provider.js'\n\nconst log = createChildLogger({ module: 'tailscale-tunnel' })\n\nconst SIGKILL_TIMEOUT_MS = 5_000\n\nexport class TailscaleTunnelProvider implements TunnelProvider {\n private child: ChildProcess | null = null\n private publicUrl = ''\n private options: Record<string, unknown>\n private exitCallback: ((code: number | null) => void) | null = null\n\n constructor(options: Record<string, unknown> = {}) {\n this.options = options\n }\n\n onExit(callback: (code: number | null) => void): void {\n this.exitCallback = callback\n }\n\n async start(localPort: number): Promise<string> {\n let hostname = ''\n try {\n const statusJson = execSync('tailscale status --json', { encoding: 'utf-8', timeout: 10_000 })\n const status = JSON.parse(statusJson)\n hostname = String(status.Self.DNSName).replace(/\\.$/, '')\n log.debug({ hostname }, 'Resolved Tailscale hostname')\n } catch (err) {\n log.warn('Failed to resolve Tailscale hostname via status --json')\n }\n\n const args = ['funnel', String(localPort)]\n if (this.options.bg) {\n args.push('--bg')\n }\n\n return new Promise<string>((resolve, reject) => {\n let settled = false\n const settle = (fn: () => void) => { if (!settled) { settled = true; fn() } }\n\n const timeout = setTimeout(() => {\n this.stop()\n settle(() => reject(new Error('Tailscale funnel timed out after 30s. Is tailscale installed?')))\n }, 30_000)\n\n try {\n this.child = spawn('tailscale', args, { stdio: ['ignore', 'pipe', 'pipe'] })\n } catch {\n clearTimeout(timeout)\n settle(() => reject(new Error(\n 'Failed to start tailscale. Install it from https://tailscale.com/download'\n )))\n return\n }\n\n // Match only Tailscale funnel URLs (*.ts.net pattern)\n const urlPattern = /https:\\/\\/[a-zA-Z0-9-]+\\.[a-zA-Z0-9-]+\\.ts\\.net/\n\n const onData = (data: Buffer) => {\n const line = data.toString()\n log.debug(line.trim())\n const match = line.match(urlPattern)\n if (match) {\n clearTimeout(timeout)\n this.publicUrl = match[0]\n log.info({ url: this.publicUrl }, 'Tailscale funnel ready')\n settle(() => resolve(this.publicUrl))\n }\n }\n\n this.child.stdout?.on('data', onData)\n this.child.stderr?.on('data', onData)\n\n this.child.on('error', (err) => {\n clearTimeout(timeout)\n settle(() => reject(new Error(\n `tailscale failed to start: ${err.message}. Install it from https://tailscale.com/download`\n )))\n })\n\n this.child.on('exit', (code) => {\n if (!this.publicUrl) {\n clearTimeout(timeout)\n if (hostname) {\n // Tailscale funnel may exit immediately after configuring — construct URL with port\n this.publicUrl = `https://${hostname}:${localPort}`\n this.child = null // process is done; prevent stop() from sending SIGTERM to dead process\n log.info({ url: this.publicUrl }, 'Tailscale funnel ready (constructed from hostname)')\n settle(() => resolve(this.publicUrl))\n } else {\n settle(() => reject(new Error(`tailscale exited with code ${code} before establishing funnel`)))\n }\n } else {\n log.error({ code }, 'tailscale exited unexpectedly after establishment')\n this.child = null\n this.exitCallback?.(code)\n }\n })\n })\n }\n\n async stop(): Promise<void> {\n const child = this.child\n if (!child) return\n this.child = null\n\n child.kill('SIGTERM')\n\n const exited = await Promise.race([\n new Promise<boolean>((resolve) => child.on('exit', () => resolve(true))),\n new Promise<boolean>((resolve) => setTimeout(() => resolve(false), SIGKILL_TIMEOUT_MS)),\n ])\n\n if (!exited) {\n log.warn('tailscale did not exit after SIGTERM, sending SIGKILL')\n child.kill('SIGKILL')\n }\n\n log.info('Tailscale funnel stopped')\n }\n\n getPublicUrl(): string {\n return this.publicUrl\n }\n}\n"],"mappings":"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","names":["fs","path","os","spawn","log","SIGKILL_TIMEOUT_MS","spawn","spawn","log","SIGKILL_TIMEOUT_MS","spawn","spawn","log","SIGKILL_TIMEOUT_MS","spawn","log","path","os","fs"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/plugins/context/context-provider.ts","../../src/plugins/context/entire/checkpoint-reader.ts"],"sourcesContent":["// NOTE: This interface is designed around Entire as the first provider.\n// It may evolve when additional providers (Cursor history, Zed, etc.) are added.\n// Providers may only support a subset of query types and should return empty results\n// for unsupported types rather than throwing.\n\nexport interface ContextProvider {\n readonly name: string;\n isAvailable(repoPath: string): Promise<boolean>;\n listSessions(query: ContextQuery): Promise<SessionListResult>;\n buildContext(query: ContextQuery, options?: ContextOptions): Promise<ContextResult>;\n}\n\nexport interface ContextQuery {\n repoPath: string;\n type: \"branch\" | \"commit\" | \"pr\" | \"latest\" | \"checkpoint\" | \"session\";\n value: string;\n}\n\nexport interface ContextOptions {\n maxTokens?: number;\n limit?: number;\n /** When true, insert `## [agentName]` headers at agent boundaries in merged history */\n labelAgent?: boolean;\n}\n\nexport interface SessionInfo {\n checkpointId: string;\n sessionIndex: string;\n transcriptPath: string;\n createdAt: string;\n endedAt: string;\n branch: string;\n agent: string;\n turnCount: number;\n filesTouched: string[];\n sessionId: string;\n}\n\nexport interface SessionListResult {\n sessions: SessionInfo[];\n estimatedTokens: number;\n}\n\nexport type ContextMode = \"full\" | \"balanced\" | \"compact\";\n\nexport interface ContextResult {\n markdown: string;\n tokenEstimate: number;\n sessionCount: number;\n totalTurns: number;\n mode: ContextMode;\n truncated: boolean;\n timeRange: { start: string; end: string };\n}\n\nexport const DEFAULT_MAX_TOKENS = 30_000;\nexport const TOKENS_PER_TURN_ESTIMATE = 400;\n","import { execFileSync } from \"child_process\";\nimport type { SessionInfo } from \"../context-provider.js\";\n\n// ─── Internal types ────────────────────────────────────────────────────────────\n\ninterface CheckpointMeta {\n checkpoint_id?: string;\n branch?: string;\n files_touched?: string[];\n sessions: Array<{\n metadata: string;\n transcript: string;\n }>;\n}\n\ninterface SessionMeta {\n session_id?: string;\n created_at?: string;\n branch?: string;\n agent?: string;\n files_touched?: string[];\n session_metrics?: {\n turn_count?: number;\n };\n}\n\n// ─── CheckpointReader ─────────────────────────────────────────────────────────\n\nconst ENTIRE_BRANCH = \"origin/entire/checkpoints/v1\";\nconst CHECKPOINT_ID_RE = /^[0-9a-f]{12}$/;\nconst SESSION_ID_RE =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;\n\nexport class CheckpointReader {\n constructor(private readonly repoPath: string) {}\n\n // ─── Git execution ───────────────────────────────────────────────────────────\n\n /**\n * Run a git command in the repo directory.\n * Returns trimmed stdout on success, empty string on failure.\n */\n private git(...args: string[]): string {\n try {\n return execFileSync(\"git\", [\"-C\", this.repoPath, ...args], {\n encoding: \"utf-8\",\n }).trim();\n } catch {\n return \"\";\n }\n }\n\n // ─── Static helpers ──────────────────────────────────────────────────────────\n\n /**\n * Convert a 12-char checkpoint ID to its shard path: \"f634acf05138\" → \"f6/34acf05138\"\n */\n static shardPath(cpId: string): string {\n return `${cpId.slice(0, 2)}/${cpId.slice(2)}`;\n }\n\n /**\n * Returns true when value looks like a 12-char lowercase hex checkpoint ID.\n */\n static isCheckpointId(value: string): boolean {\n return CHECKPOINT_ID_RE.test(value);\n }\n\n /**\n * Returns true when value looks like a UUID (session ID).\n */\n static isSessionId(value: string): boolean {\n return SESSION_ID_RE.test(value);\n }\n\n /**\n * Parse checkpoint-level metadata JSON. Returns null on error.\n */\n static parseCheckpointMeta(json: string): CheckpointMeta | null {\n try {\n const parsed = JSON.parse(json) as CheckpointMeta;\n if (!parsed || typeof parsed !== \"object\") return null;\n if (!Array.isArray(parsed.sessions)) return null;\n return parsed;\n } catch {\n return null;\n }\n }\n\n /**\n * Extract Entire-Checkpoint trailer IDs from `git log --format=\"%H|%(trailers:...)\"` output.\n * Each line is: `<hash>|<trailer_value_or_empty>`. Returns only non-empty trailer values.\n * Uses the last pipe on each line to locate the trailer value, to be robust against\n * subject lines that contain pipes.\n */\n static parseCheckpointTrailers(output: string): string[] {\n const ids: string[] = [];\n for (const line of output.split(\"\\n\")) {\n const pipe = line.lastIndexOf(\"|\");\n if (pipe === -1) continue;\n const trailerId = line.slice(pipe + 1).trim();\n if (trailerId) ids.push(trailerId);\n }\n return ids;\n }\n\n // ─── Branch check ────────────────────────────────────────────────────────────\n\n async hasEntireBranch(): Promise<boolean> {\n const out = this.git(\"branch\", \"-r\");\n return out.includes(\"entire/checkpoints/v1\");\n }\n\n // ─── Core session fetching ───────────────────────────────────────────────────\n\n private listAllCheckpointIds(): string[] {\n const out = this.git(\n \"ls-tree\",\n \"-r\",\n ENTIRE_BRANCH,\n \"--name-only\"\n );\n if (!out) return [];\n\n const ids = new Set<string>();\n for (const file of out.split(\"\\n\")) {\n const parts = file.split(\"/\");\n // Checkpoint-level metadata: XX/YYYYYYYYYY/metadata.json (3 parts)\n if (parts.length === 3 && parts[2] === \"metadata.json\") {\n ids.add(parts[0] + parts[1]);\n }\n }\n return [...ids];\n }\n\n private fetchCheckpointMeta(cpId: string): CheckpointMeta | null {\n const shard = CheckpointReader.shardPath(cpId);\n const raw = this.git(\"show\", `${ENTIRE_BRANCH}:${shard}/metadata.json`);\n if (!raw) return null;\n return CheckpointReader.parseCheckpointMeta(raw);\n }\n\n private fetchSessionMeta(metaPath: string): SessionMeta {\n const normalized = metaPath.startsWith(\"/\") ? metaPath.slice(1) : metaPath;\n const raw = this.git(\"show\", `${ENTIRE_BRANCH}:${normalized}`);\n if (!raw) return {};\n try {\n return JSON.parse(raw) as SessionMeta;\n } catch {\n return {};\n }\n }\n\n /**\n * Build SessionInfo[] from a single checkpoint's metadata.\n */\n private buildSessionsForCheckpoint(\n cpId: string,\n cpMeta: CheckpointMeta\n ): SessionInfo[] {\n const sessions: SessionInfo[] = [];\n\n for (let idx = 0; idx < cpMeta.sessions.length; idx++) {\n const sess = cpMeta.sessions[idx];\n const transcriptPath = (sess.transcript ?? \"\").replace(/^\\//, \"\");\n const metaPath = sess.metadata ?? \"\";\n\n const smeta = this.fetchSessionMeta(metaPath);\n const createdAt = smeta.created_at ?? \"\";\n\n sessions.push({\n checkpointId: cpId,\n sessionIndex: String(idx),\n transcriptPath,\n createdAt,\n endedAt: createdAt, // will be filled from JSONL by conversation builder\n branch: smeta.branch ?? cpMeta.branch ?? \"\",\n agent: smeta.agent ?? \"\",\n turnCount: smeta.session_metrics?.turn_count ?? 0,\n filesTouched: smeta.files_touched ?? cpMeta.files_touched ?? [],\n sessionId: smeta.session_id ?? \"\",\n });\n }\n\n return sessions;\n }\n\n private getSessionsForCheckpoint(cpId: string): SessionInfo[] {\n const meta = this.fetchCheckpointMeta(cpId);\n if (!meta) return [];\n return this.buildSessionsForCheckpoint(cpId, meta);\n }\n\n // ─── Public resolvers ────────────────────────────────────────────────────────\n\n /**\n * All sessions recorded on a given branch, sorted by createdAt ascending.\n */\n async resolveByBranch(branchName: string): Promise<SessionInfo[]> {\n const cpIds = this.listAllCheckpointIds();\n const sessions: SessionInfo[] = [];\n\n for (const cpId of cpIds) {\n const meta = this.fetchCheckpointMeta(cpId);\n if (!meta) continue;\n if (meta.branch !== branchName) continue;\n sessions.push(...this.buildSessionsForCheckpoint(cpId, meta));\n }\n\n sessions.sort((a, b) => a.createdAt.localeCompare(b.createdAt));\n return sessions;\n }\n\n /**\n * Sessions linked to a specific commit via the Entire-Checkpoint git trailer.\n */\n async resolveByCommit(commitHash: string): Promise<SessionInfo[]> {\n const fullHash = this.git(\"rev-parse\", commitHash);\n if (!fullHash) return [];\n\n const cpId = this.git(\n \"log\",\n \"-1\",\n \"--format=%(trailers:key=Entire-Checkpoint,valueonly)\",\n fullHash\n );\n if (!cpId) return [];\n\n return this.getSessionsForCheckpoint(cpId.trim());\n }\n\n /**\n * All sessions from a merged PR (by number or GitHub URL).\n */\n async resolveByPr(prInput: string): Promise<SessionInfo[]> {\n let prNumber: string;\n\n if (/^\\d+$/.test(prInput)) {\n prNumber = prInput;\n } else {\n const m = /\\/pull\\/(\\d+)/.exec(prInput);\n if (!m) return [];\n prNumber = m[1];\n }\n\n const mergeOut = this.git(\n \"log\",\n \"--all\",\n \"--oneline\",\n \"--grep\",\n `Merge pull request #${prNumber}`\n );\n if (!mergeOut) return [];\n\n const mergeCommit = mergeOut.split(\"\\n\")[0].split(\" \")[0];\n\n const logOut = this.git(\n \"log\",\n \"--format=%H|%(trailers:key=Entire-Checkpoint,valueonly)\",\n `${mergeCommit}^2`,\n \"--not\",\n `${mergeCommit}^1`\n );\n if (!logOut) return [];\n\n const cpIds = CheckpointReader.parseCheckpointTrailers(logOut);\n const sessions: SessionInfo[] = [];\n\n for (const cpId of cpIds) {\n sessions.push(...this.getSessionsForCheckpoint(cpId));\n }\n\n sessions.sort((a, b) => a.createdAt.localeCompare(b.createdAt));\n return sessions;\n }\n\n /**\n * Sessions matching a specific checkpoint ID.\n */\n async resolveByCheckpoint(checkpointId: string): Promise<SessionInfo[]> {\n return this.getSessionsForCheckpoint(checkpointId);\n }\n\n /**\n * Find a session by its UUID.\n */\n async resolveBySessionId(sessionId: string): Promise<SessionInfo[]> {\n const cpIds = this.listAllCheckpointIds();\n\n for (const cpId of cpIds) {\n const sessions = this.getSessionsForCheckpoint(cpId);\n const match = sessions.find((s) => s.sessionId === sessionId);\n if (match) return [match];\n }\n\n return [];\n }\n\n /**\n * Latest N sessions across all checkpoints, sorted by createdAt descending.\n */\n async resolveLatest(count: number): Promise<SessionInfo[]> {\n const cpIds = this.listAllCheckpointIds();\n const all: SessionInfo[] = [];\n\n for (const cpId of cpIds) {\n all.push(...this.getSessionsForCheckpoint(cpId));\n }\n\n all.sort((a, b) => b.createdAt.localeCompare(a.createdAt));\n return all.slice(0, count);\n }\n\n /**\n * Read the full JSONL transcript content from the entire branch.\n */\n getTranscript(transcriptPath: string): string {\n const normalized = transcriptPath.startsWith(\"/\")\n ? transcriptPath.slice(1)\n : transcriptPath;\n return this.git(\"show\", `${ENTIRE_BRANCH}:${normalized}`);\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getGlobalRoot
|
|
3
|
+
} from "./chunk-ON7HB5O7.js";
|
|
4
|
+
|
|
1
5
|
// src/core/config/config-registry.ts
|
|
2
6
|
import * as fs from "fs";
|
|
3
7
|
import * as path from "path";
|
|
4
|
-
import * as os from "os";
|
|
5
8
|
var CONFIG_REGISTRY = [
|
|
6
9
|
{
|
|
7
10
|
path: "defaultAgent",
|
|
@@ -10,7 +13,7 @@ var CONFIG_REGISTRY = [
|
|
|
10
13
|
type: "select",
|
|
11
14
|
options: (config) => {
|
|
12
15
|
try {
|
|
13
|
-
const agentsPath = path.join(
|
|
16
|
+
const agentsPath = path.join(getGlobalRoot(), "agents.json");
|
|
14
17
|
if (fs.existsSync(agentsPath)) {
|
|
15
18
|
const data = JSON.parse(fs.readFileSync(agentsPath, "utf-8"));
|
|
16
19
|
return Object.keys(data.installed ?? {});
|
|
@@ -105,6 +108,14 @@ var CONFIG_REGISTRY = [
|
|
|
105
108
|
type: "string",
|
|
106
109
|
scope: "sensitive",
|
|
107
110
|
hotReload: true
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
path: "agentSwitch.labelHistory",
|
|
114
|
+
displayName: "Label Agent in History",
|
|
115
|
+
group: "agent",
|
|
116
|
+
type: "toggle",
|
|
117
|
+
scope: "safe",
|
|
118
|
+
hotReload: true
|
|
108
119
|
}
|
|
109
120
|
];
|
|
110
121
|
function getFieldDef(path2) {
|
|
@@ -142,4 +153,4 @@ export {
|
|
|
142
153
|
resolveOptions,
|
|
143
154
|
getConfigValue
|
|
144
155
|
};
|
|
145
|
-
//# sourceMappingURL=chunk-
|
|
156
|
+
//# sourceMappingURL=chunk-NOEAJNTK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/config/config-registry.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { Config } from \"./config.js\";\nimport { getGlobalRoot } from \"../instance-context.js\";\n\nexport interface ConfigFieldDef {\n path: string;\n displayName: string;\n group: string;\n type: \"toggle\" | \"select\" | \"number\" | \"string\";\n options?: string[] | ((config: Config) => string[]);\n scope: \"safe\" | \"sensitive\";\n hotReload: boolean;\n}\n\nexport const CONFIG_REGISTRY: ConfigFieldDef[] = [\n {\n path: \"defaultAgent\",\n displayName: \"Default Agent\",\n group: \"agent\",\n type: \"select\",\n options: (config) => {\n try {\n const agentsPath = path.join(getGlobalRoot(), \"agents.json\");\n if (fs.existsSync(agentsPath)) {\n const data = JSON.parse(fs.readFileSync(agentsPath, \"utf-8\"));\n return Object.keys(data.installed ?? {});\n }\n } catch {\n /* fallback */\n }\n return Object.keys(config.agents ?? {});\n },\n scope: \"safe\",\n hotReload: true,\n },\n {\n path: \"channels.telegram.displayVerbosity\",\n displayName: \"Telegram Verbosity\",\n group: \"display\",\n type: \"select\",\n options: [\"low\", \"medium\", \"high\"],\n scope: \"safe\",\n hotReload: true,\n },\n {\n path: \"channels.discord.displayVerbosity\",\n displayName: \"Discord Verbosity\",\n group: \"display\",\n type: \"select\",\n options: [\"low\", \"medium\", \"high\"],\n scope: \"safe\",\n hotReload: true,\n },\n {\n path: \"logging.level\",\n displayName: \"Log Level\",\n group: \"logging\",\n type: \"select\",\n options: [\"silent\", \"debug\", \"info\", \"warn\", \"error\", \"fatal\"],\n scope: \"safe\",\n hotReload: true,\n },\n {\n path: \"tunnel.enabled\",\n displayName: \"Tunnel\",\n group: \"tunnel\",\n type: \"toggle\",\n scope: \"safe\",\n hotReload: false,\n },\n {\n path: \"security.maxConcurrentSessions\",\n displayName: \"Max Concurrent Sessions\",\n group: \"security\",\n type: \"number\",\n scope: \"safe\",\n hotReload: true,\n },\n {\n path: \"security.sessionTimeoutMinutes\",\n displayName: \"Session Timeout (min)\",\n group: \"security\",\n type: \"number\",\n scope: \"safe\",\n hotReload: true,\n },\n {\n path: \"workspace.baseDir\",\n displayName: \"Workspace Directory\",\n group: \"workspace\",\n type: \"string\",\n scope: \"safe\",\n hotReload: true,\n },\n {\n path: \"sessionStore.ttlDays\",\n displayName: \"Session Store TTL (days)\",\n group: \"storage\",\n type: \"number\",\n scope: \"safe\",\n hotReload: true,\n },\n {\n path: \"speech.stt.provider\",\n displayName: \"Speech to Text\",\n group: \"speech\",\n type: \"select\",\n options: [\"groq\"],\n scope: \"safe\",\n hotReload: true,\n },\n {\n path: \"speech.stt.apiKey\",\n displayName: \"STT API Key\",\n group: \"speech\",\n type: \"string\",\n scope: \"sensitive\",\n hotReload: true,\n },\n {\n path: \"agentSwitch.labelHistory\",\n displayName: \"Label Agent in History\",\n group: \"agent\",\n type: \"toggle\",\n scope: \"safe\",\n hotReload: true,\n },\n];\n\nexport function getFieldDef(path: string): ConfigFieldDef | undefined {\n return CONFIG_REGISTRY.find((f) => f.path === path);\n}\n\nexport function getSafeFields(): ConfigFieldDef[] {\n return CONFIG_REGISTRY.filter((f) => f.scope === \"safe\");\n}\n\nexport function isHotReloadable(path: string): boolean {\n const def = getFieldDef(path);\n return def?.hotReload ?? false;\n}\n\nexport function resolveOptions(\n def: ConfigFieldDef,\n config: Config,\n): string[] | undefined {\n if (!def.options) return undefined;\n return typeof def.options === \"function\" ? def.options(config) : def.options;\n}\n\nexport function getConfigValue(config: Config, path: string): unknown {\n const parts = path.split(\".\");\n let current: unknown = config;\n for (const part of parts) {\n if (current && typeof current === \"object\" && part in current) {\n current = (current as Record<string, unknown>)[part];\n } else {\n return undefined;\n }\n }\n return current;\n}\n"],"mappings":"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","names":["path"]}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// src/core/instance-context.ts
|
|
2
|
+
import path from "path";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import os from "os";
|
|
5
|
+
function createInstanceContext(opts) {
|
|
6
|
+
const { id, root, isGlobal } = opts;
|
|
7
|
+
return {
|
|
8
|
+
id,
|
|
9
|
+
root,
|
|
10
|
+
isGlobal,
|
|
11
|
+
paths: {
|
|
12
|
+
config: path.join(root, "config.json"),
|
|
13
|
+
sessions: path.join(root, "sessions.json"),
|
|
14
|
+
agents: path.join(root, "agents.json"),
|
|
15
|
+
registryCache: path.join(root, "registry-cache.json"),
|
|
16
|
+
plugins: path.join(root, "plugins"),
|
|
17
|
+
pluginsData: path.join(root, "plugins", "data"),
|
|
18
|
+
pluginRegistry: path.join(root, "plugins.json"),
|
|
19
|
+
logs: path.join(root, "logs"),
|
|
20
|
+
pid: path.join(root, "openacp.pid"),
|
|
21
|
+
running: path.join(root, "running"),
|
|
22
|
+
apiPort: path.join(root, "api.port"),
|
|
23
|
+
apiSecret: path.join(root, "api-secret"),
|
|
24
|
+
bin: path.join(root, "bin"),
|
|
25
|
+
cache: path.join(root, "cache"),
|
|
26
|
+
tunnels: path.join(root, "tunnels.json"),
|
|
27
|
+
agentsDir: path.join(root, "agents")
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function generateSlug(name) {
|
|
32
|
+
const slug = name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
33
|
+
return slug || "openacp";
|
|
34
|
+
}
|
|
35
|
+
function expandHome(p) {
|
|
36
|
+
if (p.startsWith("~")) return path.join(os.homedir(), p.slice(1));
|
|
37
|
+
return p;
|
|
38
|
+
}
|
|
39
|
+
function resolveInstanceRoot(opts) {
|
|
40
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
41
|
+
if (opts.dir) return path.join(expandHome(opts.dir), ".openacp");
|
|
42
|
+
if (opts.local) return path.join(cwd, ".openacp");
|
|
43
|
+
if (opts.global) return path.join(os.homedir(), ".openacp");
|
|
44
|
+
const localRoot = path.join(cwd, ".openacp");
|
|
45
|
+
if (fs.existsSync(localRoot)) return localRoot;
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
function getGlobalRoot() {
|
|
49
|
+
return path.join(os.homedir(), ".openacp");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export {
|
|
53
|
+
createInstanceContext,
|
|
54
|
+
generateSlug,
|
|
55
|
+
resolveInstanceRoot,
|
|
56
|
+
getGlobalRoot
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=chunk-ON7HB5O7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/instance-context.ts"],"sourcesContent":["import path from 'node:path'\nimport fs from 'node:fs'\nimport os from 'node:os'\n\nexport interface InstanceContext {\n id: string\n root: string\n isGlobal: boolean\n paths: {\n config: string\n sessions: string\n agents: string\n registryCache: string\n plugins: string\n pluginsData: string\n pluginRegistry: string\n logs: string\n pid: string\n running: string\n apiPort: string\n apiSecret: string\n bin: string\n cache: string\n tunnels: string\n agentsDir: string\n }\n}\n\nexport interface CreateInstanceContextOpts {\n id: string\n root: string\n isGlobal: boolean\n}\n\nexport function createInstanceContext(opts: CreateInstanceContextOpts): InstanceContext {\n const { id, root, isGlobal } = opts\n return {\n id, root, isGlobal,\n paths: {\n config: path.join(root, 'config.json'),\n sessions: path.join(root, 'sessions.json'),\n agents: path.join(root, 'agents.json'),\n registryCache: path.join(root, 'registry-cache.json'),\n plugins: path.join(root, 'plugins'),\n pluginsData: path.join(root, 'plugins', 'data'),\n pluginRegistry: path.join(root, 'plugins.json'),\n logs: path.join(root, 'logs'),\n pid: path.join(root, 'openacp.pid'),\n running: path.join(root, 'running'),\n apiPort: path.join(root, 'api.port'),\n apiSecret: path.join(root, 'api-secret'),\n bin: path.join(root, 'bin'),\n cache: path.join(root, 'cache'),\n tunnels: path.join(root, 'tunnels.json'),\n agentsDir: path.join(root, 'agents'),\n },\n }\n}\n\nexport function generateSlug(name: string): string {\n const slug = name.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '')\n return slug || 'openacp'\n}\n\nfunction expandHome(p: string): string {\n if (p.startsWith('~')) return path.join(os.homedir(), p.slice(1))\n return p\n}\n\nexport interface ResolveOpts {\n dir?: string\n local?: boolean\n global?: boolean\n cwd?: string\n}\n\nexport function resolveInstanceRoot(opts: ResolveOpts): string | null {\n const cwd = opts.cwd ?? process.cwd()\n if (opts.dir) return path.join(expandHome(opts.dir), '.openacp')\n if (opts.local) return path.join(cwd, '.openacp')\n if (opts.global) return path.join(os.homedir(), '.openacp')\n const localRoot = path.join(cwd, '.openacp')\n if (fs.existsSync(localRoot)) return localRoot\n return null\n}\n\nexport function getGlobalRoot(): string {\n return path.join(os.homedir(), '.openacp')\n}\n"],"mappings":";AAAA,OAAO,UAAU;AACjB,OAAO,QAAQ;AACf,OAAO,QAAQ;AAgCR,SAAS,sBAAsB,MAAkD;AACtF,QAAM,EAAE,IAAI,MAAM,SAAS,IAAI;AAC/B,SAAO;AAAA,IACL;AAAA,IAAI;AAAA,IAAM;AAAA,IACV,OAAO;AAAA,MACL,QAAQ,KAAK,KAAK,MAAM,aAAa;AAAA,MACrC,UAAU,KAAK,KAAK,MAAM,eAAe;AAAA,MACzC,QAAQ,KAAK,KAAK,MAAM,aAAa;AAAA,MACrC,eAAe,KAAK,KAAK,MAAM,qBAAqB;AAAA,MACpD,SAAS,KAAK,KAAK,MAAM,SAAS;AAAA,MAClC,aAAa,KAAK,KAAK,MAAM,WAAW,MAAM;AAAA,MAC9C,gBAAgB,KAAK,KAAK,MAAM,cAAc;AAAA,MAC9C,MAAM,KAAK,KAAK,MAAM,MAAM;AAAA,MAC5B,KAAK,KAAK,KAAK,MAAM,aAAa;AAAA,MAClC,SAAS,KAAK,KAAK,MAAM,SAAS;AAAA,MAClC,SAAS,KAAK,KAAK,MAAM,UAAU;AAAA,MACnC,WAAW,KAAK,KAAK,MAAM,YAAY;AAAA,MACvC,KAAK,KAAK,KAAK,MAAM,KAAK;AAAA,MAC1B,OAAO,KAAK,KAAK,MAAM,OAAO;AAAA,MAC9B,SAAS,KAAK,KAAK,MAAM,cAAc;AAAA,MACvC,WAAW,KAAK,KAAK,MAAM,QAAQ;AAAA,IACrC;AAAA,EACF;AACF;AAEO,SAAS,aAAa,MAAsB;AACjD,QAAM,OAAO,KAAK,YAAY,EAAE,QAAQ,eAAe,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,UAAU,EAAE;AACpG,SAAO,QAAQ;AACjB;AAEA,SAAS,WAAW,GAAmB;AACrC,MAAI,EAAE,WAAW,GAAG,EAAG,QAAO,KAAK,KAAK,GAAG,QAAQ,GAAG,EAAE,MAAM,CAAC,CAAC;AAChE,SAAO;AACT;AASO,SAAS,oBAAoB,MAAkC;AACpE,QAAM,MAAM,KAAK,OAAO,QAAQ,IAAI;AACpC,MAAI,KAAK,IAAK,QAAO,KAAK,KAAK,WAAW,KAAK,GAAG,GAAG,UAAU;AAC/D,MAAI,KAAK,MAAO,QAAO,KAAK,KAAK,KAAK,UAAU;AAChD,MAAI,KAAK,OAAQ,QAAO,KAAK,KAAK,GAAG,QAAQ,GAAG,UAAU;AAC1D,QAAM,YAAY,KAAK,KAAK,KAAK,UAAU;AAC3C,MAAI,GAAG,WAAW,SAAS,EAAG,QAAO;AACrC,SAAO;AACT;AAEO,SAAS,gBAAwB;AACtC,SAAO,KAAK,KAAK,GAAG,QAAQ,GAAG,UAAU;AAC3C;","names":[]}
|
|
@@ -10,6 +10,7 @@ function createSecurityPlugin() {
|
|
|
10
10
|
description: "User access control and session limits",
|
|
11
11
|
essential: false,
|
|
12
12
|
permissions: ["services:register", "middleware:register", "kernel:access", "commands:register"],
|
|
13
|
+
inheritableKeys: ["allowedUsers", "maxSessionsPerUser", "rateLimits"],
|
|
13
14
|
async install(ctx) {
|
|
14
15
|
const { settings, legacyConfig, terminal } = ctx;
|
|
15
16
|
if (legacyConfig) {
|
|
@@ -122,4 +123,4 @@ var security_default = createSecurityPlugin();
|
|
|
122
123
|
export {
|
|
123
124
|
security_default
|
|
124
125
|
};
|
|
125
|
-
//# sourceMappingURL=chunk-
|
|
126
|
+
//# sourceMappingURL=chunk-OSBZXY2W.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/plugins/security/index.ts"],"sourcesContent":["import type { OpenACPPlugin, InstallContext, MiddlewarePayloadMap } from '../../core/plugin/types.js'\nimport { SecurityGuard } from './security-guard.js'\nimport type { IncomingMessage } from '../../core/types.js'\n\n// Structural type for the core fields SecurityGuard needs, avoiding\n// a direct dependency on OpenACPCore's full interface.\ninterface SecurityCoreAccess {\n configManager: ConstructorParameters<typeof SecurityGuard>[0]\n sessionManager: ConstructorParameters<typeof SecurityGuard>[1]\n}\n\n// Factory function pattern (closure for state)\nfunction createSecurityPlugin(): OpenACPPlugin {\n return {\n name: '@openacp/security',\n version: '1.0.0',\n description: 'User access control and session limits',\n essential: false,\n permissions: ['services:register', 'middleware:register', 'kernel:access', 'commands:register'],\n inheritableKeys: ['allowedUsers', 'maxSessionsPerUser', 'rateLimits'],\n\n async install(ctx: InstallContext) {\n const { settings, legacyConfig, terminal } = ctx\n\n // Migrate from legacy config if present\n if (legacyConfig) {\n const securityCfg = legacyConfig.security as Record<string, unknown> | undefined\n if (securityCfg) {\n await settings.setAll({\n allowedUserIds: securityCfg.allowedUserIds ?? [],\n maxConcurrentSessions: securityCfg.maxConcurrentSessions ?? 20,\n sessionTimeoutMinutes: securityCfg.sessionTimeoutMinutes ?? 60,\n })\n terminal.log.success('Security settings migrated from legacy config')\n return\n }\n }\n\n // Save defaults (no interactive prompts needed)\n await settings.setAll({\n allowedUserIds: [],\n maxConcurrentSessions: 20,\n sessionTimeoutMinutes: 60,\n })\n terminal.log.success('Security defaults 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: 'allowedUsers', label: 'Edit allowed user IDs' },\n { value: 'maxSessions', label: `Max concurrent sessions (current: ${current.maxConcurrentSessions ?? 20})` },\n { value: 'timeout', label: `Session timeout minutes (current: ${current.sessionTimeoutMinutes ?? 60})` },\n { value: 'done', label: 'Done' },\n ],\n })\n\n if (choice === 'allowedUsers') {\n const currentIds = (current.allowedUserIds as string[]) ?? []\n const val = await terminal.text({\n message: 'Allowed user IDs (comma-separated, empty = allow all):',\n defaultValue: currentIds.join(', '),\n })\n const ids = val.split(',').map((s) => s.trim()).filter(Boolean)\n await settings.set('allowedUserIds', ids)\n terminal.log.success('Allowed user IDs updated')\n } else if (choice === 'maxSessions') {\n const val = await terminal.text({\n message: 'Max concurrent sessions:',\n defaultValue: String(current.maxConcurrentSessions ?? 20),\n validate: (v) => {\n const n = Number(v.trim())\n if (isNaN(n) || n < 1) return 'Must be a positive number'\n return undefined\n },\n })\n await settings.set('maxConcurrentSessions', Number(val.trim()))\n terminal.log.success('Max sessions updated')\n } else if (choice === 'timeout') {\n const val = await terminal.text({\n message: 'Session timeout (minutes):',\n defaultValue: String(current.sessionTimeoutMinutes ?? 60),\n validate: (v) => {\n const n = Number(v.trim())\n if (isNaN(n) || n < 1) return 'Must be a positive number'\n return undefined\n },\n })\n await settings.set('sessionTimeoutMinutes', Number(val.trim()))\n terminal.log.success('Session timeout updated')\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('Security settings cleared')\n }\n },\n\n async setup(ctx) {\n const core = ctx.core as SecurityCoreAccess\n const guard = new SecurityGuard(core.configManager, core.sessionManager)\n\n // Register middleware for message:incoming — block unauthorized users\n ctx.registerMiddleware('message:incoming', {\n handler: async (payload: MiddlewarePayloadMap['message:incoming'], next) => {\n const access = guard.checkAccess(payload as unknown as IncomingMessage)\n if (!access.allowed) {\n ctx.log.info(`Access denied: ${access.reason}`)\n return null // block\n }\n return next()\n }\n })\n\n // Register SecurityGuard as the service directly\n ctx.registerService('security', guard)\n\n ctx.registerCommand({\n name: 'dangerous',\n description: 'Toggle dangerous mode (auto-approve all permissions)',\n usage: 'on|off',\n category: 'plugin',\n handler: async (args) => {\n const mode = args.raw.trim().toLowerCase()\n if (mode === 'on') return { type: 'text', text: 'Dangerous mode enabled — all permissions will be auto-approved.' }\n if (mode === 'off') return { type: 'text', text: 'Dangerous mode disabled — permissions require manual approval.' }\n return { type: 'menu', title: 'Dangerous Mode', options: [\n { label: 'Enable', command: '/dangerous on' },\n { label: 'Disable', command: '/dangerous off' },\n ]}\n },\n })\n\n ctx.log.info('Security service ready')\n },\n }\n}\n\nexport default createSecurityPlugin()\n"],"mappings":"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","names":[]}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MAX_RETRIES
|
|
3
|
+
} from "./chunk-NHD5XDD2.js";
|
|
4
|
+
|
|
1
5
|
// src/plugins/tunnel/index.ts
|
|
6
|
+
import path from "path";
|
|
2
7
|
function createTunnelPlugin() {
|
|
3
8
|
let service = null;
|
|
4
9
|
return {
|
|
@@ -110,6 +115,7 @@ function createTunnelPlugin() {
|
|
|
110
115
|
ctx.terminal.log.success("Tunnel settings cleared");
|
|
111
116
|
}
|
|
112
117
|
},
|
|
118
|
+
inheritableKeys: ["provider", "maxUserTunnels", "auth"],
|
|
113
119
|
async setup(ctx) {
|
|
114
120
|
const config = ctx.pluginConfig;
|
|
115
121
|
if (!config.enabled) {
|
|
@@ -120,8 +126,12 @@ function createTunnelPlugin() {
|
|
|
120
126
|
ctx.log.info("Tunnel disabled (no provider configured)");
|
|
121
127
|
return;
|
|
122
128
|
}
|
|
123
|
-
const { TunnelService } = await import("./tunnel-service-
|
|
124
|
-
const
|
|
129
|
+
const { TunnelService } = await import("./tunnel-service-TBAHDXMF.js");
|
|
130
|
+
const instanceRoot = ctx.instanceRoot;
|
|
131
|
+
const tunnelSvc = new TunnelService(
|
|
132
|
+
config,
|
|
133
|
+
path.join(instanceRoot, "tunnels.json")
|
|
134
|
+
);
|
|
125
135
|
const publicUrl = await tunnelSvc.start();
|
|
126
136
|
service = tunnelSvc;
|
|
127
137
|
ctx.registerService("tunnel", tunnelSvc);
|
|
@@ -137,8 +147,8 @@ function createTunnelPlugin() {
|
|
|
137
147
|
try {
|
|
138
148
|
await tunnelSvc.stopTunnel(port);
|
|
139
149
|
return { type: "text", text: `Tunnel on port ${port} stopped.` };
|
|
140
|
-
} catch (
|
|
141
|
-
return { type: "error", message:
|
|
150
|
+
} catch (err2) {
|
|
151
|
+
return { type: "error", message: err2.message };
|
|
142
152
|
}
|
|
143
153
|
}
|
|
144
154
|
if (parts[0] && parts[0] !== "") {
|
|
@@ -148,12 +158,16 @@ function createTunnelPlugin() {
|
|
|
148
158
|
try {
|
|
149
159
|
const entry = await tunnelSvc.addTunnel(port, { label });
|
|
150
160
|
return { type: "text", text: `Tunnel created: ${entry.publicUrl ?? "starting..."}` };
|
|
151
|
-
} catch (
|
|
152
|
-
return { type: "error", message:
|
|
161
|
+
} catch (err2) {
|
|
162
|
+
return { type: "error", message: err2.message };
|
|
153
163
|
}
|
|
154
164
|
}
|
|
155
165
|
const url = tunnelSvc.getPublicUrl();
|
|
156
|
-
|
|
166
|
+
const err = tunnelSvc.getStartError();
|
|
167
|
+
let text = url ? `Tunnel: ${url}` : "No tunnel active.";
|
|
168
|
+
if (err) text += `
|
|
169
|
+
\u26A0\uFE0F System tunnel error: ${err}`;
|
|
170
|
+
return { type: "text", text };
|
|
157
171
|
}
|
|
158
172
|
});
|
|
159
173
|
ctx.registerCommand({
|
|
@@ -163,12 +177,17 @@ function createTunnelPlugin() {
|
|
|
163
177
|
handler: async () => {
|
|
164
178
|
const userTunnels = tunnelSvc.listTunnels();
|
|
165
179
|
const systemUrl = tunnelSvc.getPublicUrl();
|
|
180
|
+
const sysError = tunnelSvc.getStartError();
|
|
181
|
+
const systemDetail = sysError ? `${systemUrl} \u26A0\uFE0F ${sysError}` : systemUrl;
|
|
166
182
|
const items = [
|
|
167
|
-
{ label: "System", detail:
|
|
168
|
-
...userTunnels.map((t) =>
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
183
|
+
{ label: "System", detail: systemDetail },
|
|
184
|
+
...userTunnels.map((t) => {
|
|
185
|
+
const statusInfo = t.status === "failed" && t.retryCount > 0 ? `${t.status} (retry ${t.retryCount}/${MAX_RETRIES})` : t.status;
|
|
186
|
+
return {
|
|
187
|
+
label: t.label ?? `Port ${t.port}`,
|
|
188
|
+
detail: `${t.publicUrl ?? statusInfo} (${t.provider})`
|
|
189
|
+
};
|
|
190
|
+
})
|
|
172
191
|
];
|
|
173
192
|
return { type: "list", title: "Active Tunnels", items };
|
|
174
193
|
}
|
|
@@ -187,4 +206,4 @@ var tunnel_default = createTunnelPlugin();
|
|
|
187
206
|
export {
|
|
188
207
|
tunnel_default
|
|
189
208
|
};
|
|
190
|
-
//# sourceMappingURL=chunk-
|
|
209
|
+
//# sourceMappingURL=chunk-P3HHJANC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/plugins/tunnel/index.ts"],"sourcesContent":["import path from 'node:path'\nimport type { OpenACPPlugin, InstallContext } from '../../core/plugin/types.js'\nimport type { TunnelConfig } from '../../core/config/config.js'\nimport { MAX_RETRIES } from './tunnel-registry.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 inheritableKeys: ['provider', 'maxUserTunnels', 'auth'],\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 instanceRoot = ctx.instanceRoot\n const tunnelSvc = new TunnelService(\n config as unknown as TunnelConfig,\n path.join(instanceRoot, 'tunnels.json'),\n )\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 + health\n const url = tunnelSvc.getPublicUrl()\n const err = tunnelSvc.getStartError()\n let text = url ? `Tunnel: ${url}` : 'No tunnel active.'\n if (err) text += `\\n⚠️ System tunnel error: ${err}`\n return { type: 'text', text }\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 sysError = tunnelSvc.getStartError()\n const systemDetail = sysError ? `${systemUrl} ⚠️ ${sysError}` : systemUrl\n const items = [\n { label: 'System', detail: systemDetail },\n ...userTunnels.map(t => {\n const statusInfo = t.status === 'failed' && t.retryCount > 0\n ? `${t.status} (retry ${t.retryCount}/${MAX_RETRIES})`\n : t.status\n return {\n label: t.label ?? `Port ${t.port}`,\n detail: `${t.publicUrl ?? statusInfo} (${t.provider})`,\n }\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":["err"]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getAgentCapabilities
|
|
3
|
-
} from "./chunk-ZSLHHQPQ.js";
|
|
4
1
|
import {
|
|
5
2
|
createChildLogger
|
|
6
3
|
} from "./chunk-R6KZYF7D.js";
|
|
4
|
+
import {
|
|
5
|
+
getAgentCapabilities
|
|
6
|
+
} from "./chunk-ZSLHHQPQ.js";
|
|
7
7
|
|
|
8
8
|
// src/plugins/api-server/api-server.ts
|
|
9
9
|
import * as http from "http";
|
|
@@ -592,7 +592,7 @@ function redactDeep(obj) {
|
|
|
592
592
|
}
|
|
593
593
|
function registerConfigRoutes(router, deps) {
|
|
594
594
|
router.get("/api/config/editable", async (_req, res) => {
|
|
595
|
-
const { getSafeFields, resolveOptions, getConfigValue } = await import("./config-registry-
|
|
595
|
+
const { getSafeFields, resolveOptions, getConfigValue } = await import("./config-registry-M3FFWEVM.js");
|
|
596
596
|
const config = deps.core.configManager.get();
|
|
597
597
|
const safeFields = getSafeFields();
|
|
598
598
|
const fields = safeFields.map((def) => ({
|
|
@@ -634,7 +634,7 @@ function registerConfigRoutes(router, deps) {
|
|
|
634
634
|
deps.sendJson(res, 400, { error: "Invalid config path" });
|
|
635
635
|
return;
|
|
636
636
|
}
|
|
637
|
-
const { getFieldDef } = await import("./config-registry-
|
|
637
|
+
const { getFieldDef } = await import("./config-registry-M3FFWEVM.js");
|
|
638
638
|
const fieldDef = getFieldDef(configPath);
|
|
639
639
|
if (!fieldDef || fieldDef.scope !== "safe") {
|
|
640
640
|
deps.sendJson(res, 403, {
|
|
@@ -659,7 +659,7 @@ function registerConfigRoutes(router, deps) {
|
|
|
659
659
|
}
|
|
660
660
|
const lastKey = parts[parts.length - 1];
|
|
661
661
|
target[lastKey] = value;
|
|
662
|
-
const { ConfigSchema } = await import("./config-
|
|
662
|
+
const { ConfigSchema } = await import("./config-X4UP7H6R.js");
|
|
663
663
|
const result = ConfigSchema.safeParse(cloned);
|
|
664
664
|
if (!result.success) {
|
|
665
665
|
deps.sendJson(res, 400, {
|
|
@@ -679,7 +679,7 @@ function registerConfigRoutes(router, deps) {
|
|
|
679
679
|
}
|
|
680
680
|
updateTarget[lastKey] = value;
|
|
681
681
|
await deps.core.configManager.save(updates, configPath);
|
|
682
|
-
const { isHotReloadable } = await import("./config-registry-
|
|
682
|
+
const { isHotReloadable } = await import("./config-registry-M3FFWEVM.js");
|
|
683
683
|
const needsRestart = !isHotReloadable(configPath);
|
|
684
684
|
deps.sendJson(res, 200, {
|
|
685
685
|
ok: true,
|
|
@@ -867,7 +867,6 @@ function registerNotifyRoutes(router, deps) {
|
|
|
867
867
|
|
|
868
868
|
// src/plugins/api-server/api-server.ts
|
|
869
869
|
var log2 = createChildLogger({ module: "api-server" });
|
|
870
|
-
var DEFAULT_PORT_FILE = path2.join(os.homedir(), ".openacp", "api.port");
|
|
871
870
|
var cachedVersion;
|
|
872
871
|
function getVersion() {
|
|
873
872
|
if (cachedVersion) return cachedVersion;
|
|
@@ -889,7 +888,7 @@ var ApiServer = class {
|
|
|
889
888
|
this.core = core;
|
|
890
889
|
this.config = config;
|
|
891
890
|
this.topicManager = topicManager;
|
|
892
|
-
this.portFilePath = portFilePath ??
|
|
891
|
+
this.portFilePath = portFilePath ?? path2.join(os.homedir(), ".openacp", "api.port");
|
|
893
892
|
this.secretFilePath = secretFilePath ?? path2.join(os.homedir(), ".openacp", "api-secret");
|
|
894
893
|
this.staticServer = new StaticServer(uiDir);
|
|
895
894
|
this.sseManager = new SSEManager(
|
|
@@ -1113,4 +1112,4 @@ export {
|
|
|
1113
1112
|
StaticServer,
|
|
1114
1113
|
ApiServer
|
|
1115
1114
|
};
|
|
1116
|
-
//# sourceMappingURL=chunk-
|
|
1115
|
+
//# sourceMappingURL=chunk-R2YLDQLI.js.map
|