@openacp/cli 0.6.1 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/dist/{admin-IKPS5PFC.js → admin-3ZHEO5VP.js} +8 -4
  2. package/dist/{agent-catalog-T5ECPEDA.js → agent-catalog-IVU2KANH.js} +2 -2
  3. package/dist/{agents-55NX3DHM.js → agents-SXIY4IEF.js} +2 -2
  4. package/dist/{chunk-H7ZMPBZC.js → chunk-3KGRVAEV.js} +1 -1
  5. package/dist/chunk-3KGRVAEV.js.map +1 -0
  6. package/dist/{chunk-GINCOFNW.js → chunk-6LSFRNHE.js} +2 -2
  7. package/dist/chunk-6LSFRNHE.js.map +1 -0
  8. package/dist/{chunk-UB7XUO7C.js → chunk-AVCHZESZ.js} +3 -3
  9. package/dist/{chunk-J6X5SW6O.js → chunk-CKOK7JW6.js} +3 -2
  10. package/dist/{chunk-J6X5SW6O.js.map → chunk-CKOK7JW6.js.map} +1 -1
  11. package/dist/{chunk-4LFDEW22.js → chunk-EWYNCHUH.js} +33 -20
  12. package/dist/chunk-EWYNCHUH.js.map +1 -0
  13. package/dist/{chunk-4TR5Y3MP.js → chunk-F3AICYO4.js} +39 -1
  14. package/dist/chunk-F3AICYO4.js.map +1 -0
  15. package/dist/{chunk-LGQYTK55.js → chunk-FMWSVLRM.js} +31 -2
  16. package/dist/chunk-FMWSVLRM.js.map +1 -0
  17. package/dist/{chunk-AKIU4JBF.js → chunk-FZ5BIWG5.js} +6 -6
  18. package/dist/chunk-FZ5BIWG5.js.map +1 -0
  19. package/dist/{chunk-2KJC3ILH.js → chunk-G3OHCXZG.js} +34 -24
  20. package/dist/chunk-G3OHCXZG.js.map +1 -0
  21. package/dist/{chunk-R3UJUOXI.js → chunk-HP2IJYCA.js} +1817 -1015
  22. package/dist/chunk-HP2IJYCA.js.map +1 -0
  23. package/dist/chunk-IER5UCY7.js +298 -0
  24. package/dist/chunk-IER5UCY7.js.map +1 -0
  25. package/dist/{chunk-TOZQ3JFN.js → chunk-KO5RL7MZ.js} +2 -2
  26. package/dist/{chunk-7G5QKLLF.js → chunk-NXEQXRQR.js} +53 -9
  27. package/dist/chunk-NXEQXRQR.js.map +1 -0
  28. package/dist/{chunk-ZCHNAM3B.js → chunk-OHR6SBMC.js} +3 -3
  29. package/dist/chunk-PWFPTG5X.js +101 -0
  30. package/dist/chunk-PWFPTG5X.js.map +1 -0
  31. package/dist/{chunk-IMILOCR5.js → chunk-TMCQZAXN.js} +2 -2
  32. package/dist/chunk-TMCQZAXN.js.map +1 -0
  33. package/dist/{chunk-T22OLSET.js → chunk-TTDSLV35.js} +1 -1
  34. package/dist/chunk-TTDSLV35.js.map +1 -0
  35. package/dist/cli.js +41 -36
  36. package/dist/cli.js.map +1 -1
  37. package/dist/{config-AK2W3E67.js → config-4YSJ4NCI.js} +2 -2
  38. package/dist/{config-editor-VIA7A72X.js → config-editor-F25HEMGL.js} +4 -4
  39. package/dist/{config-registry-QQOJ2GQP.js → config-registry-7I6GGDOY.js} +2 -2
  40. package/dist/{daemon-G27YZUWB.js → daemon-I6XMRQ6P.js} +3 -3
  41. package/dist/{discord-2DKRH45T.js → discord-VHCBN3JJ.js} +226 -172
  42. package/dist/discord-VHCBN3JJ.js.map +1 -0
  43. package/dist/{doctor-CHCYUTV5.js → doctor-GPW5ECK6.js} +4 -4
  44. package/dist/doctor-Y3SCSVPI.js +9 -0
  45. package/dist/index.d.ts +176 -90
  46. package/dist/index.js +22 -12
  47. package/dist/install-cloudflared-G2GUKCHA.js +32 -0
  48. package/dist/install-cloudflared-G2GUKCHA.js.map +1 -0
  49. package/dist/install-jq-7QTU7XYY.js +31 -0
  50. package/dist/install-jq-7QTU7XYY.js.map +1 -0
  51. package/dist/{integrate-VOUYBPPZ.js → integrate-O4OCR4SN.js} +23 -11
  52. package/dist/integrate-O4OCR4SN.js.map +1 -0
  53. package/dist/{main-56SPFYW4.js → main-P4X6SAPZ.js} +29 -18
  54. package/dist/main-P4X6SAPZ.js.map +1 -0
  55. package/dist/{new-session-DRRP2J7E.js → new-session-PUNUHGYP.js} +3 -3
  56. package/dist/post-upgrade-6N4JCV5S.js +79 -0
  57. package/dist/post-upgrade-6N4JCV5S.js.map +1 -0
  58. package/dist/{session-FVFLBREJ.js → session-ZMAM67AA.js} +2 -2
  59. package/dist/{settings-LPOLJ6SA.js → settings-OEQEZS5Y.js} +3 -2
  60. package/dist/{setup-IPWJCIJM.js → setup-7YBFKRG7.js} +5 -3
  61. package/dist/{tunnel-service-U6V4HQOO.js → tunnel-service-BMIBHUBK.js} +35 -17
  62. package/dist/tunnel-service-BMIBHUBK.js.map +1 -0
  63. package/package.json +2 -1
  64. package/dist/chunk-2KJC3ILH.js.map +0 -1
  65. package/dist/chunk-4LFDEW22.js.map +0 -1
  66. package/dist/chunk-4TR5Y3MP.js.map +0 -1
  67. package/dist/chunk-7G5QKLLF.js.map +0 -1
  68. package/dist/chunk-AKIU4JBF.js.map +0 -1
  69. package/dist/chunk-GINCOFNW.js.map +0 -1
  70. package/dist/chunk-H7ZMPBZC.js.map +0 -1
  71. package/dist/chunk-IMILOCR5.js.map +0 -1
  72. package/dist/chunk-LGQYTK55.js.map +0 -1
  73. package/dist/chunk-R3UJUOXI.js.map +0 -1
  74. package/dist/chunk-RF3DUYFO.js +0 -103
  75. package/dist/chunk-RF3DUYFO.js.map +0 -1
  76. package/dist/chunk-T22OLSET.js.map +0 -1
  77. package/dist/chunk-THBR6OXH.js +0 -62
  78. package/dist/chunk-THBR6OXH.js.map +0 -1
  79. package/dist/discord-2DKRH45T.js.map +0 -1
  80. package/dist/doctor-AN6AZ3PF.js +0 -9
  81. package/dist/install-cloudflared-BTGUD7SW.js +0 -8
  82. package/dist/integrate-VOUYBPPZ.js.map +0 -1
  83. package/dist/main-56SPFYW4.js.map +0 -1
  84. package/dist/setup-IPWJCIJM.js.map +0 -1
  85. package/dist/tunnel-service-U6V4HQOO.js.map +0 -1
  86. /package/dist/{admin-IKPS5PFC.js.map → admin-3ZHEO5VP.js.map} +0 -0
  87. /package/dist/{agent-catalog-T5ECPEDA.js.map → agent-catalog-IVU2KANH.js.map} +0 -0
  88. /package/dist/{agents-55NX3DHM.js.map → agents-SXIY4IEF.js.map} +0 -0
  89. /package/dist/{chunk-UB7XUO7C.js.map → chunk-AVCHZESZ.js.map} +0 -0
  90. /package/dist/{chunk-TOZQ3JFN.js.map → chunk-KO5RL7MZ.js.map} +0 -0
  91. /package/dist/{chunk-ZCHNAM3B.js.map → chunk-OHR6SBMC.js.map} +0 -0
  92. /package/dist/{config-AK2W3E67.js.map → config-4YSJ4NCI.js.map} +0 -0
  93. /package/dist/{config-editor-VIA7A72X.js.map → config-editor-F25HEMGL.js.map} +0 -0
  94. /package/dist/{config-registry-QQOJ2GQP.js.map → config-registry-7I6GGDOY.js.map} +0 -0
  95. /package/dist/{daemon-G27YZUWB.js.map → daemon-I6XMRQ6P.js.map} +0 -0
  96. /package/dist/{doctor-AN6AZ3PF.js.map → doctor-GPW5ECK6.js.map} +0 -0
  97. /package/dist/{doctor-CHCYUTV5.js.map → doctor-Y3SCSVPI.js.map} +0 -0
  98. /package/dist/{install-cloudflared-BTGUD7SW.js.map → new-session-PUNUHGYP.js.map} +0 -0
  99. /package/dist/{new-session-DRRP2J7E.js.map → session-ZMAM67AA.js.map} +0 -0
  100. /package/dist/{session-FVFLBREJ.js.map → settings-OEQEZS5Y.js.map} +0 -0
  101. /package/dist/{settings-LPOLJ6SA.js.map → setup-7YBFKRG7.js.map} +0 -0
@@ -1,16 +1,20 @@
1
1
  import {
2
- buildDangerousModeKeyboard,
2
+ buildSessionControlKeyboard,
3
3
  handleDangerous,
4
4
  handleDangerousButton,
5
5
  handleRestart,
6
+ handleTTS,
7
+ handleTTSButton,
6
8
  handleUpdate
7
- } from "./chunk-7G5QKLLF.js";
9
+ } from "./chunk-NXEQXRQR.js";
8
10
  import "./chunk-ESOPMQAY.js";
9
11
  export {
10
- buildDangerousModeKeyboard,
12
+ buildSessionControlKeyboard,
11
13
  handleDangerous,
12
14
  handleDangerousButton,
13
15
  handleRestart,
16
+ handleTTS,
17
+ handleTTSButton,
14
18
  handleUpdate
15
19
  };
16
- //# sourceMappingURL=admin-IKPS5PFC.js.map
20
+ //# sourceMappingURL=admin-3ZHEO5VP.js.map
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  AgentCatalog
3
- } from "./chunk-J6X5SW6O.js";
3
+ } from "./chunk-CKOK7JW6.js";
4
4
  import "./chunk-5HGXUCMX.js";
5
5
  import "./chunk-JKBFUAJK.js";
6
6
  import "./chunk-ESOPMQAY.js";
7
7
  export {
8
8
  AgentCatalog
9
9
  };
10
- //# sourceMappingURL=agent-catalog-T5ECPEDA.js.map
10
+ //# sourceMappingURL=agent-catalog-IVU2KANH.js.map
@@ -3,7 +3,7 @@ import {
3
3
  handleAgents,
4
4
  handleInstall,
5
5
  showAgentsList
6
- } from "./chunk-H7ZMPBZC.js";
6
+ } from "./chunk-3KGRVAEV.js";
7
7
  import "./chunk-ESOPMQAY.js";
8
8
  export {
9
9
  handleAgentButton,
@@ -11,4 +11,4 @@ export {
11
11
  handleInstall,
12
12
  showAgentsList
13
13
  };
14
- //# sourceMappingURL=agents-55NX3DHM.js.map
14
+ //# sourceMappingURL=agents-SXIY4IEF.js.map
@@ -200,4 +200,4 @@ export {
200
200
  handleInstall,
201
201
  handleAgentButton
202
202
  };
203
- //# sourceMappingURL=chunk-H7ZMPBZC.js.map
203
+ //# sourceMappingURL=chunk-3KGRVAEV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/discord/commands/agents.ts"],"sourcesContent":["import {\n ActionRowBuilder,\n ButtonBuilder,\n ButtonStyle,\n} from 'discord.js'\nimport type { ChatInputCommandInteraction, ButtonInteraction } from 'discord.js'\nimport { log } from '../../../core/log.js'\nimport type { InstallProgress } from '../../../core/types.js'\nimport type { DiscordAdapter } from '../adapter.js'\n\nconst AGENTS_PER_PAGE = 5\n\nfunction buildProgressBar(percent: number): string {\n const filled = Math.round(percent / 10)\n const empty = 10 - filled\n return '█'.repeat(filled) + '░'.repeat(empty)\n}\n\nfunction truncate(text: string, maxLen: number): string {\n if (text.length <= maxLen) return text\n return text.slice(0, maxLen - 1) + '…'\n}\n\nexport async function handleAgents(\n interaction: ChatInputCommandInteraction,\n adapter: DiscordAdapter,\n page = 0,\n): Promise<void> {\n await interaction.deferReply({ ephemeral: true })\n const { content, components } = buildAgentsContent(adapter, page)\n await interaction.editReply({ content, components })\n}\n\nexport async function showAgentsList(\n interaction: ButtonInteraction,\n adapter: DiscordAdapter,\n page = 0,\n): Promise<void> {\n const { content, components } = buildAgentsContent(adapter, page)\n await interaction.followUp({ content, components, ephemeral: true })\n}\n\nfunction buildAgentsContent(\n adapter: DiscordAdapter,\n page: number,\n): { content: string; components: ActionRowBuilder<ButtonBuilder>[] } {\n const catalog = adapter.core.agentCatalog\n const items = catalog.getAvailable()\n\n const installed = items.filter((i: any) => i.installed)\n const available = items.filter((i: any) => !i.installed)\n\n let content = '**🤖 Agents**\\n\\n'\n\n if (installed.length > 0) {\n content += '**Installed:**\\n'\n for (const item of installed) {\n content += `✅ **${item.name}**`\n if (item.description) content += ` — *${truncate(item.description, 50)}*`\n content += '\\n'\n }\n content += '\\n'\n }\n\n const components: ActionRowBuilder<ButtonBuilder>[] = []\n\n if (available.length > 0) {\n const totalPages = Math.ceil(available.length / AGENTS_PER_PAGE)\n const safePage = Math.max(0, Math.min(page, totalPages - 1))\n const pageItems = available.slice(safePage * AGENTS_PER_PAGE, (safePage + 1) * AGENTS_PER_PAGE)\n\n content += `**Available to install:**`\n if (totalPages > 1) content += ` (${safePage + 1}/${totalPages})`\n content += '\\n'\n\n for (const item of pageItems) {\n if (item.available) {\n content += `⬇️ **${item.name}**`\n } else {\n const deps = item.missingDeps?.join(', ') ?? 'requirements not met'\n content += `⚠️ **${item.name}** *(needs: ${deps})*`\n }\n if (item.description) content += `\\n *${truncate(item.description, 60)}*`\n content += '\\n'\n }\n\n // Install buttons row\n const installable = pageItems.filter((i: any) => i.available)\n if (installable.length > 0) {\n const installRow = new ActionRowBuilder<ButtonBuilder>()\n for (const item of installable) {\n installRow.addComponents(\n new ButtonBuilder()\n .setCustomId(`ag:install:${item.key}`)\n .setLabel(`⬇️ ${item.name}`)\n .setStyle(ButtonStyle.Secondary),\n )\n }\n components.push(installRow)\n }\n\n // Pagination row\n if (totalPages > 1) {\n const pageRow = new ActionRowBuilder<ButtonBuilder>()\n if (safePage > 0) {\n pageRow.addComponents(\n new ButtonBuilder()\n .setCustomId(`ag:page:${safePage - 1}`)\n .setLabel('◀️ Prev')\n .setStyle(ButtonStyle.Secondary),\n )\n }\n if (safePage < totalPages - 1) {\n pageRow.addComponents(\n new ButtonBuilder()\n .setCustomId(`ag:page:${safePage + 1}`)\n .setLabel('Next ▶️')\n .setStyle(ButtonStyle.Secondary),\n )\n }\n if (pageRow.components.length > 0) components.push(pageRow)\n }\n } else {\n content += '*All agents are already installed!*'\n }\n\n return { content, components }\n}\n\nexport async function handleInstall(\n interaction: ChatInputCommandInteraction,\n adapter: DiscordAdapter,\n): Promise<void> {\n await interaction.deferReply({ ephemeral: true })\n\n const nameOrId = interaction.options.getString('name', true)\n await installAgentWithProgress(interaction, adapter, nameOrId)\n}\n\nexport async function handleAgentButton(\n interaction: ButtonInteraction,\n adapter: DiscordAdapter,\n): Promise<void> {\n const { customId } = interaction\n\n if (customId.startsWith('ag:install:')) {\n const nameOrId = customId.replace('ag:install:', '')\n try { await interaction.deferReply({ ephemeral: true }) } catch { /* ignore */ }\n await installAgentWithProgress(interaction, adapter, nameOrId)\n return\n }\n\n if (customId.startsWith('ag:page:')) {\n const page = parseInt(customId.replace('ag:page:', ''), 10)\n const { content, components } = buildAgentsContent(adapter, page)\n try {\n await interaction.update({ content, components })\n } catch (err) {\n log.warn({ err }, '[discord-agents] Failed to update page')\n }\n }\n}\n\nasync function installAgentWithProgress(\n interaction: ChatInputCommandInteraction | ButtonInteraction,\n adapter: DiscordAdapter,\n nameOrId: string,\n): Promise<void> {\n const catalog = adapter.core.agentCatalog\n\n // Track the latest status for periodic edits\n let statusText = `⏳ Installing **${nameOrId}**...`\n let lastEdit = 0\n const EDIT_THROTTLE_MS = 1500\n\n const editStatus = async (text: string) => {\n const now = Date.now()\n if (now - lastEdit > EDIT_THROTTLE_MS) {\n lastEdit = now\n statusText = text\n try {\n if (interaction.deferred || interaction.replied) {\n await interaction.editReply(text)\n }\n } catch { /* rate limit or unchanged */ }\n } else {\n statusText = text\n }\n }\n\n // Set initial message\n try {\n if (interaction.deferred || interaction.replied) {\n await interaction.editReply(statusText)\n }\n } catch { /* ignore */ }\n\n const progress: InstallProgress = {\n onStart(_id, _name) { /* initial message already sent */ },\n async onStep(step) { await editStatus(`⏳ **${nameOrId}**: ${step}`) },\n async onDownloadProgress(percent) {\n const bar = buildProgressBar(percent)\n await editStatus(`⏳ **${nameOrId}**\\nDownloading... ${bar} ${percent}%`)\n },\n async onSuccess(name) {\n const row = new ActionRowBuilder<ButtonBuilder>().addComponents(\n new ButtonBuilder()\n .setCustomId(`na:${nameOrId}`)\n .setLabel(`🚀 Start session with ${name}`)\n .setStyle(ButtonStyle.Primary),\n )\n try {\n if (interaction.deferred || interaction.replied) {\n await interaction.editReply({\n content: `✅ **${name}** installed!`,\n components: [row],\n })\n }\n } catch { /* ignore */ }\n },\n async onError(error) {\n try {\n if (interaction.deferred || interaction.replied) {\n await interaction.editReply(`❌ ${error}`)\n }\n } catch { /* ignore */ }\n },\n }\n\n const result = await catalog.install(nameOrId, progress)\n\n // Show setup steps as a follow-up message\n if (result.ok && result.setupSteps?.length) {\n let setupText = `📋 **Setup for ${result.agentKey}:**\\n\\n`\n for (const step of result.setupSteps) {\n setupText += `→ ${step}\\n`\n }\n setupText += `\\n*Run in terminal: \\`openacp agents info ${result.agentKey}\\`*`\n try {\n await interaction.followUp({ content: setupText, ephemeral: true })\n } catch { /* ignore */ }\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  DoctorEngine
3
- } from "./chunk-ZCHNAM3B.js";
3
+ } from "./chunk-OHR6SBMC.js";
4
4
  import {
5
5
  log
6
6
  } from "./chunk-ESOPMQAY.js";
@@ -131,4 +131,4 @@ export {
131
131
  runDoctorInline,
132
132
  handleDoctorButton
133
133
  };
134
- //# sourceMappingURL=chunk-GINCOFNW.js.map
134
+ //# sourceMappingURL=chunk-6LSFRNHE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/discord/commands/doctor.ts"],"sourcesContent":["import {\n ActionRowBuilder,\n ButtonBuilder,\n ButtonStyle,\n} from 'discord.js'\nimport type { ChatInputCommandInteraction, ButtonInteraction } from 'discord.js'\nimport { DoctorEngine } from '../../../core/doctor/index.js'\nimport type { DoctorReport, PendingFix } from '../../../core/doctor/types.js'\nimport { log } from '../../../core/log.js'\nimport type { DiscordAdapter } from '../adapter.js'\n\n// In-memory store of pending fixes keyed by \"guildId:channelId:messageId\"\nconst pendingFixesStore = new Map<string, PendingFix[]>()\n\nfunction renderReport(report: DoctorReport): {\n content: string\n components: ActionRowBuilder<ButtonBuilder>[]\n} {\n const icons = { pass: '✅', warn: '⚠️', fail: '❌' }\n const lines: string[] = ['🩺 **OpenACP Doctor**\\n']\n\n for (const category of report.categories) {\n lines.push(`**${category.name}**`)\n for (const result of category.results) {\n lines.push(` ${icons[result.status]} ${result.message}`)\n }\n lines.push('')\n }\n\n const { passed, warnings, failed, fixed } = report.summary\n const fixedStr = fixed > 0 ? `, ${fixed} fixed` : ''\n lines.push(`**Result:** ${passed} passed, ${warnings} warnings, ${failed} failed${fixedStr}`)\n\n const components: ActionRowBuilder<ButtonBuilder>[] = []\n\n if (report.pendingFixes.length > 0) {\n const row = new ActionRowBuilder<ButtonBuilder>()\n for (let i = 0; i < Math.min(report.pendingFixes.length, 5); i++) {\n row.addComponents(\n new ButtonBuilder()\n .setCustomId(`m:doctor:fix:${i}`)\n .setLabel(`🔧 Fix: ${report.pendingFixes[i].message.slice(0, 30)}`)\n .setStyle(ButtonStyle.Primary),\n )\n }\n components.push(row)\n }\n\n return { content: lines.join('\\n'), components }\n}\n\nexport async function handleDoctor(\n interaction: ChatInputCommandInteraction,\n _adapter: DiscordAdapter,\n): Promise<void> {\n await interaction.deferReply({ ephemeral: true })\n\n try {\n const engine = new DoctorEngine()\n const report = await engine.runAll()\n const { content, components } = renderReport(report)\n\n // Store pending fixes for button callbacks\n const storeKey = `${interaction.guildId}:${interaction.channelId}`\n if (report.pendingFixes.length > 0) {\n pendingFixesStore.set(storeKey, report.pendingFixes)\n }\n\n await interaction.editReply({ content, components })\n } catch (err) {\n log.error({ err }, '[discord-doctor] Doctor command failed')\n await interaction.editReply(\n `❌ Doctor failed: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n}\n\nexport async function runDoctorInline(\n interaction: ButtonInteraction,\n _adapter: DiscordAdapter,\n): Promise<void> {\n try {\n const engine = new DoctorEngine()\n const report = await engine.runAll()\n const { content, components } = renderReport(report)\n\n const storeKey = `${interaction.guildId}:${interaction.channelId}`\n if (report.pendingFixes.length > 0) {\n pendingFixesStore.set(storeKey, report.pendingFixes)\n }\n\n await interaction.followUp({ content, components, ephemeral: true })\n } catch (err) {\n log.error({ err }, '[discord-doctor] Doctor inline failed')\n await interaction.followUp({\n content: `❌ Doctor failed: ${err instanceof Error ? err.message : String(err)}`,\n ephemeral: true,\n })\n }\n}\n\nexport async function handleDoctorButton(\n interaction: ButtonInteraction,\n _adapter: DiscordAdapter,\n): Promise<void> {\n const { customId } = interaction\n\n if (customId === 'm:doctor') {\n try { await interaction.deferUpdate() } catch { /* ignore */ }\n await runDoctorInline(interaction, _adapter)\n return\n }\n\n if (customId.startsWith('m:doctor:fix:')) {\n const index = parseInt(customId.replace('m:doctor:fix:', ''), 10)\n const storeKey = `${interaction.guildId}:${interaction.channelId}`\n const fixes = pendingFixesStore.get(storeKey)\n\n try { await interaction.deferUpdate() } catch { /* ignore */ }\n\n if (!fixes || index < 0 || index >= fixes.length) {\n try { await interaction.followUp({ content: '⚠️ Fix no longer available.', ephemeral: true }) } catch { /* */ }\n return\n }\n\n const pending = fixes[index]\n try {\n const result = await pending.fix()\n if (result.success) {\n // Re-run doctor to show updated status\n const engine = new DoctorEngine()\n const report = await engine.runAll()\n const { content, components } = renderReport(report)\n\n if (report.pendingFixes.length > 0) {\n pendingFixesStore.set(storeKey, report.pendingFixes)\n } else {\n pendingFixesStore.delete(storeKey)\n }\n\n try { await interaction.followUp({ content, components, ephemeral: true }) } catch { /* ignore */ }\n } else {\n try { await interaction.followUp({ content: `❌ Fix failed: ${result.message}`, ephemeral: true }) } catch { /* */ }\n }\n } catch (err) {\n log.error({ err, index }, '[discord-doctor] Doctor fix callback failed')\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -8,10 +8,10 @@ import {
8
8
  validateBotToken,
9
9
  validateChatId,
10
10
  validateDiscordToken
11
- } from "./chunk-2KJC3ILH.js";
11
+ } from "./chunk-G3OHCXZG.js";
12
12
  import {
13
13
  expandHome
14
- } from "./chunk-4LFDEW22.js";
14
+ } from "./chunk-EWYNCHUH.js";
15
15
 
16
16
  // src/core/config-editor.ts
17
17
  import { select, input } from "@inquirer/prompts";
@@ -706,4 +706,4 @@ function flattenToPaths(obj, prefix = "") {
706
706
  export {
707
707
  runConfigEditor
708
708
  };
709
- //# sourceMappingURL=chunk-UB7XUO7C.js.map
709
+ //# sourceMappingURL=chunk-AVCHZESZ.js.map
@@ -196,7 +196,8 @@ function validateExtractedPaths(destDir) {
196
196
  const realDest = fs.realpathSync(destDir);
197
197
  const entries = fs.readdirSync(destDir, { recursive: true, withFileTypes: true });
198
198
  for (const entry of entries) {
199
- const parentPath = entry.parentPath ?? entry.path;
199
+ const dirent = entry;
200
+ const parentPath = dirent.parentPath ?? dirent.path ?? destDir;
200
201
  const fullPath = path.join(parentPath, entry.name);
201
202
  let realPath;
202
203
  try {
@@ -480,4 +481,4 @@ var AgentCatalog = class {
480
481
  export {
481
482
  AgentCatalog
482
483
  };
483
- //# sourceMappingURL=chunk-J6X5SW6O.js.map
484
+ //# sourceMappingURL=chunk-CKOK7JW6.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/agent-catalog.ts","../../src/core/agent-installer.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { AgentStore } from \"./agent-store.js\";\nimport { installAgent, uninstallAgent, resolveDistribution } from \"./agent-installer.js\";\nimport { getAgentAlias, checkDependencies } from \"./agent-dependencies.js\";\nimport type {\n AgentDefinition,\n RegistryAgent,\n AgentListItem,\n AvailabilityResult,\n InstallProgress,\n InstallResult,\n InstalledAgent,\n} from \"./types.js\";\nimport { createChildLogger } from \"./log.js\";\n\nconst log = createChildLogger({ module: \"agent-catalog\" });\n\nconst REGISTRY_URL = \"https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json\";\nconst CACHE_PATH = path.join(os.homedir(), \".openacp\", \"registry-cache.json\");\nconst DEFAULT_TTL_HOURS = 24;\n\ninterface RegistryCache {\n fetchedAt: string;\n ttlHours: number;\n data: { agents: RegistryAgent[] };\n}\n\nexport class AgentCatalog {\n private store: AgentStore;\n private registryAgents: RegistryAgent[] = [];\n\n constructor(store?: AgentStore) {\n this.store = store ?? new AgentStore();\n }\n\n load(): void {\n this.store.load();\n this.loadRegistryFromCacheOrSnapshot();\n this.enrichInstalledFromRegistry();\n }\n\n // --- Registry ---\n\n async fetchRegistry(): Promise<void> {\n try {\n log.info(\"Fetching agent registry from CDN...\");\n const response = await fetch(REGISTRY_URL);\n if (!response.ok) throw new Error(`HTTP ${response.status}`);\n const data = await response.json() as { agents: RegistryAgent[] };\n this.registryAgents = data.agents ?? [];\n\n const cache: RegistryCache = {\n fetchedAt: new Date().toISOString(),\n ttlHours: DEFAULT_TTL_HOURS,\n data,\n };\n fs.mkdirSync(path.dirname(CACHE_PATH), { recursive: true });\n fs.writeFileSync(CACHE_PATH, JSON.stringify(cache, null, 2));\n log.info({ count: this.registryAgents.length }, \"Registry updated\");\n } catch (err) {\n log.warn({ err }, \"Failed to fetch registry, using cached data\");\n }\n }\n\n async refreshRegistryIfStale(): Promise<void> {\n if (this.isCacheStale()) {\n await this.fetchRegistry();\n }\n }\n\n getRegistryAgents(): RegistryAgent[] {\n return this.registryAgents;\n }\n\n getRegistryAgent(registryId: string): RegistryAgent | undefined {\n return this.registryAgents.find((a) => a.id === registryId);\n }\n\n findRegistryAgent(keyOrId: string): RegistryAgent | undefined {\n const byId = this.registryAgents.find((a) => a.id === keyOrId);\n if (byId) return byId;\n return this.registryAgents.find((a) => getAgentAlias(a.id) === keyOrId);\n }\n\n // --- Installed ---\n\n getInstalled(): InstalledAgent[] {\n return Object.values(this.store.getInstalled());\n }\n\n getInstalledEntries(): Record<string, InstalledAgent> {\n return this.store.getInstalled();\n }\n\n getInstalledAgent(key: string): InstalledAgent | undefined {\n return this.store.getAgent(key);\n }\n\n // --- Discovery ---\n\n getAvailable(): AgentListItem[] {\n const installed = this.store.getInstalled();\n const items: AgentListItem[] = [];\n const seenKeys = new Set<string>();\n\n for (const [key, agent] of Object.entries(installed)) {\n seenKeys.add(key);\n const availability = agent.registryId\n ? checkDependencies(agent.registryId)\n : { available: true };\n const registryEntry = agent.registryId\n ? this.registryAgents.find((a) => a.id === agent.registryId)\n : undefined;\n items.push({\n key,\n registryId: agent.registryId ?? key,\n name: agent.name,\n version: agent.version,\n description: registryEntry?.description,\n distribution: agent.distribution,\n installed: true,\n available: availability.available,\n missingDeps: availability.missing?.map((m) => m.label),\n });\n }\n\n for (const agent of this.registryAgents) {\n const alias = getAgentAlias(agent.id);\n if (seenKeys.has(alias)) continue;\n seenKeys.add(alias);\n\n const dist = resolveDistribution(agent);\n const availability = checkDependencies(agent.id);\n\n items.push({\n key: alias,\n registryId: agent.id,\n name: agent.name,\n version: agent.version,\n description: agent.description,\n distribution: dist?.type ?? \"binary\",\n installed: false,\n available: dist !== null && availability.available,\n missingDeps: availability.missing?.map((m) => m.label),\n });\n }\n\n return items;\n }\n\n checkAvailability(keyOrId: string): AvailabilityResult {\n const agent = this.findRegistryAgent(keyOrId);\n if (!agent) return { available: false, reason: \"Not found in the agent registry.\" };\n\n const dist = resolveDistribution(agent);\n if (!dist) {\n return { available: false, reason: `Not available for your system. Check ${agent.website ?? agent.repository ?? \"their website\"} for other options.` };\n }\n\n return checkDependencies(agent.id);\n }\n\n // --- Install/Uninstall ---\n\n async install(keyOrId: string, progress?: InstallProgress, force?: boolean): Promise<InstallResult> {\n const agent = this.findRegistryAgent(keyOrId);\n if (!agent) {\n const msg = `\"${keyOrId}\" was not found in the agent registry. Run \"openacp agents\" to see what's available.`;\n await progress?.onError(msg);\n return { ok: false, agentKey: keyOrId, error: msg };\n }\n\n const agentKey = getAgentAlias(agent.id);\n if (this.store.hasAgent(agentKey) && !force) {\n const existing = this.store.getAgent(agentKey)!;\n const msg = `${agent.name} is already installed (v${existing.version}). Use --force to reinstall.`;\n await progress?.onError(msg);\n return { ok: false, agentKey, error: msg };\n }\n\n return installAgent(agent, this.store, progress);\n }\n\n async uninstall(key: string): Promise<{ ok: boolean; error?: string }> {\n if (!this.store.hasAgent(key)) {\n return { ok: false, error: `\"${key}\" is not installed.` };\n }\n await uninstallAgent(key, this.store);\n return { ok: true };\n }\n\n // --- Resolution (for AgentManager) ---\n\n resolve(key: string): AgentDefinition | undefined {\n const agent = this.store.getAgent(key);\n if (!agent) return undefined;\n return {\n name: key,\n command: agent.command,\n args: agent.args,\n workingDirectory: agent.workingDirectory,\n env: agent.env,\n };\n }\n\n // --- Internal ---\n\n /**\n * Enrich installed agents (especially migrated ones) with registry data.\n * Fixes agents that were migrated with version:\"unknown\", distribution:\"custom\",\n * or generic names by matching them to registry entries.\n */\n private enrichInstalledFromRegistry(): void {\n const installed = this.store.getInstalled();\n let changed = false;\n\n for (const [key, agent] of Object.entries(installed)) {\n const regAgent = agent.registryId\n ? this.registryAgents.find((a) => a.id === agent.registryId)\n : this.registryAgents.find((a) => getAgentAlias(a.id) === key);\n\n if (!regAgent) continue;\n\n let updated = false;\n\n // Enrich name if it's a generic capitalized key (e.g. \"Claude\" from migration)\n if (agent.name !== regAgent.name) {\n agent.name = regAgent.name;\n updated = true;\n }\n\n // Enrich version if unknown\n if (agent.version === \"unknown\") {\n agent.version = regAgent.version;\n updated = true;\n }\n\n // Enrich registryId if missing\n if (!agent.registryId) {\n agent.registryId = regAgent.id;\n updated = true;\n }\n\n // Enrich distribution from \"custom\" to actual type\n if (agent.distribution === \"custom\") {\n const dist = resolveDistribution(regAgent);\n if (dist) {\n agent.distribution = dist.type;\n updated = true;\n }\n }\n\n if (updated) {\n this.store.addAgent(key, agent);\n changed = true;\n }\n }\n\n if (changed) {\n log.info(\"Enriched installed agents with registry data\");\n }\n }\n\n private isCacheStale(): boolean {\n if (!fs.existsSync(CACHE_PATH)) return true;\n try {\n const raw = JSON.parse(fs.readFileSync(CACHE_PATH, \"utf-8\") as string) as RegistryCache;\n const fetchedAt = new Date(raw.fetchedAt).getTime();\n const ttlMs = (raw.ttlHours ?? DEFAULT_TTL_HOURS) * 60 * 60 * 1000;\n return Date.now() - fetchedAt > ttlMs;\n } catch {\n return true;\n }\n }\n\n private loadRegistryFromCacheOrSnapshot(): void {\n // Try cache first\n if (fs.existsSync(CACHE_PATH)) {\n try {\n const raw = JSON.parse(fs.readFileSync(CACHE_PATH, \"utf-8\") as string) as RegistryCache;\n if (raw.data?.agents) {\n this.registryAgents = raw.data.agents;\n log.debug({ count: this.registryAgents.length }, \"Loaded registry from cache\");\n return;\n }\n } catch {\n log.warn(\"Failed to load registry cache\");\n }\n }\n\n // Fallback: bundled snapshot\n try {\n // Try multiple paths for tsc and tsup builds\n const candidates = [\n path.join(import.meta.dirname, \"data\", \"registry-snapshot.json\"),\n path.join(import.meta.dirname, \"..\", \"data\", \"registry-snapshot.json\"),\n path.join(import.meta.dirname, \"..\", \"..\", \"data\", \"registry-snapshot.json\"),\n ];\n\n for (const candidate of candidates) {\n if (fs.existsSync(candidate)) {\n const raw = JSON.parse(fs.readFileSync(candidate, \"utf-8\") as string);\n this.registryAgents = raw.agents ?? [];\n log.debug({ count: this.registryAgents.length }, \"Loaded registry from bundled snapshot\");\n return;\n }\n }\n\n log.warn(\"No registry data available (no cache, no snapshot)\");\n } catch {\n log.warn(\"Failed to load bundled registry snapshot\");\n }\n }\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { createChildLogger } from \"./log.js\";\nimport type { InstalledAgent, RegistryAgent, InstallProgress, InstallResult } from \"./types.js\";\nimport { getAgentAlias, checkDependencies, checkRuntimeAvailable, getAgentSetup } from \"./agent-dependencies.js\";\nimport { AgentStore } from \"./agent-store.js\";\n\nconst log = createChildLogger({ module: \"agent-installer\" });\n\nconst AGENTS_DIR = path.join(os.homedir(), \".openacp\", \"agents\");\n\nconst ARCH_MAP: Record<string, string> = {\n arm64: \"aarch64\",\n x64: \"x86_64\",\n};\n\nconst PLATFORM_MAP: Record<string, string> = {\n darwin: \"darwin\",\n linux: \"linux\",\n win32: \"windows\",\n};\n\nexport function getPlatformKey(): string {\n const platform = PLATFORM_MAP[process.platform] ?? process.platform;\n const arch = ARCH_MAP[process.arch] ?? process.arch;\n return `${platform}-${arch}`;\n}\n\nexport type ResolvedDistribution =\n | { type: \"npx\"; package: string; args: string[]; env?: Record<string, string> }\n | { type: \"uvx\"; package: string; args: string[]; env?: Record<string, string> }\n | { type: \"binary\"; archive: string; cmd: string; args: string[]; env?: Record<string, string> };\n\nexport function resolveDistribution(agent: RegistryAgent): ResolvedDistribution | null {\n const dist = agent.distribution;\n\n if (dist.npx) {\n return { type: \"npx\", package: dist.npx.package, args: dist.npx.args ?? [], env: dist.npx.env };\n }\n if (dist.uvx) {\n return { type: \"uvx\", package: dist.uvx.package, args: dist.uvx.args ?? [], env: dist.uvx.env };\n }\n if (dist.binary) {\n const platformKey = getPlatformKey();\n const target = dist.binary[platformKey];\n if (!target) return null;\n return { type: \"binary\", archive: target.archive, cmd: target.cmd, args: target.args ?? [], env: target.env };\n }\n return null;\n}\n\nexport function buildInstalledAgent(\n registryId: string,\n name: string,\n version: string,\n dist: ResolvedDistribution,\n binaryPath?: string,\n): InstalledAgent {\n if (dist.type === \"npx\") {\n // Use latest version: strip pinned version from package name (e.g. @google/gemini-cli@0.34.0 → @google/gemini-cli)\n const npxPackage = stripPackageVersion(dist.package);\n return {\n registryId, name, version, distribution: \"npx\",\n command: \"npx\", args: [npxPackage, ...dist.args],\n env: dist.env ?? {}, installedAt: new Date().toISOString(), binaryPath: null,\n };\n }\n if (dist.type === \"uvx\") {\n // Strip pinned version: \"fast-agent-acp==0.6.6\" → \"fast-agent-acp\", \"minion-code@0.1.44\" → \"minion-code\"\n const uvxPackage = stripPythonPackageVersion(dist.package);\n return {\n registryId, name, version, distribution: \"uvx\",\n command: \"uvx\", args: [uvxPackage, ...dist.args],\n env: dist.env ?? {}, installedAt: new Date().toISOString(), binaryPath: null,\n };\n }\n // binary\n const absCmd = path.resolve(binaryPath!, dist.cmd);\n return {\n registryId, name, version, distribution: \"binary\",\n command: absCmd, args: dist.args,\n env: dist.env ?? {}, installedAt: new Date().toISOString(), binaryPath: binaryPath!,\n };\n}\n\n/**\n * Strip pinned version from npm package name so npx always uses latest.\n * e.g. \"@google/gemini-cli@0.34.0\" → \"@google/gemini-cli\"\n * \"cline@2.9.0\" → \"cline\"\n * \"@scope/pkg\" → \"@scope/pkg\" (no version, unchanged)\n */\nfunction stripPackageVersion(pkg: string): string {\n // Scoped: @scope/name@version → find the second @\n if (pkg.startsWith(\"@\")) {\n const afterScope = pkg.indexOf(\"/\");\n if (afterScope === -1) return pkg;\n const versionAt = pkg.indexOf(\"@\", afterScope + 1);\n return versionAt === -1 ? pkg : pkg.slice(0, versionAt);\n }\n // Unscoped: name@version\n const at = pkg.indexOf(\"@\");\n return at === -1 ? pkg : pkg.slice(0, at);\n}\n\n/**\n * Strip pinned version from Python package name so uvx always uses latest.\n * e.g. \"fast-agent-acp==0.6.6\" → \"fast-agent-acp\"\n * \"minion-code@0.1.44\" → \"minion-code\"\n * \"crow-cli\" → \"crow-cli\" (no version, unchanged)\n */\nfunction stripPythonPackageVersion(pkg: string): string {\n // Python-style: name==version or name>=version\n const pyMatch = pkg.match(/^([^=@><!]+)/);\n if (pyMatch && pkg.includes(\"==\")) return pyMatch[1]!;\n // npm-style @ used in some uvx packages\n const at = pkg.indexOf(\"@\");\n return at === -1 ? pkg : pkg.slice(0, at);\n}\n\nexport async function installAgent(\n agent: RegistryAgent,\n store: AgentStore,\n progress?: InstallProgress,\n): Promise<InstallResult> {\n const agentKey = getAgentAlias(agent.id);\n await progress?.onStart(agent.id, agent.name);\n\n // 1. Check dependencies\n await progress?.onStep(\"Checking requirements...\");\n const depResult = checkDependencies(agent.id);\n if (!depResult.available) {\n const hints = depResult.missing!.map((m) => ` ${m.label}: ${m.installHint}`).join(\"\\n\");\n const msg = `${agent.name} needs some tools installed first:\\n${hints}`;\n await progress?.onError(msg);\n return { ok: false, agentKey, error: msg };\n }\n\n // 2. Resolve distribution\n const dist = resolveDistribution(agent);\n if (!dist) {\n const platformKey = getPlatformKey();\n const msg = `${agent.name} is not available for your system (${platformKey}). Check their website for other install options.`;\n await progress?.onError(msg);\n return { ok: false, agentKey, error: msg };\n }\n\n // 3. Check runtime\n if (dist.type === \"uvx\" && !checkRuntimeAvailable(\"uvx\")) {\n const msg = `${agent.name} requires Python's uvx tool.\\nInstall it with: pip install uv`;\n await progress?.onError(msg, \"pip install uv\");\n return { ok: false, agentKey, error: msg, hint: \"pip install uv\" };\n }\n\n // 4. Install based on type\n let binaryPath: string | undefined;\n\n if (dist.type === \"binary\") {\n try {\n binaryPath = await downloadAndExtract(agent.id, dist.archive, progress);\n } catch (err) {\n const msg = `Failed to download ${agent.name}. Please try again or install manually.`;\n await progress?.onError(msg);\n return { ok: false, agentKey, error: msg };\n }\n } else {\n await progress?.onStep(\"Setting up... (will download on first use)\");\n }\n\n // 5. Save to store\n const installed = buildInstalledAgent(agent.id, agent.name, agent.version, dist, binaryPath);\n store.addAgent(agentKey, installed);\n\n const setup = getAgentSetup(agent.id);\n await progress?.onSuccess(agent.name);\n return { ok: true, agentKey, setupSteps: setup?.setupSteps };\n}\n\nasync function downloadAndExtract(\n agentId: string,\n archiveUrl: string,\n progress?: InstallProgress,\n): Promise<string> {\n const destDir = path.join(AGENTS_DIR, agentId);\n fs.mkdirSync(destDir, { recursive: true });\n\n await progress?.onStep(\"Downloading...\");\n log.info({ agentId, url: archiveUrl }, \"Downloading agent binary\");\n\n const response = await fetch(archiveUrl);\n if (!response.ok) {\n throw new Error(`Download failed: ${response.status} ${response.statusText}`);\n }\n\n const contentLength = Number(response.headers.get(\"content-length\") || 0);\n const buffer = await readResponseWithProgress(response, contentLength, progress);\n\n await progress?.onStep(\"Extracting...\");\n\n if (archiveUrl.endsWith(\".zip\")) {\n await extractZip(buffer, destDir);\n } else {\n await extractTarGz(buffer, destDir);\n }\n\n await progress?.onStep(\"Ready!\");\n return destDir;\n}\n\nasync function readResponseWithProgress(\n response: Response,\n contentLength: number,\n progress?: InstallProgress,\n): Promise<Buffer> {\n if (!response.body || contentLength === 0) {\n const arrayBuffer = await response.arrayBuffer();\n return Buffer.from(arrayBuffer);\n }\n\n const reader = response.body.getReader();\n const chunks: Uint8Array[] = [];\n let received = 0;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n received += value.length;\n if (contentLength > 0) {\n await progress?.onDownloadProgress(Math.round((received / contentLength) * 100));\n }\n }\n\n return Buffer.concat(chunks);\n}\n\nfunction validateExtractedPaths(destDir: string): void {\n const realDest = fs.realpathSync(destDir);\n const entries = fs.readdirSync(destDir, { recursive: true, withFileTypes: true });\n for (const entry of entries) {\n const parentPath = (entry as unknown as { parentPath?: string; path?: string }).parentPath ?? (entry as unknown as { path: string }).path;\n const fullPath = path.join(parentPath, entry.name);\n let realPath: string;\n try {\n realPath = fs.realpathSync(fullPath);\n } catch {\n // Broken symlink — check where it points\n const linkTarget = fs.readlinkSync(fullPath);\n realPath = path.resolve(path.dirname(fullPath), linkTarget);\n }\n if (!realPath.startsWith(realDest + path.sep) && realPath !== realDest) {\n fs.rmSync(destDir, { recursive: true, force: true });\n throw new Error(`Archive contains unsafe path: ${entry.name}`);\n }\n }\n}\n\nasync function extractTarGz(buffer: Buffer, destDir: string): Promise<void> {\n const { execFileSync } = await import(\"node:child_process\");\n const tmpFile = path.join(destDir, \"_archive.tar.gz\");\n fs.writeFileSync(tmpFile, buffer);\n try {\n execFileSync(\"tar\", [\"xzf\", tmpFile, \"-C\", destDir], { stdio: \"pipe\" });\n } finally {\n fs.unlinkSync(tmpFile);\n }\n validateExtractedPaths(destDir);\n}\n\nasync function extractZip(buffer: Buffer, destDir: string): Promise<void> {\n const { execFileSync } = await import(\"node:child_process\");\n const tmpFile = path.join(destDir, \"_archive.zip\");\n fs.writeFileSync(tmpFile, buffer);\n try {\n execFileSync(\"unzip\", [\"-o\", tmpFile, \"-d\", destDir], { stdio: \"pipe\" });\n } finally {\n fs.unlinkSync(tmpFile);\n }\n validateExtractedPaths(destDir);\n}\n\nexport async function uninstallAgent(\n agentKey: string,\n store: AgentStore,\n): Promise<void> {\n const agent = store.getAgent(agentKey);\n if (!agent) return;\n\n if (agent.binaryPath && fs.existsSync(agent.binaryPath)) {\n fs.rmSync(agent.binaryPath, { recursive: true, force: true });\n log.info({ agentKey, binaryPath: agent.binaryPath }, \"Deleted agent binary\");\n }\n\n store.removeAgent(agentKey);\n}\n"],"mappings":"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","names":["fs","path","os","log"]}
1
+ {"version":3,"sources":["../../src/core/agent-catalog.ts","../../src/core/agent-installer.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { AgentStore } from \"./agent-store.js\";\nimport { installAgent, uninstallAgent, resolveDistribution } from \"./agent-installer.js\";\nimport { getAgentAlias, checkDependencies } from \"./agent-dependencies.js\";\nimport type {\n AgentDefinition,\n RegistryAgent,\n AgentListItem,\n AvailabilityResult,\n InstallProgress,\n InstallResult,\n InstalledAgent,\n} from \"./types.js\";\nimport { createChildLogger } from \"./log.js\";\n\nconst log = createChildLogger({ module: \"agent-catalog\" });\n\nconst REGISTRY_URL = \"https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json\";\nconst CACHE_PATH = path.join(os.homedir(), \".openacp\", \"registry-cache.json\");\nconst DEFAULT_TTL_HOURS = 24;\n\ninterface RegistryCache {\n fetchedAt: string;\n ttlHours: number;\n data: { agents: RegistryAgent[] };\n}\n\nexport class AgentCatalog {\n private store: AgentStore;\n private registryAgents: RegistryAgent[] = [];\n\n constructor(store?: AgentStore) {\n this.store = store ?? new AgentStore();\n }\n\n load(): void {\n this.store.load();\n this.loadRegistryFromCacheOrSnapshot();\n this.enrichInstalledFromRegistry();\n }\n\n // --- Registry ---\n\n async fetchRegistry(): Promise<void> {\n try {\n log.info(\"Fetching agent registry from CDN...\");\n const response = await fetch(REGISTRY_URL);\n if (!response.ok) throw new Error(`HTTP ${response.status}`);\n const data = await response.json() as { agents: RegistryAgent[] };\n this.registryAgents = data.agents ?? [];\n\n const cache: RegistryCache = {\n fetchedAt: new Date().toISOString(),\n ttlHours: DEFAULT_TTL_HOURS,\n data,\n };\n fs.mkdirSync(path.dirname(CACHE_PATH), { recursive: true });\n fs.writeFileSync(CACHE_PATH, JSON.stringify(cache, null, 2));\n log.info({ count: this.registryAgents.length }, \"Registry updated\");\n } catch (err) {\n log.warn({ err }, \"Failed to fetch registry, using cached data\");\n }\n }\n\n async refreshRegistryIfStale(): Promise<void> {\n if (this.isCacheStale()) {\n await this.fetchRegistry();\n }\n }\n\n getRegistryAgents(): RegistryAgent[] {\n return this.registryAgents;\n }\n\n getRegistryAgent(registryId: string): RegistryAgent | undefined {\n return this.registryAgents.find((a) => a.id === registryId);\n }\n\n findRegistryAgent(keyOrId: string): RegistryAgent | undefined {\n const byId = this.registryAgents.find((a) => a.id === keyOrId);\n if (byId) return byId;\n return this.registryAgents.find((a) => getAgentAlias(a.id) === keyOrId);\n }\n\n // --- Installed ---\n\n getInstalled(): InstalledAgent[] {\n return Object.values(this.store.getInstalled());\n }\n\n getInstalledEntries(): Record<string, InstalledAgent> {\n return this.store.getInstalled();\n }\n\n getInstalledAgent(key: string): InstalledAgent | undefined {\n return this.store.getAgent(key);\n }\n\n // --- Discovery ---\n\n getAvailable(): AgentListItem[] {\n const installed = this.store.getInstalled();\n const items: AgentListItem[] = [];\n const seenKeys = new Set<string>();\n\n for (const [key, agent] of Object.entries(installed)) {\n seenKeys.add(key);\n const availability = agent.registryId\n ? checkDependencies(agent.registryId)\n : { available: true };\n const registryEntry = agent.registryId\n ? this.registryAgents.find((a) => a.id === agent.registryId)\n : undefined;\n items.push({\n key,\n registryId: agent.registryId ?? key,\n name: agent.name,\n version: agent.version,\n description: registryEntry?.description,\n distribution: agent.distribution,\n installed: true,\n available: availability.available,\n missingDeps: availability.missing?.map((m) => m.label),\n });\n }\n\n for (const agent of this.registryAgents) {\n const alias = getAgentAlias(agent.id);\n if (seenKeys.has(alias)) continue;\n seenKeys.add(alias);\n\n const dist = resolveDistribution(agent);\n const availability = checkDependencies(agent.id);\n\n items.push({\n key: alias,\n registryId: agent.id,\n name: agent.name,\n version: agent.version,\n description: agent.description,\n distribution: dist?.type ?? \"binary\",\n installed: false,\n available: dist !== null && availability.available,\n missingDeps: availability.missing?.map((m) => m.label),\n });\n }\n\n return items;\n }\n\n checkAvailability(keyOrId: string): AvailabilityResult {\n const agent = this.findRegistryAgent(keyOrId);\n if (!agent) return { available: false, reason: \"Not found in the agent registry.\" };\n\n const dist = resolveDistribution(agent);\n if (!dist) {\n return { available: false, reason: `Not available for your system. Check ${agent.website ?? agent.repository ?? \"their website\"} for other options.` };\n }\n\n return checkDependencies(agent.id);\n }\n\n // --- Install/Uninstall ---\n\n async install(keyOrId: string, progress?: InstallProgress, force?: boolean): Promise<InstallResult> {\n const agent = this.findRegistryAgent(keyOrId);\n if (!agent) {\n const msg = `\"${keyOrId}\" was not found in the agent registry. Run \"openacp agents\" to see what's available.`;\n await progress?.onError(msg);\n return { ok: false, agentKey: keyOrId, error: msg };\n }\n\n const agentKey = getAgentAlias(agent.id);\n if (this.store.hasAgent(agentKey) && !force) {\n const existing = this.store.getAgent(agentKey)!;\n const msg = `${agent.name} is already installed (v${existing.version}). Use --force to reinstall.`;\n await progress?.onError(msg);\n return { ok: false, agentKey, error: msg };\n }\n\n return installAgent(agent, this.store, progress);\n }\n\n async uninstall(key: string): Promise<{ ok: boolean; error?: string }> {\n if (!this.store.hasAgent(key)) {\n return { ok: false, error: `\"${key}\" is not installed.` };\n }\n await uninstallAgent(key, this.store);\n return { ok: true };\n }\n\n // --- Resolution (for AgentManager) ---\n\n resolve(key: string): AgentDefinition | undefined {\n const agent = this.store.getAgent(key);\n if (!agent) return undefined;\n return {\n name: key,\n command: agent.command,\n args: agent.args,\n workingDirectory: agent.workingDirectory,\n env: agent.env,\n };\n }\n\n // --- Internal ---\n\n /**\n * Enrich installed agents (especially migrated ones) with registry data.\n * Fixes agents that were migrated with version:\"unknown\", distribution:\"custom\",\n * or generic names by matching them to registry entries.\n */\n private enrichInstalledFromRegistry(): void {\n const installed = this.store.getInstalled();\n let changed = false;\n\n for (const [key, agent] of Object.entries(installed)) {\n const regAgent = agent.registryId\n ? this.registryAgents.find((a) => a.id === agent.registryId)\n : this.registryAgents.find((a) => getAgentAlias(a.id) === key);\n\n if (!regAgent) continue;\n\n let updated = false;\n\n // Enrich name if it's a generic capitalized key (e.g. \"Claude\" from migration)\n if (agent.name !== regAgent.name) {\n agent.name = regAgent.name;\n updated = true;\n }\n\n // Enrich version if unknown\n if (agent.version === \"unknown\") {\n agent.version = regAgent.version;\n updated = true;\n }\n\n // Enrich registryId if missing\n if (!agent.registryId) {\n agent.registryId = regAgent.id;\n updated = true;\n }\n\n // Enrich distribution from \"custom\" to actual type\n if (agent.distribution === \"custom\") {\n const dist = resolveDistribution(regAgent);\n if (dist) {\n agent.distribution = dist.type;\n updated = true;\n }\n }\n\n if (updated) {\n this.store.addAgent(key, agent);\n changed = true;\n }\n }\n\n if (changed) {\n log.info(\"Enriched installed agents with registry data\");\n }\n }\n\n private isCacheStale(): boolean {\n if (!fs.existsSync(CACHE_PATH)) return true;\n try {\n const raw = JSON.parse(fs.readFileSync(CACHE_PATH, \"utf-8\") as string) as RegistryCache;\n const fetchedAt = new Date(raw.fetchedAt).getTime();\n const ttlMs = (raw.ttlHours ?? DEFAULT_TTL_HOURS) * 60 * 60 * 1000;\n return Date.now() - fetchedAt > ttlMs;\n } catch {\n return true;\n }\n }\n\n private loadRegistryFromCacheOrSnapshot(): void {\n // Try cache first\n if (fs.existsSync(CACHE_PATH)) {\n try {\n const raw = JSON.parse(fs.readFileSync(CACHE_PATH, \"utf-8\") as string) as RegistryCache;\n if (raw.data?.agents) {\n this.registryAgents = raw.data.agents;\n log.debug({ count: this.registryAgents.length }, \"Loaded registry from cache\");\n return;\n }\n } catch {\n log.warn(\"Failed to load registry cache\");\n }\n }\n\n // Fallback: bundled snapshot\n try {\n // Try multiple paths for tsc and tsup builds\n const candidates = [\n path.join(import.meta.dirname, \"data\", \"registry-snapshot.json\"),\n path.join(import.meta.dirname, \"..\", \"data\", \"registry-snapshot.json\"),\n path.join(import.meta.dirname, \"..\", \"..\", \"data\", \"registry-snapshot.json\"),\n ];\n\n for (const candidate of candidates) {\n if (fs.existsSync(candidate)) {\n const raw = JSON.parse(fs.readFileSync(candidate, \"utf-8\") as string);\n this.registryAgents = raw.agents ?? [];\n log.debug({ count: this.registryAgents.length }, \"Loaded registry from bundled snapshot\");\n return;\n }\n }\n\n log.warn(\"No registry data available (no cache, no snapshot)\");\n } catch {\n log.warn(\"Failed to load bundled registry snapshot\");\n }\n }\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { createChildLogger } from \"./log.js\";\nimport type { InstalledAgent, RegistryAgent, InstallProgress, InstallResult } from \"./types.js\";\nimport { getAgentAlias, checkDependencies, checkRuntimeAvailable, getAgentSetup } from \"./agent-dependencies.js\";\nimport { AgentStore } from \"./agent-store.js\";\n\nconst log = createChildLogger({ module: \"agent-installer\" });\n\nconst AGENTS_DIR = path.join(os.homedir(), \".openacp\", \"agents\");\n\nconst ARCH_MAP: Record<string, string> = {\n arm64: \"aarch64\",\n x64: \"x86_64\",\n};\n\nconst PLATFORM_MAP: Record<string, string> = {\n darwin: \"darwin\",\n linux: \"linux\",\n win32: \"windows\",\n};\n\nexport function getPlatformKey(): string {\n const platform = PLATFORM_MAP[process.platform] ?? process.platform;\n const arch = ARCH_MAP[process.arch] ?? process.arch;\n return `${platform}-${arch}`;\n}\n\nexport type ResolvedDistribution =\n | { type: \"npx\"; package: string; args: string[]; env?: Record<string, string> }\n | { type: \"uvx\"; package: string; args: string[]; env?: Record<string, string> }\n | { type: \"binary\"; archive: string; cmd: string; args: string[]; env?: Record<string, string> };\n\nexport function resolveDistribution(agent: RegistryAgent): ResolvedDistribution | null {\n const dist = agent.distribution;\n\n if (dist.npx) {\n return { type: \"npx\", package: dist.npx.package, args: dist.npx.args ?? [], env: dist.npx.env };\n }\n if (dist.uvx) {\n return { type: \"uvx\", package: dist.uvx.package, args: dist.uvx.args ?? [], env: dist.uvx.env };\n }\n if (dist.binary) {\n const platformKey = getPlatformKey();\n const target = dist.binary[platformKey];\n if (!target) return null;\n return { type: \"binary\", archive: target.archive, cmd: target.cmd, args: target.args ?? [], env: target.env };\n }\n return null;\n}\n\nexport function buildInstalledAgent(\n registryId: string,\n name: string,\n version: string,\n dist: ResolvedDistribution,\n binaryPath?: string,\n): InstalledAgent {\n if (dist.type === \"npx\") {\n // Use latest version: strip pinned version from package name (e.g. @google/gemini-cli@0.34.0 → @google/gemini-cli)\n const npxPackage = stripPackageVersion(dist.package);\n return {\n registryId, name, version, distribution: \"npx\",\n command: \"npx\", args: [npxPackage, ...dist.args],\n env: dist.env ?? {}, installedAt: new Date().toISOString(), binaryPath: null,\n };\n }\n if (dist.type === \"uvx\") {\n // Strip pinned version: \"fast-agent-acp==0.6.6\" → \"fast-agent-acp\", \"minion-code@0.1.44\" → \"minion-code\"\n const uvxPackage = stripPythonPackageVersion(dist.package);\n return {\n registryId, name, version, distribution: \"uvx\",\n command: \"uvx\", args: [uvxPackage, ...dist.args],\n env: dist.env ?? {}, installedAt: new Date().toISOString(), binaryPath: null,\n };\n }\n // binary\n const absCmd = path.resolve(binaryPath!, dist.cmd);\n return {\n registryId, name, version, distribution: \"binary\",\n command: absCmd, args: dist.args,\n env: dist.env ?? {}, installedAt: new Date().toISOString(), binaryPath: binaryPath!,\n };\n}\n\n/**\n * Strip pinned version from npm package name so npx always uses latest.\n * e.g. \"@google/gemini-cli@0.34.0\" → \"@google/gemini-cli\"\n * \"cline@2.9.0\" → \"cline\"\n * \"@scope/pkg\" → \"@scope/pkg\" (no version, unchanged)\n */\nfunction stripPackageVersion(pkg: string): string {\n // Scoped: @scope/name@version → find the second @\n if (pkg.startsWith(\"@\")) {\n const afterScope = pkg.indexOf(\"/\");\n if (afterScope === -1) return pkg;\n const versionAt = pkg.indexOf(\"@\", afterScope + 1);\n return versionAt === -1 ? pkg : pkg.slice(0, versionAt);\n }\n // Unscoped: name@version\n const at = pkg.indexOf(\"@\");\n return at === -1 ? pkg : pkg.slice(0, at);\n}\n\n/**\n * Strip pinned version from Python package name so uvx always uses latest.\n * e.g. \"fast-agent-acp==0.6.6\" → \"fast-agent-acp\"\n * \"minion-code@0.1.44\" → \"minion-code\"\n * \"crow-cli\" → \"crow-cli\" (no version, unchanged)\n */\nfunction stripPythonPackageVersion(pkg: string): string {\n // Python-style: name==version or name>=version\n const pyMatch = pkg.match(/^([^=@><!]+)/);\n if (pyMatch && pkg.includes(\"==\")) return pyMatch[1]!;\n // npm-style @ used in some uvx packages\n const at = pkg.indexOf(\"@\");\n return at === -1 ? pkg : pkg.slice(0, at);\n}\n\nexport async function installAgent(\n agent: RegistryAgent,\n store: AgentStore,\n progress?: InstallProgress,\n): Promise<InstallResult> {\n const agentKey = getAgentAlias(agent.id);\n await progress?.onStart(agent.id, agent.name);\n\n // 1. Check dependencies\n await progress?.onStep(\"Checking requirements...\");\n const depResult = checkDependencies(agent.id);\n if (!depResult.available) {\n const hints = depResult.missing!.map((m) => ` ${m.label}: ${m.installHint}`).join(\"\\n\");\n const msg = `${agent.name} needs some tools installed first:\\n${hints}`;\n await progress?.onError(msg);\n return { ok: false, agentKey, error: msg };\n }\n\n // 2. Resolve distribution\n const dist = resolveDistribution(agent);\n if (!dist) {\n const platformKey = getPlatformKey();\n const msg = `${agent.name} is not available for your system (${platformKey}). Check their website for other install options.`;\n await progress?.onError(msg);\n return { ok: false, agentKey, error: msg };\n }\n\n // 3. Check runtime\n if (dist.type === \"uvx\" && !checkRuntimeAvailable(\"uvx\")) {\n const msg = `${agent.name} requires Python's uvx tool.\\nInstall it with: pip install uv`;\n await progress?.onError(msg, \"pip install uv\");\n return { ok: false, agentKey, error: msg, hint: \"pip install uv\" };\n }\n\n // 4. Install based on type\n let binaryPath: string | undefined;\n\n if (dist.type === \"binary\") {\n try {\n binaryPath = await downloadAndExtract(agent.id, dist.archive, progress);\n } catch (err) {\n const msg = `Failed to download ${agent.name}. Please try again or install manually.`;\n await progress?.onError(msg);\n return { ok: false, agentKey, error: msg };\n }\n } else {\n await progress?.onStep(\"Setting up... (will download on first use)\");\n }\n\n // 5. Save to store\n const installed = buildInstalledAgent(agent.id, agent.name, agent.version, dist, binaryPath);\n store.addAgent(agentKey, installed);\n\n const setup = getAgentSetup(agent.id);\n await progress?.onSuccess(agent.name);\n return { ok: true, agentKey, setupSteps: setup?.setupSteps };\n}\n\nasync function downloadAndExtract(\n agentId: string,\n archiveUrl: string,\n progress?: InstallProgress,\n): Promise<string> {\n const destDir = path.join(AGENTS_DIR, agentId);\n fs.mkdirSync(destDir, { recursive: true });\n\n await progress?.onStep(\"Downloading...\");\n log.info({ agentId, url: archiveUrl }, \"Downloading agent binary\");\n\n const response = await fetch(archiveUrl);\n if (!response.ok) {\n throw new Error(`Download failed: ${response.status} ${response.statusText}`);\n }\n\n const contentLength = Number(response.headers.get(\"content-length\") || 0);\n const buffer = await readResponseWithProgress(response, contentLength, progress);\n\n await progress?.onStep(\"Extracting...\");\n\n if (archiveUrl.endsWith(\".zip\")) {\n await extractZip(buffer, destDir);\n } else {\n await extractTarGz(buffer, destDir);\n }\n\n await progress?.onStep(\"Ready!\");\n return destDir;\n}\n\nasync function readResponseWithProgress(\n response: Response,\n contentLength: number,\n progress?: InstallProgress,\n): Promise<Buffer> {\n if (!response.body || contentLength === 0) {\n const arrayBuffer = await response.arrayBuffer();\n return Buffer.from(arrayBuffer);\n }\n\n const reader = response.body.getReader();\n const chunks: Uint8Array[] = [];\n let received = 0;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n received += value.length;\n if (contentLength > 0) {\n await progress?.onDownloadProgress(Math.round((received / contentLength) * 100));\n }\n }\n\n return Buffer.concat(chunks);\n}\n\nfunction validateExtractedPaths(destDir: string): void {\n const realDest = fs.realpathSync(destDir);\n const entries = fs.readdirSync(destDir, { recursive: true, withFileTypes: true });\n for (const entry of entries) {\n // Node <20.12 uses `path`, >=20.12 uses `parentPath` on Dirent with recursive readdir\n const dirent = entry as fs.Dirent & { parentPath?: string; path?: string };\n const parentPath = dirent.parentPath ?? dirent.path ?? destDir;\n const fullPath = path.join(parentPath, entry.name);\n let realPath: string;\n try {\n realPath = fs.realpathSync(fullPath);\n } catch {\n // Broken symlink — check where it points\n const linkTarget = fs.readlinkSync(fullPath);\n realPath = path.resolve(path.dirname(fullPath), linkTarget);\n }\n if (!realPath.startsWith(realDest + path.sep) && realPath !== realDest) {\n fs.rmSync(destDir, { recursive: true, force: true });\n throw new Error(`Archive contains unsafe path: ${entry.name}`);\n }\n }\n}\n\nasync function extractTarGz(buffer: Buffer, destDir: string): Promise<void> {\n const { execFileSync } = await import(\"node:child_process\");\n const tmpFile = path.join(destDir, \"_archive.tar.gz\");\n fs.writeFileSync(tmpFile, buffer);\n try {\n execFileSync(\"tar\", [\"xzf\", tmpFile, \"-C\", destDir], { stdio: \"pipe\" });\n } finally {\n fs.unlinkSync(tmpFile);\n }\n validateExtractedPaths(destDir);\n}\n\nasync function extractZip(buffer: Buffer, destDir: string): Promise<void> {\n const { execFileSync } = await import(\"node:child_process\");\n const tmpFile = path.join(destDir, \"_archive.zip\");\n fs.writeFileSync(tmpFile, buffer);\n try {\n execFileSync(\"unzip\", [\"-o\", tmpFile, \"-d\", destDir], { stdio: \"pipe\" });\n } finally {\n fs.unlinkSync(tmpFile);\n }\n validateExtractedPaths(destDir);\n}\n\nexport async function uninstallAgent(\n agentKey: string,\n store: AgentStore,\n): Promise<void> {\n const agent = store.getAgent(agentKey);\n if (!agent) return;\n\n if (agent.binaryPath && fs.existsSync(agent.binaryPath)) {\n fs.rmSync(agent.binaryPath, { recursive: true, force: true });\n log.info({ agentKey, binaryPath: agent.binaryPath }, \"Deleted agent binary\");\n }\n\n store.removeAgent(agentKey);\n}\n"],"mappings":"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","names":["fs","path","os","log"]}
@@ -41,14 +41,16 @@ var migrations = [
41
41
  if (!agents || typeof agents !== "object") return false;
42
42
  let changed = false;
43
43
  for (const [agentName, agentDef] of Object.entries(agents)) {
44
- if (!agentDef?.command) continue;
44
+ if (!agentDef || typeof agentDef !== "object" || !("command" in agentDef)) continue;
45
+ const def = agentDef;
46
+ if (typeof def.command !== "string") continue;
45
47
  for (const [correctCmd, legacyCmds] of Object.entries(COMMAND_MIGRATIONS)) {
46
- if (legacyCmds.includes(agentDef.command)) {
48
+ if (legacyCmds.includes(def.command)) {
47
49
  log.warn(
48
- { agent: agentName, oldCommand: agentDef.command, newCommand: correctCmd },
49
- `Auto-migrating agent command: "${agentDef.command}" \u2192 "${correctCmd}"`
50
+ { agent: agentName, oldCommand: def.command, newCommand: correctCmd },
51
+ `Auto-migrating agent command: "${def.command}" \u2192 "${correctCmd}"`
50
52
  );
51
- agentDef.command = correctCmd;
53
+ def.command = correctCmd;
52
54
  changed = true;
53
55
  }
54
56
  }
@@ -68,8 +70,10 @@ var migrations = [
68
70
  "codex": "codex-acp"
69
71
  };
70
72
  const installed = {};
71
- for (const [key, cfg] of Object.entries(agents)) {
72
- const registryId = COMMAND_TO_REGISTRY[cfg.command] ?? null;
73
+ for (const [key, val] of Object.entries(agents)) {
74
+ const cfg = val;
75
+ const command = typeof cfg.command === "string" ? cfg.command : "";
76
+ const registryId = COMMAND_TO_REGISTRY[command] ?? null;
73
77
  installed[key] = {
74
78
  registryId,
75
79
  name: key.charAt(0).toUpperCase() + key.slice(1),
@@ -142,7 +146,7 @@ var UsageSchema = z.object({
142
146
  retentionDays: z.number().default(90)
143
147
  }).default({});
144
148
  var SpeechProviderSchema = z.object({
145
- apiKey: z.string().min(1),
149
+ apiKey: z.string().min(1).optional(),
146
150
  model: z.string().optional()
147
151
  }).passthrough();
148
152
  var SpeechSchema = z.object({
@@ -179,10 +183,13 @@ var ConfigSchema = z.object({
179
183
  }).default({}),
180
184
  tunnel: TunnelSchema,
181
185
  usage: UsageSchema,
182
- integrations: z.record(z.string(), z.object({
183
- installed: z.boolean(),
184
- installedAt: z.string().optional()
185
- })).default({}),
186
+ integrations: z.record(
187
+ z.string(),
188
+ z.object({
189
+ installed: z.boolean(),
190
+ installedAt: z.string().optional()
191
+ })
192
+ ).default({}),
186
193
  speech: SpeechSchema
187
194
  });
188
195
  function expandHome(p) {
@@ -284,7 +291,7 @@ var ConfigManager = class extends EventEmitter {
284
291
  this.config = result.data;
285
292
  }
286
293
  if (changePath) {
287
- const { getConfigValue } = await import("./config-registry-QQOJ2GQP.js");
294
+ const { getConfigValue } = await import("./config-registry-7I6GGDOY.js");
288
295
  const value = getConfigValue(this.config, changePath);
289
296
  const oldValue = oldConfig ? getConfigValue(oldConfig, changePath) : void 0;
290
297
  this.emit("config:changed", { path: changePath, value, oldValue });
@@ -375,18 +382,24 @@ var ConfigManager = class extends EventEmitter {
375
382
  raw.speech = raw.speech || {};
376
383
  const speech = raw.speech;
377
384
  speech.stt = speech.stt || {};
378
- speech.stt.providers = speech.stt.providers || {};
379
- speech.stt.providers.groq = speech.stt.providers.groq || {};
380
- speech.stt.providers.groq.apiKey = process.env.OPENACP_SPEECH_GROQ_API_KEY;
385
+ const stt = speech.stt;
386
+ stt.providers = stt.providers || {};
387
+ const providers = stt.providers;
388
+ providers.groq = providers.groq || {};
389
+ providers.groq.apiKey = process.env.OPENACP_SPEECH_GROQ_API_KEY;
381
390
  }
382
391
  }
383
392
  deepMerge(target, source) {
384
393
  for (const key of Object.keys(source)) {
385
- if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) {
394
+ const val = source[key];
395
+ if (val && typeof val === "object" && !Array.isArray(val)) {
386
396
  if (!target[key]) target[key] = {};
387
- this.deepMerge(target[key], source[key]);
397
+ this.deepMerge(
398
+ target[key],
399
+ val
400
+ );
388
401
  } else {
389
- target[key] = source[key];
402
+ target[key] = val;
390
403
  }
391
404
  }
392
405
  }
@@ -399,4 +412,4 @@ export {
399
412
  expandHome,
400
413
  ConfigManager
401
414
  };
402
- //# sourceMappingURL=chunk-4LFDEW22.js.map
415
+ //# sourceMappingURL=chunk-EWYNCHUH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/config.ts","../../src/core/config-migrations.ts"],"sourcesContent":["import { z } from \"zod\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { EventEmitter } from \"node:events\";\nimport { applyMigrations } from \"./config-migrations.js\";\nimport { createChildLogger } from \"./log.js\";\nconst log = createChildLogger({ module: \"config\" });\n\nconst BaseChannelSchema = z\n .object({\n enabled: z.boolean().default(false),\n adapter: z.string().optional(), // package name for plugin adapters\n })\n .passthrough();\n\nexport const PLUGINS_DIR = path.join(os.homedir(), \".openacp\", \"plugins\");\n\nconst AgentSchema = z.object({\n command: z.string(),\n args: z.array(z.string()).default([]),\n workingDirectory: z.string().optional(),\n env: z.record(z.string(), z.string()).default({}),\n});\n\nconst LoggingSchema = z\n .object({\n level: z\n .enum([\"silent\", \"debug\", \"info\", \"warn\", \"error\", \"fatal\"])\n .default(\"info\"),\n logDir: z.string().default(\"~/.openacp/logs\"),\n maxFileSize: z.union([z.string(), z.number()]).default(\"10m\"),\n maxFiles: z.number().default(7),\n sessionLogRetentionDays: z.number().default(30),\n })\n .default({});\n\nexport type LoggingConfig = z.infer<typeof LoggingSchema>;\n\nconst TunnelAuthSchema = z\n .object({\n enabled: z.boolean().default(false),\n token: z.string().optional(),\n })\n .default({});\n\nconst TunnelSchema = z\n .object({\n enabled: z.boolean().default(false),\n port: z.number().default(3100),\n provider: z\n .enum([\"cloudflare\", \"ngrok\", \"bore\", \"tailscale\"])\n .default(\"cloudflare\"),\n options: z.record(z.string(), z.unknown()).default({}),\n maxUserTunnels: z.number().default(5),\n storeTtlMinutes: z.number().default(60),\n auth: TunnelAuthSchema,\n })\n .default({});\n\nexport type TunnelConfig = z.infer<typeof TunnelSchema>;\n\nconst UsageSchema = z\n .object({\n enabled: z.boolean().default(true),\n monthlyBudget: z.number().optional(),\n warningThreshold: z.number().default(0.8),\n currency: z.string().default(\"USD\"),\n retentionDays: z.number().default(90),\n })\n .default({});\n\nexport type UsageConfig = z.infer<typeof UsageSchema>;\n\nconst SpeechProviderSchema = z\n .object({\n apiKey: z.string().min(1).optional(),\n model: z.string().optional(),\n })\n .passthrough();\n\nconst SpeechSchema = z\n .object({\n stt: z\n .object({\n provider: z.string().nullable().default(null),\n providers: z.record(SpeechProviderSchema).default({}),\n })\n .default({}),\n tts: z\n .object({\n provider: z.string().nullable().default(null),\n providers: z.record(SpeechProviderSchema).default({}),\n })\n .default({}),\n })\n .optional()\n .default({});\n\nexport const ConfigSchema = z.object({\n channels: z.record(z.string(), BaseChannelSchema),\n agents: z.record(z.string(), AgentSchema).optional().default({}),\n defaultAgent: z.string(),\n workspace: z\n .object({\n baseDir: z.string().default(\"~/openacp-workspace\"),\n })\n .default({}),\n security: z\n .object({\n allowedUserIds: z.array(z.string()).default([]),\n maxConcurrentSessions: z.number().default(20),\n sessionTimeoutMinutes: z.number().default(60),\n })\n .default({}),\n logging: LoggingSchema,\n runMode: z.enum([\"foreground\", \"daemon\"]).default(\"foreground\"),\n autoStart: z.boolean().default(false),\n api: z\n .object({\n port: z.number().default(21420),\n host: z.string().default(\"127.0.0.1\"),\n })\n .default({}),\n sessionStore: z\n .object({\n ttlDays: z.number().default(30),\n })\n .default({}),\n tunnel: TunnelSchema,\n usage: UsageSchema,\n integrations: z\n .record(\n z.string(),\n z.object({\n installed: z.boolean(),\n installedAt: z.string().optional(),\n }),\n )\n .default({}),\n speech: SpeechSchema,\n});\n\nexport type Config = z.infer<typeof ConfigSchema>;\n\nexport function expandHome(p: string): string {\n if (p.startsWith(\"~\")) {\n return path.join(os.homedir(), p.slice(1));\n }\n return p;\n}\n\nconst DEFAULT_CONFIG = {\n channels: {\n telegram: {\n enabled: false,\n botToken: \"YOUR_BOT_TOKEN_HERE\",\n chatId: 0,\n notificationTopicId: null,\n assistantTopicId: null,\n },\n discord: {\n enabled: false,\n botToken: \"YOUR_DISCORD_BOT_TOKEN_HERE\",\n guildId: \"\",\n forumChannelId: null,\n notificationChannelId: null,\n assistantThreadId: null,\n },\n },\n agents: {\n claude: { command: \"claude-agent-acp\", args: [], env: {} },\n codex: { command: \"codex\", args: [\"--acp\"], env: {} },\n },\n defaultAgent: \"claude\",\n workspace: { baseDir: \"~/openacp-workspace\" },\n security: {\n allowedUserIds: [],\n maxConcurrentSessions: 20,\n sessionTimeoutMinutes: 60,\n },\n sessionStore: { ttlDays: 30 },\n tunnel: {\n enabled: true,\n port: 3100,\n provider: \"cloudflare\",\n options: {},\n storeTtlMinutes: 60,\n auth: { enabled: false },\n },\n usage: {},\n};\n\nexport class ConfigManager extends EventEmitter {\n private config!: Config;\n private configPath: string;\n\n constructor() {\n super();\n this.configPath =\n process.env.OPENACP_CONFIG_PATH || expandHome(\"~/.openacp/config.json\");\n }\n\n async load(): Promise<void> {\n // 1. Ensure directory exists\n const dir = path.dirname(this.configPath);\n fs.mkdirSync(dir, { recursive: true });\n\n // 2. If config file doesn't exist, create default\n if (!fs.existsSync(this.configPath)) {\n fs.writeFileSync(\n this.configPath,\n JSON.stringify(DEFAULT_CONFIG, null, 2),\n );\n log.info({ configPath: this.configPath }, \"Config created\");\n log.info(\n \"Please edit it with your channel credentials (Telegram bot token, Discord bot token, etc.), then restart.\",\n );\n process.exit(1);\n }\n\n // 3. Read and parse\n const raw = JSON.parse(fs.readFileSync(this.configPath, \"utf-8\"));\n\n // 3.5. Auto-migrate config\n const { changed: configUpdated } = applyMigrations(raw);\n if (configUpdated) {\n fs.writeFileSync(this.configPath, JSON.stringify(raw, null, 2));\n }\n\n // 4. Apply env var overrides\n this.applyEnvOverrides(raw);\n\n // 5. Validate with Zod\n const result = ConfigSchema.safeParse(raw);\n if (!result.success) {\n log.error(\"Config validation failed\");\n for (const issue of result.error.issues) {\n log.error(\n { path: issue.path.join(\".\"), message: issue.message },\n \"Validation error\",\n );\n }\n process.exit(1);\n }\n this.config = result.data;\n }\n\n get(): Config {\n return this.config;\n }\n\n async save(\n updates: Record<string, unknown>,\n changePath?: string,\n ): Promise<void> {\n const oldConfig = this.config ? structuredClone(this.config) : undefined;\n // Read current file, merge updates, write back\n const raw = JSON.parse(fs.readFileSync(this.configPath, \"utf-8\"));\n this.deepMerge(raw, updates);\n fs.writeFileSync(this.configPath, JSON.stringify(raw, null, 2));\n // Re-validate and update in-memory config\n const result = ConfigSchema.safeParse(raw);\n if (result.success) {\n this.config = result.data;\n }\n // Emit change event if path provided\n if (changePath) {\n const { getConfigValue } = await import(\"./config-registry.js\");\n const value = getConfigValue(this.config, changePath);\n const oldValue = oldConfig\n ? getConfigValue(oldConfig, changePath)\n : undefined;\n this.emit(\"config:changed\", { path: changePath, value, oldValue });\n }\n }\n\n resolveWorkspace(input?: string): string {\n if (!input) {\n const resolved = expandHome(this.config.workspace.baseDir);\n fs.mkdirSync(resolved, { recursive: true });\n return resolved;\n }\n if (input.startsWith(\"/\") || input.startsWith(\"~\")) {\n const resolved = expandHome(input);\n fs.mkdirSync(resolved, { recursive: true });\n return resolved;\n }\n // Named workspace → lowercase, under baseDir\n const name = input.toLowerCase();\n const resolved = path.join(expandHome(this.config.workspace.baseDir), name);\n fs.mkdirSync(resolved, { recursive: true });\n return resolved;\n }\n\n async exists(): Promise<boolean> {\n return fs.existsSync(this.configPath);\n }\n\n getConfigPath(): string {\n return this.configPath;\n }\n\n async writeNew(config: Config): Promise<void> {\n const dir = path.dirname(this.configPath);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(this.configPath, JSON.stringify(config, null, 2));\n }\n\n private applyEnvOverrides(raw: Record<string, unknown>): void {\n const overrides: [string, string[]][] = [\n [\"OPENACP_TELEGRAM_BOT_TOKEN\", [\"channels\", \"telegram\", \"botToken\"]],\n [\"OPENACP_TELEGRAM_CHAT_ID\", [\"channels\", \"telegram\", \"chatId\"]],\n [\"OPENACP_DISCORD_BOT_TOKEN\", [\"channels\", \"discord\", \"botToken\"]],\n [\"OPENACP_DISCORD_GUILD_ID\", [\"channels\", \"discord\", \"guildId\"]],\n [\"OPENACP_DEFAULT_AGENT\", [\"defaultAgent\"]],\n [\"OPENACP_RUN_MODE\", [\"runMode\"]],\n [\"OPENACP_API_PORT\", [\"api\", \"port\"]],\n ];\n for (const [envVar, configPath] of overrides) {\n const value = process.env[envVar];\n if (value !== undefined) {\n let target: Record<string, unknown> = raw;\n for (let i = 0; i < configPath.length - 1; i++) {\n if (!target[configPath[i]]) target[configPath[i]] = {};\n target = target[configPath[i]] as Record<string, unknown>;\n }\n const key = configPath[configPath.length - 1];\n // Convert numeric fields to number\n target[key] =\n key === \"chatId\" || key === \"port\" ? Number(value) : value;\n }\n }\n\n // Logging env var overrides\n if (process.env.OPENACP_LOG_LEVEL) {\n raw.logging = raw.logging || {};\n (raw.logging as Record<string, unknown>).level =\n process.env.OPENACP_LOG_LEVEL;\n }\n if (process.env.OPENACP_LOG_DIR) {\n raw.logging = raw.logging || {};\n (raw.logging as Record<string, unknown>).logDir =\n process.env.OPENACP_LOG_DIR;\n }\n if (process.env.OPENACP_DEBUG && !process.env.OPENACP_LOG_LEVEL) {\n raw.logging = raw.logging || {};\n (raw.logging as Record<string, unknown>).level = \"debug\";\n }\n\n // Tunnel env var overrides\n if (process.env.OPENACP_TUNNEL_ENABLED) {\n raw.tunnel = raw.tunnel || {};\n (raw.tunnel as Record<string, unknown>).enabled =\n process.env.OPENACP_TUNNEL_ENABLED === \"true\";\n }\n if (process.env.OPENACP_TUNNEL_PORT) {\n raw.tunnel = raw.tunnel || {};\n (raw.tunnel as Record<string, unknown>).port = Number(\n process.env.OPENACP_TUNNEL_PORT,\n );\n }\n if (process.env.OPENACP_TUNNEL_PROVIDER) {\n raw.tunnel = raw.tunnel || {};\n (raw.tunnel as Record<string, unknown>).provider =\n process.env.OPENACP_TUNNEL_PROVIDER;\n }\n\n // Speech env var overrides\n if (process.env.OPENACP_SPEECH_STT_PROVIDER) {\n raw.speech = raw.speech || {};\n const speech = raw.speech as Record<string, unknown>;\n speech.stt = speech.stt || {};\n (speech.stt as Record<string, unknown>).provider = process.env.OPENACP_SPEECH_STT_PROVIDER;\n }\n if (process.env.OPENACP_SPEECH_GROQ_API_KEY) {\n raw.speech = raw.speech || {};\n const speech = raw.speech as Record<string, unknown>;\n speech.stt = speech.stt || {};\n const stt = speech.stt as Record<string, unknown>;\n stt.providers = stt.providers || {};\n const providers = stt.providers as Record<string, unknown>;\n providers.groq = providers.groq || {};\n (providers.groq as Record<string, unknown>).apiKey =\n process.env.OPENACP_SPEECH_GROQ_API_KEY;\n }\n }\n\n private deepMerge(\n target: Record<string, unknown>,\n source: Record<string, unknown>,\n ): void {\n for (const key of Object.keys(source)) {\n const val = source[key];\n if (\n val &&\n typeof val === \"object\" &&\n !Array.isArray(val)\n ) {\n if (!target[key]) target[key] = {};\n this.deepMerge(\n target[key] as Record<string, unknown>,\n val as Record<string, unknown>,\n );\n } else {\n target[key] = val;\n }\n }\n }\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { createChildLogger } from \"./log.js\";\nconst log = createChildLogger({ module: \"config-migrations\" });\n\ntype RawConfig = Record<string, unknown>;\n\nexport interface Migration {\n name: string;\n apply: (raw: RawConfig) => boolean; // returns true if config was modified\n}\n\nexport const migrations: Migration[] = [\n {\n name: \"add-tunnel-section\",\n apply(raw) {\n if (raw.tunnel) return false;\n raw.tunnel = {\n enabled: true,\n port: 3100,\n provider: \"cloudflare\",\n options: {},\n storeTtlMinutes: 60,\n auth: { enabled: false },\n };\n log.info(\"Added tunnel section to config (enabled by default with cloudflare)\");\n return true;\n },\n },\n {\n name: \"fix-agent-commands\",\n apply(raw) {\n const COMMAND_MIGRATIONS: Record<string, string[]> = {\n \"claude-agent-acp\": [\"claude\", \"claude-code\"],\n };\n\n const agents = raw.agents;\n if (!agents || typeof agents !== \"object\") return false;\n\n let changed = false;\n for (const [agentName, agentDef] of Object.entries(agents as Record<string, unknown>)) {\n if (!agentDef || typeof agentDef !== \"object\" || !(\"command\" in agentDef)) continue;\n const def = agentDef as Record<string, unknown>;\n if (typeof def.command !== \"string\") continue;\n for (const [correctCmd, legacyCmds] of Object.entries(COMMAND_MIGRATIONS)) {\n if (legacyCmds.includes(def.command as string)) {\n log.warn(\n { agent: agentName, oldCommand: def.command, newCommand: correctCmd },\n `Auto-migrating agent command: \"${def.command}\" → \"${correctCmd}\"`,\n );\n def.command = correctCmd;\n changed = true;\n }\n }\n }\n return changed;\n },\n },\n {\n name: \"migrate-agents-to-store\",\n apply(raw) {\n const agentsJsonPath = path.join(os.homedir(), \".openacp\", \"agents.json\");\n if (fs.existsSync(agentsJsonPath)) return false;\n\n const agents = raw.agents as Record<string, unknown> | undefined;\n if (!agents || Object.keys(agents).length === 0) return false;\n\n const COMMAND_TO_REGISTRY: Record<string, string> = {\n \"claude-agent-acp\": \"claude-acp\",\n \"codex\": \"codex-acp\",\n };\n\n const installed: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(agents)) {\n const cfg = val as Record<string, unknown>;\n const command = typeof cfg.command === \"string\" ? cfg.command : \"\";\n const registryId = COMMAND_TO_REGISTRY[command] ?? null;\n installed[key] = {\n registryId,\n name: key.charAt(0).toUpperCase() + key.slice(1),\n version: \"unknown\",\n distribution: \"custom\",\n command: cfg.command,\n args: cfg.args ?? [],\n env: cfg.env ?? {},\n workingDirectory: cfg.workingDirectory ?? undefined,\n installedAt: new Date().toISOString(),\n binaryPath: null,\n };\n }\n\n fs.mkdirSync(path.dirname(agentsJsonPath), { recursive: true });\n fs.writeFileSync(agentsJsonPath, JSON.stringify({ version: 1, installed }, null, 2));\n\n raw.agents = {};\n return true;\n },\n },\n];\n\n/**\n * Apply all migrations to raw config (mutates in place).\n * Returns whether any changes were made.\n */\nexport function applyMigrations(\n raw: RawConfig,\n migrationList: Migration[] = migrations,\n): { changed: boolean } {\n let changed = false;\n for (const migration of migrationList) {\n if (migration.apply(raw)) {\n changed = true;\n }\n }\n return { changed };\n}\n"],"mappings":"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","names":["fs","path","os","log","resolved"]}
@@ -87,6 +87,44 @@ var CONFIG_REGISTRY = [
87
87
  type: "string",
88
88
  scope: "sensitive",
89
89
  hotReload: true
90
+ },
91
+ {
92
+ path: "speech.tts.provider",
93
+ displayName: "Text to Speech",
94
+ group: "speech",
95
+ type: "select",
96
+ options: ["edge-tts"],
97
+ scope: "safe",
98
+ hotReload: true
99
+ },
100
+ {
101
+ path: "speech.tts.providers.edge-tts.voice",
102
+ displayName: "Edge TTS Voice",
103
+ group: "speech",
104
+ type: "select",
105
+ options: [
106
+ "en-US-AriaNeural",
107
+ "en-US-GuyNeural",
108
+ "en-US-JennyNeural",
109
+ "en-GB-SoniaNeural",
110
+ "en-AU-NatashaNeural",
111
+ "vi-VN-HoaiMyNeural",
112
+ "vi-VN-NamMinhNeural",
113
+ "zh-CN-XiaoxiaoNeural",
114
+ "zh-CN-YunxiNeural",
115
+ "ja-JP-NanamiNeural",
116
+ "ja-JP-KeitaNeural",
117
+ "ko-KR-SunHiNeural",
118
+ "ko-KR-InJoonNeural",
119
+ "es-ES-ElviraNeural",
120
+ "fr-FR-DeniseNeural",
121
+ "de-DE-KatjaNeural",
122
+ "pt-BR-FranciscaNeural",
123
+ "hi-IN-SwaraNeural",
124
+ "ar-SA-ZariyahNeural"
125
+ ],
126
+ scope: "safe",
127
+ hotReload: true
90
128
  }
91
129
  ];
92
130
  function getFieldDef(path2) {
@@ -124,4 +162,4 @@ export {
124
162
  resolveOptions,
125
163
  getConfigValue
126
164
  };
127
- //# sourceMappingURL=chunk-4TR5Y3MP.js.map
165
+ //# sourceMappingURL=chunk-F3AICYO4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/config-registry.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport type { Config } from './config.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(os.homedir(), \".openacp\", \"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 { /* fallback */ }\n return Object.keys(config.agents ?? {});\n },\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: 'speech.tts.provider',\n displayName: 'Text to Speech',\n group: 'speech',\n type: 'select',\n options: ['edge-tts'],\n scope: 'safe',\n hotReload: true,\n },\n {\n path: 'speech.tts.providers.edge-tts.voice',\n displayName: 'Edge TTS Voice',\n group: 'speech',\n type: 'select',\n options: [\n 'en-US-AriaNeural', 'en-US-GuyNeural', 'en-US-JennyNeural',\n 'en-GB-SoniaNeural', 'en-AU-NatashaNeural',\n 'vi-VN-HoaiMyNeural', 'vi-VN-NamMinhNeural',\n 'zh-CN-XiaoxiaoNeural', 'zh-CN-YunxiNeural',\n 'ja-JP-NanamiNeural', 'ja-JP-KeitaNeural',\n 'ko-KR-SunHiNeural', 'ko-KR-InJoonNeural',\n 'es-ES-ElviraNeural', 'fr-FR-DeniseNeural',\n 'de-DE-KatjaNeural', 'pt-BR-FranciscaNeural',\n 'hi-IN-SwaraNeural', 'ar-SA-ZariyahNeural',\n ],\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(def: ConfigFieldDef, config: Config): 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"]}
@@ -17,6 +17,34 @@ var ChannelAdapter = class {
17
17
  }
18
18
  };
19
19
 
20
+ // src/adapters/shared/message-dispatcher.ts
21
+ async function dispatchMessage(handlers, ctx, content) {
22
+ switch (content.type) {
23
+ case "text":
24
+ return handlers.onText(ctx, content);
25
+ case "thought":
26
+ return handlers.onThought(ctx, content);
27
+ case "tool_call":
28
+ return handlers.onToolCall(ctx, content);
29
+ case "tool_update":
30
+ return handlers.onToolUpdate(ctx, content);
31
+ case "plan":
32
+ return handlers.onPlan(ctx, content);
33
+ case "usage":
34
+ return handlers.onUsage(ctx, content);
35
+ case "session_end":
36
+ return handlers.onSessionEnd(ctx, content);
37
+ case "error":
38
+ return handlers.onError(ctx, content);
39
+ case "attachment":
40
+ return handlers.onAttachment(ctx, content);
41
+ case "system_message":
42
+ return handlers.onSystemMessage(ctx, content);
43
+ default:
44
+ return;
45
+ }
46
+ }
47
+
20
48
  // src/product-guide.ts
21
49
  var PRODUCT_GUIDE = `
22
50
  # OpenACP \u2014 Product Guide
@@ -437,6 +465,7 @@ Session logs auto-cleanup: 30 days (configurable via \`logging.sessionLogRetenti
437
465
 
438
466
  export {
439
467
  ChannelAdapter,
440
- PRODUCT_GUIDE
468
+ PRODUCT_GUIDE,
469
+ dispatchMessage
441
470
  };
442
- //# sourceMappingURL=chunk-LGQYTK55.js.map
471
+ //# sourceMappingURL=chunk-FMWSVLRM.js.map