@openacp/cli 2026.326.3 → 2026.327.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.
Files changed (72) hide show
  1. package/README.md +2 -2
  2. package/dist/{adapter-77ZCVABT.js → adapter-IZNL6AK2.js} +13 -13
  3. package/dist/{adapter-6ANPBSVU.js → adapter-Z435XYBQ.js} +2 -2
  4. package/dist/{api-server-CHVSUDBX.js → api-server-2I7B3MXR.js} +2 -2
  5. package/dist/{api-server-3PYLRBCN.js → api-server-5VEESFOT.js} +2 -2
  6. package/dist/{chunk-Y64XWMJ4.js → chunk-366FOUJG.js} +2 -2
  7. package/dist/{chunk-WVLDNYOJ.js → chunk-5RO42TWV.js} +2 -2
  8. package/dist/{chunk-UNJUWWQO.js → chunk-5YW56UJK.js} +1 -10
  9. package/dist/chunk-5YW56UJK.js.map +1 -0
  10. package/dist/{chunk-NBFIBGAT.js → chunk-7KGWYNWE.js} +1 -1
  11. package/dist/chunk-CDAUYTVP.js +41 -0
  12. package/dist/chunk-CDAUYTVP.js.map +1 -0
  13. package/dist/{chunk-Q6ZXJTZB.js → chunk-CFM4GJ74.js} +7 -11
  14. package/dist/chunk-CFM4GJ74.js.map +1 -0
  15. package/dist/{chunk-RKB2ZK6S.js → chunk-FDLQ5M6W.js} +17 -136
  16. package/dist/chunk-FDLQ5M6W.js.map +1 -0
  17. package/dist/{chunk-FQEBWOZR.js → chunk-QUXTZU36.js} +45 -126
  18. package/dist/chunk-QUXTZU36.js.map +1 -0
  19. package/dist/{chunk-QSDZDHNS.js → chunk-VO3A2NI4.js} +4 -4
  20. package/dist/{chunk-V5JT5TPD.js → chunk-ZPTM4NGK.js} +2 -2
  21. package/dist/cli.js +145 -46
  22. package/dist/cli.js.map +1 -1
  23. package/dist/{config-editor-HNEKXRLQ.js → config-editor-2GYL2SSZ.js} +2 -2
  24. package/dist/{core-plugins-VEUNFTMB.js → core-plugins-I6UPXQBL.js} +7 -10
  25. package/dist/{discord-NOJQ5PZO.js → discord-DXDTGVGS.js} +2 -2
  26. package/dist/index.d.ts +14 -62
  27. package/dist/index.js +15 -21
  28. package/dist/index.js.map +1 -1
  29. package/dist/{integrate-5C6KSU6D.js → integrate-APK4OEQF.js} +2 -2
  30. package/dist/integrate-APK4OEQF.js.map +1 -0
  31. package/dist/{main-T5WVCCFN.js → main-EJBK65NS.js} +33 -52
  32. package/dist/main-EJBK65NS.js.map +1 -0
  33. package/dist/{new-session-AVQCNXRG.js → new-session-HFO5GHSZ.js} +3 -3
  34. package/dist/plugin-create-LCXXNDK6.js +950 -0
  35. package/dist/plugin-create-LCXXNDK6.js.map +1 -0
  36. package/dist/plugin-search-HQ4WQKOF.js +40 -0
  37. package/dist/plugin-search-HQ4WQKOF.js.map +1 -0
  38. package/dist/{post-upgrade-XLHZ6ZB7.js → post-upgrade-2MG3VUDV.js} +2 -2
  39. package/dist/registry-client-AVGRE4CF.js +8 -0
  40. package/dist/{setup-BAI2F24H.js → setup-N7KT56O7.js} +7 -7
  41. package/dist/{telegram-ZDC3JQF2.js → telegram-QWMJU3A6.js} +2 -2
  42. package/package.json +2 -2
  43. package/dist/chunk-2CX4IEEC.js +0 -124
  44. package/dist/chunk-2CX4IEEC.js.map +0 -1
  45. package/dist/chunk-FQEBWOZR.js.map +0 -1
  46. package/dist/chunk-Q6ZXJTZB.js.map +0 -1
  47. package/dist/chunk-RKB2ZK6S.js.map +0 -1
  48. package/dist/chunk-UNJUWWQO.js.map +0 -1
  49. package/dist/chunk-WAAD23KY.js +0 -222
  50. package/dist/chunk-WAAD23KY.js.map +0 -1
  51. package/dist/integrate-5C6KSU6D.js.map +0 -1
  52. package/dist/main-T5WVCCFN.js.map +0 -1
  53. package/dist/plugin-create-LYF5PP5W.js +0 -327
  54. package/dist/plugin-create-LYF5PP5W.js.map +0 -1
  55. package/dist/usage-WYNK6ZC5.js +0 -10
  56. /package/dist/{adapter-77ZCVABT.js.map → adapter-IZNL6AK2.js.map} +0 -0
  57. /package/dist/{adapter-6ANPBSVU.js.map → adapter-Z435XYBQ.js.map} +0 -0
  58. /package/dist/{api-server-3PYLRBCN.js.map → api-server-2I7B3MXR.js.map} +0 -0
  59. /package/dist/{api-server-CHVSUDBX.js.map → api-server-5VEESFOT.js.map} +0 -0
  60. /package/dist/{chunk-Y64XWMJ4.js.map → chunk-366FOUJG.js.map} +0 -0
  61. /package/dist/{chunk-WVLDNYOJ.js.map → chunk-5RO42TWV.js.map} +0 -0
  62. /package/dist/{chunk-NBFIBGAT.js.map → chunk-7KGWYNWE.js.map} +0 -0
  63. /package/dist/{chunk-QSDZDHNS.js.map → chunk-VO3A2NI4.js.map} +0 -0
  64. /package/dist/{chunk-V5JT5TPD.js.map → chunk-ZPTM4NGK.js.map} +0 -0
  65. /package/dist/{config-editor-HNEKXRLQ.js.map → config-editor-2GYL2SSZ.js.map} +0 -0
  66. /package/dist/{core-plugins-VEUNFTMB.js.map → core-plugins-I6UPXQBL.js.map} +0 -0
  67. /package/dist/{discord-NOJQ5PZO.js.map → discord-DXDTGVGS.js.map} +0 -0
  68. /package/dist/{new-session-AVQCNXRG.js.map → new-session-HFO5GHSZ.js.map} +0 -0
  69. /package/dist/{post-upgrade-XLHZ6ZB7.js.map → post-upgrade-2MG3VUDV.js.map} +0 -0
  70. /package/dist/{telegram-ZDC3JQF2.js.map → registry-client-AVGRE4CF.js.map} +0 -0
  71. /package/dist/{setup-BAI2F24H.js.map → setup-N7KT56O7.js.map} +0 -0
  72. /package/dist/{usage-WYNK6ZC5.js.map → telegram-QWMJU3A6.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/commands/plugin-create.ts","../../src/cli/plugin-template/package-json.ts","../../src/cli/plugin-template/tsconfig.ts","../../src/cli/plugin-template/dotfiles.ts","../../src/cli/plugin-template/readme.ts","../../src/cli/plugin-template/plugin-source.ts","../../src/cli/plugin-template/plugin-test.ts","../../src/cli/plugin-template/claude-md.ts","../../src/cli/plugin-template/plugin-guide.ts"],"sourcesContent":["import * as p from '@clack/prompts'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport { getCurrentVersion } from '../version.js'\nimport {\n type TemplateParams,\n generatePackageJson,\n generateTsconfig,\n generateGitignore,\n generateNpmignore,\n generateEditorconfig,\n generateReadme,\n generatePluginSource,\n generatePluginTest,\n generateClaudeMd,\n generatePluginGuide,\n} from '../plugin-template/index.js'\n\nexport async function cmdPluginCreate(): Promise<void> {\n p.intro('Create a new OpenACP plugin')\n\n const result = await p.group(\n {\n name: () =>\n p.text({\n message: 'Plugin name (e.g., @myorg/adapter-matrix)',\n placeholder: '@myorg/my-plugin',\n validate: (value: string | undefined) => {\n if (!value || !value.trim()) return 'Plugin name is required'\n if (!/^(@[a-z0-9-]+\\/)?[a-z0-9-]+$/.test(value.trim())) {\n return 'Must be a valid npm package name (lowercase, hyphens, optional @scope/)'\n }\n return undefined\n },\n }),\n description: () =>\n p.text({\n message: 'Description',\n placeholder: 'A short description of your plugin',\n }),\n author: () =>\n p.text({\n message: 'Author',\n placeholder: 'Your Name <email@example.com>',\n }),\n license: () =>\n p.select({\n message: 'License',\n options: [\n { value: 'MIT', label: 'MIT' },\n { value: 'Apache-2.0', label: 'Apache 2.0' },\n { value: 'ISC', label: 'ISC' },\n { value: 'UNLICENSED', label: 'Unlicensed (private)' },\n ],\n }),\n },\n {\n onCancel: () => {\n p.cancel('Plugin creation cancelled.')\n process.exit(0)\n },\n },\n )\n\n const pluginName = result.name.trim()\n const dirName = pluginName.replace(/^@[^/]+\\//, '') // strip scope for directory name\n const targetDir = path.resolve(process.cwd(), dirName)\n\n if (fs.existsSync(targetDir)) {\n p.cancel(`Directory \"${dirName}\" already exists.`)\n process.exit(1)\n }\n\n const spinner = p.spinner()\n spinner.start('Scaffolding plugin...')\n\n // Create directory structure\n fs.mkdirSync(path.join(targetDir, 'src', '__tests__'), { recursive: true })\n\n // Collect template params\n const params: TemplateParams = {\n pluginName,\n description: (result.description as string) || '',\n author: (result.author as string) || '',\n license: result.license as string,\n cliVersion: getCurrentVersion(),\n }\n\n // Generate and write all files\n const files: Array<{ relativePath: string; content: string }> = [\n { relativePath: 'package.json', content: generatePackageJson(params) },\n { relativePath: 'tsconfig.json', content: generateTsconfig() },\n { relativePath: '.gitignore', content: generateGitignore() },\n { relativePath: '.npmignore', content: generateNpmignore() },\n { relativePath: '.editorconfig', content: generateEditorconfig() },\n { relativePath: 'README.md', content: generateReadme(params) },\n { relativePath: 'CLAUDE.md', content: generateClaudeMd(params) },\n { relativePath: 'PLUGIN_GUIDE.md', content: generatePluginGuide(params) },\n { relativePath: path.join('src', 'index.ts'), content: generatePluginSource(params) },\n { relativePath: path.join('src', '__tests__', 'index.test.ts'), content: generatePluginTest(params) },\n ]\n\n for (const file of files) {\n fs.writeFileSync(path.join(targetDir, file.relativePath), file.content)\n }\n\n spinner.stop('Plugin scaffolded!')\n\n p.note(\n [\n `cd ${dirName}`,\n 'npm install',\n 'npm run build',\n 'npm test',\n '',\n '# Start development with hot-reload:',\n `openacp dev .`,\n ].join('\\n'),\n 'Next steps',\n )\n\n p.outro(`Plugin ${pluginName} created in ./${dirName}`)\n}\n","export interface TemplateParams {\n pluginName: string\n description: string\n author: string\n license: string\n cliVersion: string\n}\n\nexport function generatePackageJson(params: TemplateParams): string {\n const packageJson = {\n name: params.pluginName,\n version: '0.1.0',\n description: params.description || '',\n type: 'module',\n main: 'dist/index.js',\n types: 'dist/index.d.ts',\n scripts: {\n build: 'tsc',\n dev: 'tsc --watch',\n test: 'vitest',\n prepublishOnly: 'npm run build',\n },\n author: params.author || '',\n license: params.license,\n keywords: ['openacp', 'openacp-plugin'],\n engines: {\n openacp: `>=${params.cliVersion}`,\n },\n peerDependencies: {\n '@openacp/cli': `>=${params.cliVersion}`,\n },\n devDependencies: {\n '@openacp/plugin-sdk': params.cliVersion,\n typescript: '^5.4.0',\n vitest: '^3.0.0',\n },\n }\n return JSON.stringify(packageJson, null, 2) + '\\n'\n}\n","export function generateTsconfig(): string {\n const tsconfig = {\n compilerOptions: {\n target: 'ES2022',\n module: 'NodeNext',\n moduleResolution: 'NodeNext',\n declaration: true,\n outDir: 'dist',\n rootDir: 'src',\n strict: true,\n esModuleInterop: true,\n skipLibCheck: true,\n forceConsistentCasingInFileNames: true,\n },\n include: ['src'],\n exclude: ['node_modules', 'dist', 'src/**/__tests__'],\n }\n return JSON.stringify(tsconfig, null, 2) + '\\n'\n}\n","export function generateGitignore(): string {\n return ['node_modules/', 'dist/', '*.tsbuildinfo', '.DS_Store', ''].join('\\n')\n}\n\nexport function generateNpmignore(): string {\n return ['src/', 'tsconfig.json', '.editorconfig', '.gitignore', '*.test.ts', '__tests__/', ''].join('\\n')\n}\n\nexport function generateEditorconfig(): string {\n return [\n 'root = true',\n '',\n '[*]',\n 'indent_style = space',\n 'indent_size = 2',\n 'end_of_line = lf',\n 'charset = utf-8',\n 'trim_trailing_whitespace = true',\n 'insert_final_newline = true',\n '',\n ].join('\\n')\n}\n","import type { TemplateParams } from './package-json.js'\n\nexport function generateReadme(params: TemplateParams): string {\n return [\n `# ${params.pluginName}`,\n '',\n params.description || 'An OpenACP plugin.',\n '',\n '## Installation',\n '',\n '```bash',\n `openacp plugin add ${params.pluginName}`,\n '```',\n '',\n '## Development',\n '',\n '```bash',\n 'npm install',\n 'npm run build',\n 'npm test',\n '',\n '# Live development with hot-reload:',\n `openacp dev .`,\n '```',\n '',\n '## License',\n '',\n params.license,\n '',\n ].join('\\n')\n}\n","import type { TemplateParams } from './package-json.js'\n\nexport function generatePluginSource(params: TemplateParams): string {\n const dirName = params.pluginName.replace(/^@[^/]+\\//, '')\n const escapedDescription = (params.description || '').replace(/'/g, \"\\\\'\")\n\n return `import type { OpenACPPlugin, PluginContext, InstallContext, MigrateContext } from '@openacp/plugin-sdk'\n\nconst plugin: OpenACPPlugin = {\n name: '${params.pluginName}',\n version: '0.1.0',\n description: '${escapedDescription}',\n\n // Declare which permissions your plugin needs.\n // Available: events:read, events:emit, services:register, services:use,\n // middleware:register, commands:register, storage:read, storage:write, kernel:access\n permissions: ['events:read', 'services:register'],\n\n // Dependencies on other plugins (loaded before this one).\n // pluginDependencies: { '@openacp/security': '>=1.0.0' },\n\n // Optional dependencies (used if available, gracefully degrade if not).\n // optionalPluginDependencies: { '@openacp/usage': '>=1.0.0' },\n\n /**\n * Called during server startup in dependency order.\n * Register services, middleware, commands, and event listeners here.\n */\n async setup(ctx: PluginContext): Promise<void> {\n ctx.log.info('Plugin setup started')\n\n // Example: register a service\n // ctx.registerService('my-service', myServiceImpl)\n\n // Example: listen to events\n // ctx.on('session:created', (event) => { ... })\n\n // Example: register a slash command\n // ctx.registerCommand({\n // name: 'mycommand',\n // description: 'Does something useful',\n // category: 'plugin',\n // async handler(args) {\n // return { type: 'text', text: 'Hello from ${params.pluginName}!' }\n // },\n // })\n\n ctx.log.info('Plugin setup complete')\n },\n\n /**\n * Called during server shutdown in reverse dependency order.\n * Clean up resources, close connections, stop timers here.\n * Has a 10-second timeout.\n */\n async teardown(): Promise<void> {\n // Clean up resources here\n },\n\n /**\n * Called when user runs \\`openacp plugin add ${params.pluginName}\\`.\n * Use ctx.terminal for interactive prompts to gather configuration.\n */\n async install(ctx: InstallContext): Promise<void> {\n ctx.terminal.log.info('Installing ${params.pluginName}...')\n\n // Example: prompt for configuration\n // const apiKey = await ctx.terminal.text({\n // message: 'Enter your API key',\n // validate: (v) => v.length === 0 ? 'Required' : undefined,\n // })\n // await ctx.settings.set('apiKey', apiKey)\n\n ctx.terminal.log.success('Installation complete!')\n },\n\n /**\n * Called when user runs \\`openacp plugin configure ${params.pluginName}\\`.\n * Re-run configuration prompts to update settings.\n */\n async configure(ctx: InstallContext): Promise<void> {\n ctx.terminal.log.info('Configuring ${params.pluginName}...')\n\n // Re-run configuration prompts, pre-filling with current values\n // const current = await ctx.settings.getAll()\n // ...\n\n ctx.terminal.log.success('Configuration updated!')\n },\n\n /**\n * Called during boot when the plugin version has changed.\n * Migrate settings from the old format to the new format.\n */\n async migrate(ctx: MigrateContext, oldSettings: unknown, oldVersion: string): Promise<unknown> {\n ctx.log.info(\\`Migrating from v\\${oldVersion}\\`)\n // Return the migrated settings object\n return oldSettings\n },\n\n /**\n * Called when user runs \\`openacp plugin remove ${params.pluginName}\\`.\n * Clean up any external resources. If opts.purge is true, delete all data.\n */\n async uninstall(ctx: InstallContext, opts: { purge: boolean }): Promise<void> {\n ctx.terminal.log.info('Uninstalling ${params.pluginName}...')\n if (opts.purge) {\n await ctx.settings.clear()\n }\n ctx.terminal.log.success('Uninstalled!')\n },\n}\n\nexport default plugin\n`\n}\n","import type { TemplateParams } from './package-json.js'\n\nexport function generatePluginTest(params: TemplateParams): string {\n return `import { describe, it, expect } from 'vitest'\nimport { createTestContext, createTestInstallContext } from '@openacp/plugin-sdk/testing'\nimport plugin from '../index.js'\n\ndescribe('${params.pluginName}', () => {\n it('has correct metadata', () => {\n expect(plugin.name).toBe('${params.pluginName}')\n expect(plugin.version).toBeDefined()\n expect(plugin.setup).toBeInstanceOf(Function)\n })\n\n it('sets up without errors', async () => {\n const ctx = createTestContext({\n pluginName: '${params.pluginName}',\n pluginConfig: { enabled: true },\n permissions: plugin.permissions,\n })\n await expect(plugin.setup(ctx)).resolves.not.toThrow()\n })\n\n it('tears down without errors', async () => {\n if (plugin.teardown) {\n await expect(plugin.teardown()).resolves.not.toThrow()\n }\n })\n\n it('installs without errors', async () => {\n if (plugin.install) {\n const ctx = createTestInstallContext({\n pluginName: '${params.pluginName}',\n terminalResponses: { password: [''], confirm: [true], select: ['apiKey'] },\n })\n await expect(plugin.install(ctx)).resolves.not.toThrow()\n }\n })\n})\n`\n}\n","import type { TemplateParams } from './package-json.js'\n\nexport function generateClaudeMd(params: TemplateParams): string {\n return `# CLAUDE.md\n\nThis file provides context for AI coding agents (Claude, Cursor, etc.) working on this plugin.\n\n## Project Overview\n\nThis is an OpenACP plugin. OpenACP bridges AI coding agents to messaging platforms via the Agent Client Protocol (ACP). Plugins extend OpenACP with new adapters, services, commands, and middleware.\n\n- **Package**: ${params.pluginName}\n- **SDK**: \\`@openacp/plugin-sdk\\` (types, base classes, testing utilities)\n- **Entry point**: \\`src/index.ts\\` (default export of \\`OpenACPPlugin\\` object)\n\n## Build & Run\n\n\\`\\`\\`bash\nnpm install # Install dependencies\nnpm run build # Compile TypeScript (tsc)\nnpm run dev # Watch mode (tsc --watch)\nnpm test # Run tests (vitest)\n\\`\\`\\`\n\n### Development with hot-reload\n\n\\`\\`\\`bash\nopenacp dev . # Compiles, watches, and reloads plugin on changes\n\\`\\`\\`\n\n## File Structure\n\n\\`\\`\\`\nsrc/\n index.ts — Plugin entry point (exports OpenACPPlugin)\n __tests__/\n index.test.ts — Tests using @openacp/plugin-sdk/testing\npackage.json — engines.openacp declares minimum CLI version\ntsconfig.json — ES2022, NodeNext, strict mode\nCLAUDE.md — This file (AI agent context)\nPLUGIN_GUIDE.md — Human-readable developer guide\n\\`\\`\\`\n\n## Architecture: How OpenACP Plugins Work\n\n### Plugin Lifecycle\n\n\\`\\`\\`\ninstall ──> [reboot] ──> migrate? ──> setup ──> [running] ──> teardown ──> uninstall\n\\`\\`\\`\n\n| Hook | Trigger | Interactive? | Has Services? |\n|------|---------|-------------|---------------|\n| \\`install(ctx)\\` | \\`openacp plugin add <name>\\` | Yes | No |\n| \\`migrate(ctx, oldSettings, oldVersion)\\` | Boot — stored version differs from plugin version | No | No |\n| \\`configure(ctx)\\` | \\`openacp plugin configure <name>\\` | Yes | No |\n| \\`setup(ctx)\\` | Every boot, after migrate | No | Yes |\n| \\`teardown()\\` | Shutdown (10s timeout) | No | Yes |\n| \\`uninstall(ctx, opts)\\` | \\`openacp plugin remove <name>\\` | Yes | No |\n\n### OpenACPPlugin Interface\n\n\\`\\`\\`typescript\ninterface OpenACPPlugin {\n name: string // unique identifier, e.g. '@myorg/my-plugin'\n version: string // semver\n description?: string\n permissions?: PluginPermission[]\n pluginDependencies?: Record<string, string> // name -> semver range\n optionalPluginDependencies?: Record<string, string> // used if available\n overrides?: string // replace a built-in plugin entirely\n settingsSchema?: ZodSchema // Zod validation for settings\n essential?: boolean // true = needs setup before system can run\n\n setup(ctx: PluginContext): Promise<void>\n teardown?(): Promise<void>\n install?(ctx: InstallContext): Promise<void>\n configure?(ctx: InstallContext): Promise<void>\n migrate?(ctx: MigrateContext, oldSettings: unknown, oldVersion: string): Promise<unknown>\n uninstall?(ctx: InstallContext, opts: { purge: boolean }): Promise<void>\n}\n\\`\\`\\`\n\n### PluginContext API (available in setup)\n\n\\`\\`\\`typescript\ninterface PluginContext {\n pluginName: string\n pluginConfig: Record<string, unknown> // from settings.json\n\n // Events (requires 'events:read' / 'events:emit')\n on(event: string, handler: (...args: unknown[]) => void): void\n off(event: string, handler: (...args: unknown[]) => void): void\n emit(event: string, payload: unknown): void\n\n // Services (requires 'services:register' / 'services:use')\n registerService<T>(name: string, implementation: T): void\n getService<T>(name: string): T | undefined\n\n // Middleware (requires 'middleware:register')\n registerMiddleware<H extends MiddlewareHook>(hook: H, opts: MiddlewareOptions<MiddlewarePayloadMap[H]>): void\n\n // Commands (requires 'commands:register')\n registerCommand(def: CommandDef): void\n\n // Storage (requires 'storage:read' / 'storage:write')\n storage: PluginStorage // get, set, delete, list, getDataDir\n\n // Messaging (requires 'services:use')\n sendMessage(sessionId: string, content: OutgoingMessage): Promise<void>\n\n // Kernel access (requires 'kernel:access')\n sessions: SessionManager\n config: ConfigManager\n eventBus: EventBus\n\n // Always available\n log: Logger // trace, debug, info, warn, error, fatal, child\n}\n\\`\\`\\`\n\n### CommandDef and CommandResponse\n\n\\`\\`\\`typescript\ninterface CommandDef {\n name: string // command name without slash\n description: string // shown in /help\n usage?: string // e.g. '<city>' or 'on|off'\n category: 'system' | 'plugin'\n handler(args: CommandArgs): Promise<CommandResponse | void>\n}\n\ninterface CommandArgs {\n raw: string // text after command name\n sessionId: string | null\n channelId: string // 'telegram', 'discord', 'slack'\n userId: string\n reply(content: string | CommandResponse): Promise<void>\n}\n\ntype CommandResponse =\n | { type: 'text'; text: string }\n | { type: 'menu'; title: string; options: MenuOption[] }\n | { type: 'list'; title: string; items: ListItem[] }\n | { type: 'confirm'; question: string; onYes: string; onNo: string }\n | { type: 'error'; message: string }\n | { type: 'silent' }\n\\`\\`\\`\n\n### Settings System\n\n- \\`settingsSchema\\`: Zod schema for validation\n- \\`SettingsAPI\\` (in InstallContext): get, set, getAll, setAll, delete, clear, has\n- Settings stored at \\`~/.openacp/plugins/@scope/name/settings.json\\`\n- \\`PluginStorage\\` (in PluginContext): key-value store at \\`~/.openacp/plugins/data/@scope/name/kv.json\\`\n- \\`storage.getDataDir()\\`: returns path for large files, databases, caches\n\n### InstallContext (for install/configure/uninstall)\n\n\\`\\`\\`typescript\ninterface InstallContext {\n pluginName: string\n terminal: TerminalIO // text, select, confirm, password, multiselect, log, spinner, note\n settings: SettingsAPI\n legacyConfig?: Record<string, unknown>\n dataDir: string\n log: Logger\n}\n\\`\\`\\`\n\n### Service Interfaces (available via ctx.getService)\n\n| Service name | Interface | Description |\n|---|---|---|\n| \\`security\\` | \\`SecurityService\\` | Access control, session limits, user roles |\n| \\`file-service\\` | \\`FileServiceInterface\\` | File saving, resolving, format conversion |\n| \\`notifications\\` | \\`NotificationService\\` | Send notifications to users |\n| \\`usage\\` | \\`UsageService\\` | Token/cost tracking and budget checking |\n| \\`speech\\` | \\`SpeechServiceInterface\\` | Text-to-speech and speech-to-text |\n| \\`tunnel\\` | \\`TunnelServiceInterface\\` | Port tunneling and public URL management |\n| \\`context\\` | \\`ContextService\\` | Context building for agent sessions |\n\n## Plugin Permissions\n\nDeclare in \\`permissions\\` array. Only request what you need.\n\n| Permission | Allows |\n|---|---|\n| \\`events:read\\` | \\`ctx.on()\\` — subscribe to events |\n| \\`events:emit\\` | \\`ctx.emit()\\` — emit custom events (must prefix with plugin name) |\n| \\`services:register\\` | \\`ctx.registerService()\\` — provide services to other plugins |\n| \\`services:use\\` | \\`ctx.getService()\\`, \\`ctx.sendMessage()\\` — consume services |\n| \\`middleware:register\\` | \\`ctx.registerMiddleware()\\` — intercept and modify flows |\n| \\`commands:register\\` | \\`ctx.registerCommand()\\` — add chat commands |\n| \\`storage:read\\` | \\`ctx.storage.get()\\`, \\`ctx.storage.list()\\` |\n| \\`storage:write\\` | \\`ctx.storage.set()\\`, \\`ctx.storage.delete()\\` |\n| \\`kernel:access\\` | \\`ctx.sessions\\`, \\`ctx.config\\`, \\`ctx.eventBus\\`, \\`ctx.core\\` |\n\nCalling a method without the required permission throws \\`PluginPermissionError\\`.\n\n## Middleware Hooks (18 total)\n\nRegister with \\`ctx.registerMiddleware(hook, { priority?, handler })\\`. Return \\`null\\` to block the flow, call \\`next()\\` to continue.\n\n### Message flow\n- \\`message:incoming\\` — incoming user message (channelId, threadId, userId, text, attachments)\n- \\`message:outgoing\\` — outgoing message to user (sessionId, message)\n\n### Agent flow\n- \\`agent:beforePrompt\\` — before prompt is sent to agent (sessionId, text, attachments)\n- \\`agent:beforeEvent\\` — before agent event is processed (sessionId, event)\n- \\`agent:afterEvent\\` — after agent event, before delivery (sessionId, event, outgoingMessage)\n\n### Turn lifecycle\n- \\`turn:start\\` — agent turn begins (sessionId, promptText, promptNumber)\n- \\`turn:end\\` — agent turn ends (sessionId, stopReason, durationMs)\n\n### File system\n- \\`fs:beforeRead\\` — before file read (sessionId, path, line, limit)\n- \\`fs:beforeWrite\\` — before file write (sessionId, path, content)\n\n### Terminal\n- \\`terminal:beforeCreate\\` — before terminal process spawned (sessionId, command, args, env, cwd)\n- \\`terminal:afterExit\\` — after terminal process exits (sessionId, terminalId, command, exitCode, durationMs)\n\n### Permission\n- \\`permission:beforeRequest\\` — before permission prompt (sessionId, request, autoResolve)\n- \\`permission:afterResolve\\` — after permission resolved (sessionId, requestId, decision, userId, durationMs)\n\n### Session\n- \\`session:beforeCreate\\` — before session creation (agentName, workingDir, userId, channelId, threadId)\n- \\`session:afterDestroy\\` — after session destroyed (sessionId, reason, durationMs, promptCount)\n\n### Control\n- \\`mode:beforeChange\\` — before mode change (sessionId, fromMode, toMode)\n- \\`config:beforeChange\\` — before config change (sessionId, configId, oldValue, newValue)\n- \\`model:beforeChange\\` — before model change (sessionId, fromModel, toModel)\n- \\`agent:beforeCancel\\` — before agent cancellation (sessionId, reason)\n\n## Plugin Events (subscribe with ctx.on)\n\n### System\n- \\`kernel:booted\\`, \\`system:ready\\`, \\`system:shutdown\\`, \\`system:commands-ready\\`\n\n### Plugin lifecycle\n- \\`plugin:loaded\\`, \\`plugin:failed\\`, \\`plugin:disabled\\`, \\`plugin:unloaded\\`\n\n### Session\n- \\`session:created\\`, \\`session:ended\\`, \\`session:named\\`, \\`session:updated\\`\n\n### Agent\n- \\`agent:event\\`, \\`agent:prompt\\`\n\n### Permission\n- \\`permission:request\\`, \\`permission:resolved\\`\n\n## Testing\n\nUse \\`@openacp/plugin-sdk/testing\\`:\n\n\\`\\`\\`typescript\nimport { createTestContext, createTestInstallContext, mockServices } from '@openacp/plugin-sdk/testing'\n\\`\\`\\`\n\n### createTestContext(opts)\n\nCreates a test \\`PluginContext\\`. All state is in-memory.\n\n\\`\\`\\`typescript\nconst ctx = createTestContext({\n pluginName: '${params.pluginName}',\n pluginConfig: { enabled: true },\n permissions: plugin.permissions,\n services: { security: mockServices.security() },\n})\nawait plugin.setup(ctx)\nexpect(ctx.registeredCommands.has('mycommand')).toBe(true)\nconst response = await ctx.executeCommand('mycommand', { raw: 'test' })\n\\`\\`\\`\n\nInspection properties: \\`registeredServices\\`, \\`registeredCommands\\`, \\`registeredMiddleware\\`, \\`emittedEvents\\`, \\`sentMessages\\`, \\`executeCommand()\\`.\n\n### createTestInstallContext(opts)\n\nCreates a test \\`InstallContext\\`. Terminal prompts auto-answered from \\`terminalResponses\\`.\n\n\\`\\`\\`typescript\nconst ctx = createTestInstallContext({\n pluginName: '${params.pluginName}',\n terminalResponses: { password: ['sk-test-key'], select: ['en'] },\n})\nawait plugin.install!(ctx)\nexpect(ctx.settingsData.get('apiKey')).toBe('sk-test-key')\n\\`\\`\\`\n\n### mockServices\n\nFactory functions for mock service implementations:\n\n\\`\\`\\`typescript\nmockServices.security(overrides?) // checkAccess, checkSessionLimit, getUserRole\nmockServices.fileService(overrides?) // saveFile, resolveFile, readTextFileWithRange\nmockServices.notifications(overrides?) // notify, notifyAll\nmockServices.usage(overrides?) // trackUsage, checkBudget, getSummary\nmockServices.speech(overrides?) // textToSpeech, speechToText, register*\nmockServices.tunnel(overrides?) // getPublicUrl, start, stop, getStore, fileUrl, diffUrl\nmockServices.context(overrides?) // buildContext, registerProvider\n\\`\\`\\`\n\n## Conventions\n\n- **ESM-only**: \\`\"type\": \"module\"\\` in package.json\n- **Import extensions**: All imports must use \\`.js\\` extension (e.g., \\`import x from './util.js'\\`)\n- **TypeScript strict mode**: \\`strict: true\\` in tsconfig.json\n- **Target**: ES2022, module NodeNext\n- **Test framework**: Vitest\n- **Test files**: \\`src/**/__tests__/*.test.ts\\`\n\n## How to Add a Command\n\n\\`\\`\\`typescript\n// In setup():\nctx.registerCommand({\n name: 'mycommand',\n description: 'Does something useful',\n usage: '<arg>',\n category: 'plugin',\n async handler(args) {\n const input = args.raw.trim()\n if (!input) return { type: 'error', message: 'Usage: /mycommand <arg>' }\n return { type: 'text', text: \\\\\\`Result: \\\\\\${input}\\\\\\` }\n },\n})\n\\`\\`\\`\n\nRequires \\`commands:register\\` permission. Available as \\`/mycommand\\` (if no conflict) and \\`/pluginscope:mycommand\\` (always).\n\n## How to Add a Service\n\n\\`\\`\\`typescript\n// In setup():\nconst myService = new MyService()\nctx.registerService('my-service', myService)\n\\`\\`\\`\n\nRequires \\`services:register\\` permission. Other plugins consume with \\`ctx.getService<MyService>('my-service')\\`.\n\n## How to Add Middleware\n\n\\`\\`\\`typescript\n// In setup():\nctx.registerMiddleware('message:outgoing', {\n priority: 50, // lower = earlier execution\n handler: async (payload, next) => {\n payload.message.text = modifyText(payload.message.text)\n return next() // continue chain; return null to block\n },\n})\n\\`\\`\\`\n\nRequires \\`middleware:register\\` permission.\n\n## How Settings Work\n\n1. Define \\`settingsSchema\\` (Zod) on the plugin object\n2. In \\`install()\\`: use \\`ctx.terminal\\` for interactive prompts, save with \\`ctx.settings.set()\\`\n3. In \\`configure()\\`: re-run prompts with current values pre-filled\n4. In \\`setup()\\`: read settings from \\`ctx.pluginConfig\\`\n5. In \\`migrate()\\`: transform old settings to new format on version change\n\n## Version Compatibility\n\nThe \\`engines.openacp\\` field in package.json declares the minimum CLI version. OpenACP checks this on install and warns if incompatible.\n`\n}\n","import type { TemplateParams } from './package-json.js'\n\nexport function generatePluginGuide(params: TemplateParams): string {\n return `# Plugin Developer Guide\n\n## Overview\n\n**${params.pluginName}** is an OpenACP plugin.\n\n> TODO: Describe what this plugin does.\n\n## Project Structure\n\n\\`\\`\\`\nsrc/\n index.ts — Plugin entry point (exports OpenACPPlugin object)\n __tests__/\n index.test.ts — Tests using Vitest + @openacp/plugin-sdk/testing\npackage.json — npm package config with engines.openacp constraint\ntsconfig.json — TypeScript strict mode, ES2022, NodeNext\nCLAUDE.md — Full technical reference for AI coding agents\nPLUGIN_GUIDE.md — This file\n\\`\\`\\`\n\n## Development Workflow\n\n1. **Edit** \\`src/index.ts\\` — implement your plugin logic\n2. **Dev mode**: \\`openacp dev .\\` — compiles, watches, and hot-reloads your plugin\n3. **Test**: \\`npm test\\` — runs Vitest with SDK testing utilities\n4. **Build**: \\`npm run build\\` — compiles TypeScript to \\`dist/\\`\n\n\\`\\`\\`bash\nnpm install\nopenacp dev . # start developing with hot-reload\nnpm test # run tests\nnpm run build # compile for publishing\n\\`\\`\\`\n\n## Adding a Command\n\nRegister commands in your \\`setup()\\` function. Requires \\`commands:register\\` permission.\n\n\\`\\`\\`typescript\nasync setup(ctx: PluginContext) {\n ctx.registerCommand({\n name: 'greet',\n description: 'Send a greeting',\n usage: '[name]',\n category: 'plugin',\n async handler(args) {\n const name = args.raw.trim() || 'World'\n return { type: 'text', text: \\\\\\`Hello, \\\\\\${name}!\\\\\\` }\n },\n })\n}\n\\`\\`\\`\n\nThe command will be available as \\`/greet\\` in all messaging platforms.\n\n## Adding a Service\n\nProvide a service that other plugins can consume. Requires \\`services:register\\` permission.\n\n\\`\\`\\`typescript\nasync setup(ctx: PluginContext) {\n const myService = {\n doSomething(input: string): string {\n return input.toUpperCase()\n },\n }\n ctx.registerService('my-service', myService)\n}\n\\`\\`\\`\n\nOther plugins access it with \\`ctx.getService<MyServiceType>('my-service')\\`.\n\n## Adding Middleware\n\nIntercept and modify message flows. Requires \\`middleware:register\\` permission.\n\n\\`\\`\\`typescript\nasync setup(ctx: PluginContext) {\n ctx.registerMiddleware('message:outgoing', {\n priority: 50,\n handler: async (payload, next) => {\n // Modify the message before delivery\n payload.message.text += '\\\\n-- sent via ${params.pluginName}'\n return next() // continue the chain\n // return null to block the message entirely\n },\n })\n}\n\\`\\`\\`\n\n## Handling Settings\n\n### Install flow (first-time setup)\n\n\\`\\`\\`typescript\nasync install(ctx: InstallContext) {\n const apiKey = await ctx.terminal.password({\n message: 'Enter your API key:',\n validate: (v) => v.length > 0 ? undefined : 'Required',\n })\n await ctx.settings.set('apiKey', apiKey)\n ctx.terminal.log.success('Configured!')\n}\n\\`\\`\\`\n\n### Configure flow (reconfiguration)\n\n\\`\\`\\`typescript\nasync configure(ctx: InstallContext) {\n const current = await ctx.settings.getAll()\n const apiKey = await ctx.terminal.password({\n message: \\\\\\`API key (current: \\\\\\${current.apiKey ? '***' : 'not set'}):\\\\\\`,\n })\n if (apiKey) await ctx.settings.set('apiKey', apiKey)\n ctx.terminal.log.success('Updated!')\n}\n\\`\\`\\`\n\n### Reading settings at runtime\n\n\\`\\`\\`typescript\nasync setup(ctx: PluginContext) {\n const apiKey = ctx.pluginConfig.apiKey as string\n if (!apiKey) {\n ctx.log.warn('Not configured — run: openacp plugin configure ${params.pluginName}')\n return\n }\n // Use apiKey...\n}\n\\`\\`\\`\n\n## Testing\n\nTests use Vitest and \\`@openacp/plugin-sdk/testing\\`.\n\n\\`\\`\\`typescript\nimport { describe, it, expect } from 'vitest'\nimport { createTestContext, createTestInstallContext, mockServices } from '@openacp/plugin-sdk/testing'\nimport plugin from '../index.js'\n\ndescribe('${params.pluginName}', () => {\n it('registers commands on setup', async () => {\n const ctx = createTestContext({ pluginName: '${params.pluginName}' })\n await plugin.setup(ctx)\n expect(ctx.registeredCommands.has('greet')).toBe(true)\n })\n\n it('command returns expected response', async () => {\n const ctx = createTestContext({ pluginName: '${params.pluginName}' })\n await plugin.setup(ctx)\n const res = await ctx.executeCommand('greet', { raw: 'Alice' })\n expect(res).toEqual({ type: 'text', text: 'Hello, Alice!' })\n })\n\n it('install saves settings', async () => {\n const ctx = createTestInstallContext({\n pluginName: '${params.pluginName}',\n terminalResponses: { password: ['sk-test-key'] },\n })\n await plugin.install!(ctx)\n expect(ctx.settingsData.get('apiKey')).toBe('sk-test-key')\n })\n})\n\\`\\`\\`\n\n### Available mock services\n\n\\`\\`\\`typescript\nconst ctx = createTestContext({\n pluginName: '${params.pluginName}',\n services: {\n security: mockServices.security(),\n usage: mockServices.usage({ async checkBudget() { return { ok: false, percent: 100 } } }),\n },\n})\n\\`\\`\\`\n\n## Publishing\n\n1. Update \\`version\\` in both \\`package.json\\` and \\`src/index.ts\\`\n2. Build and test:\n \\`\\`\\`bash\n npm run build\n npm test\n \\`\\`\\`\n3. Publish:\n \\`\\`\\`bash\n npm publish --access public\n \\`\\`\\`\n4. Users install with:\n \\`\\`\\`bash\n openacp plugin install ${params.pluginName}\n \\`\\`\\`\n5. Submit to the [OpenACP Plugin Registry](https://github.com/Open-ACP/plugin-registry) for discoverability.\n\n## Useful Links\n\n- [Architecture: Plugin System](https://docs.openacp.dev/architecture/plugin-system)\n- [Architecture: Writing Plugins](https://docs.openacp.dev/architecture/writing-plugins)\n- [Architecture: Command System](https://docs.openacp.dev/architecture/command-system)\n- [Plugin SDK Reference](https://docs.openacp.dev/extending/plugin-sdk-reference)\n- [Getting Started: Your First Plugin](https://docs.openacp.dev/extending/getting-started-plugin)\n- [Dev Mode](https://docs.openacp.dev/extending/dev-mode)\n- [Contributing](https://github.com/Open-ACP/OpenACP/blob/main/CONTRIBUTING.md)\n`\n}\n"],"mappings":"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","names":["spinner"]}
@@ -0,0 +1,40 @@
1
+ import {
2
+ RegistryClient
3
+ } from "./chunk-CDAUYTVP.js";
4
+ import "./chunk-VUNV25KB.js";
5
+
6
+ // src/cli/commands/plugin-search.ts
7
+ async function cmdPluginSearch(args) {
8
+ const query = args.join(" ").trim();
9
+ if (!query) {
10
+ console.error("Usage: openacp plugin search <query>");
11
+ process.exit(1);
12
+ }
13
+ const client = new RegistryClient();
14
+ try {
15
+ const results = await client.search(query);
16
+ if (results.length === 0) {
17
+ console.log(`No plugins found matching "${query}"`);
18
+ return;
19
+ }
20
+ console.log(`
21
+ Found ${results.length} plugin${results.length > 1 ? "s" : ""} matching "${query}":
22
+ `);
23
+ for (const p of results) {
24
+ const verified = p.verified ? " \u2713" : "";
25
+ const featured = p.featured ? " \u2B50" : "";
26
+ console.log(` ${p.icon || "\u{1F4E6}"} ${p.displayName ?? p.name}${verified}${featured}`);
27
+ console.log(` ${p.description}`);
28
+ console.log(` ${p.category} | v${p.version} | npm: ${p.npm}`);
29
+ console.log(` Install: openacp plugin install ${p.name}`);
30
+ console.log();
31
+ }
32
+ } catch (err) {
33
+ console.error(`Failed to search registry: ${err}`);
34
+ process.exit(1);
35
+ }
36
+ }
37
+ export {
38
+ cmdPluginSearch
39
+ };
40
+ //# sourceMappingURL=plugin-search-HQ4WQKOF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/commands/plugin-search.ts"],"sourcesContent":["import { RegistryClient } from '../../core/plugin/registry-client.js'\n\nexport async function cmdPluginSearch(args: string[]): Promise<void> {\n const query = args.join(' ').trim()\n if (!query) {\n console.error('Usage: openacp plugin search <query>')\n process.exit(1)\n }\n\n const client = new RegistryClient()\n\n try {\n const results = await client.search(query)\n\n if (results.length === 0) {\n console.log(`No plugins found matching \"${query}\"`)\n return\n }\n\n console.log(`\\nFound ${results.length} plugin${results.length > 1 ? 's' : ''} matching \"${query}\":\\n`)\n for (const p of results) {\n const verified = p.verified ? ' ✓' : ''\n const featured = p.featured ? ' ⭐' : ''\n console.log(` ${p.icon || '📦'} ${p.displayName ?? p.name}${verified}${featured}`)\n console.log(` ${p.description}`)\n console.log(` ${p.category} | v${p.version} | npm: ${p.npm}`)\n console.log(` Install: openacp plugin install ${p.name}`)\n console.log()\n }\n } catch (err) {\n console.error(`Failed to search registry: ${err}`)\n process.exit(1)\n }\n}\n"],"mappings":";;;;;;AAEA,eAAsB,gBAAgB,MAA+B;AACnE,QAAM,QAAQ,KAAK,KAAK,GAAG,EAAE,KAAK;AAClC,MAAI,CAAC,OAAO;AACV,YAAQ,MAAM,sCAAsC;AACpD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAS,IAAI,eAAe;AAElC,MAAI;AACF,UAAM,UAAU,MAAM,OAAO,OAAO,KAAK;AAEzC,QAAI,QAAQ,WAAW,GAAG;AACxB,cAAQ,IAAI,8BAA8B,KAAK,GAAG;AAClD;AAAA,IACF;AAEA,YAAQ,IAAI;AAAA,QAAW,QAAQ,MAAM,UAAU,QAAQ,SAAS,IAAI,MAAM,EAAE,cAAc,KAAK;AAAA,CAAM;AACrG,eAAW,KAAK,SAAS;AACvB,YAAM,WAAW,EAAE,WAAW,YAAO;AACrC,YAAM,WAAW,EAAE,WAAW,YAAO;AACrC,cAAQ,IAAI,KAAK,EAAE,QAAQ,WAAI,IAAI,EAAE,eAAe,EAAE,IAAI,GAAG,QAAQ,GAAG,QAAQ,EAAE;AAClF,cAAQ,IAAI,QAAQ,EAAE,WAAW,EAAE;AACnC,cAAQ,IAAI,QAAQ,EAAE,QAAQ,OAAO,EAAE,OAAO,WAAW,EAAE,GAAG,EAAE;AAChE,cAAQ,IAAI,wCAAwC,EAAE,IAAI,EAAE;AAC5D,cAAQ,IAAI;AAAA,IACd;AAAA,EACF,SAAS,KAAK;AACZ,YAAQ,MAAM,8BAA8B,GAAG,EAAE;AACjD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;","names":[]}
@@ -29,7 +29,7 @@ async function runPostUpgradeChecks(config) {
29
29
  }
30
30
  }
31
31
  try {
32
- const { getIntegration } = await import("./integrate-5C6KSU6D.js");
32
+ const { getIntegration } = await import("./integrate-APK4OEQF.js");
33
33
  const integration = getIntegration("claude");
34
34
  if (integration) {
35
35
  const allInstalled = integration.items.every((item) => item.isInstalled());
@@ -77,4 +77,4 @@ async function runPostUpgradeChecks(config) {
77
77
  export {
78
78
  runPostUpgradeChecks
79
79
  };
80
- //# sourceMappingURL=post-upgrade-XLHZ6ZB7.js.map
80
+ //# sourceMappingURL=post-upgrade-2MG3VUDV.js.map
@@ -0,0 +1,8 @@
1
+ import {
2
+ RegistryClient
3
+ } from "./chunk-CDAUYTVP.js";
4
+ import "./chunk-VUNV25KB.js";
5
+ export {
6
+ RegistryClient
7
+ };
8
+ //# sourceMappingURL=registry-client-AVGRE4CF.js.map
@@ -1,11 +1,11 @@
1
- import {
2
- validateDiscordToken
3
- } from "./chunk-6RXVEXF3.js";
4
1
  import {
5
2
  validateBotAdmin,
6
3
  validateBotToken,
7
4
  validateChatId
8
5
  } from "./chunk-WQCJTU2C.js";
6
+ import {
7
+ validateDiscordToken
8
+ } from "./chunk-6RXVEXF3.js";
9
9
  import {
10
10
  commandExists
11
11
  } from "./chunk-ZSLHHQPQ.js";
@@ -340,7 +340,7 @@ async function setupIntegrations(config) {
340
340
  );
341
341
  if (installClaude) {
342
342
  try {
343
- const { getIntegration } = await import("./integrate-5C6KSU6D.js");
343
+ const { getIntegration } = await import("./integrate-APK4OEQF.js");
344
344
  const integration = getIntegration("claude");
345
345
  if (integration) {
346
346
  for (const item of integration.items) {
@@ -507,7 +507,7 @@ async function runSetup(configManager, opts) {
507
507
  currentStep++;
508
508
  if (settingsManager && pluginRegistry) {
509
509
  const { createInstallContext } = await import("./install-context-XPWTFT3J.js");
510
- const telegramPlugin = (await import("./telegram-ZDC3JQF2.js")).default;
510
+ const telegramPlugin = (await import("./telegram-QWMJU3A6.js")).default;
511
511
  const ctx = createInstallContext({
512
512
  pluginName: telegramPlugin.name,
513
513
  settingsManager,
@@ -530,7 +530,7 @@ async function runSetup(configManager, opts) {
530
530
  currentStep++;
531
531
  if (settingsManager && pluginRegistry) {
532
532
  const { createInstallContext } = await import("./install-context-XPWTFT3J.js");
533
- const discordPlugin = (await import("./discord-NOJQ5PZO.js")).default;
533
+ const discordPlugin = (await import("./discord-DXDTGVGS.js")).default;
534
534
  const ctx = createInstallContext({
535
535
  pluginName: discordPlugin.name,
536
536
  settingsManager,
@@ -744,4 +744,4 @@ export {
744
744
  validateChatId,
745
745
  validateDiscordToken
746
746
  };
747
- //# sourceMappingURL=setup-BAI2F24H.js.map
747
+ //# sourceMappingURL=setup-N7KT56O7.js.map
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  telegram_default
3
- } from "./chunk-Y64XWMJ4.js";
3
+ } from "./chunk-366FOUJG.js";
4
4
  import "./chunk-VUNV25KB.js";
5
5
  export {
6
6
  telegram_default as default
7
7
  };
8
- //# sourceMappingURL=telegram-ZDC3JQF2.js.map
8
+ //# sourceMappingURL=telegram-QWMJU3A6.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openacp/cli",
3
- "version": "2026.0326.3",
3
+ "version": "2026.0327.1",
4
4
  "description": "Self-hosted bridge for AI coding agents via ACP protocol",
5
5
  "type": "module",
6
6
  "bin": {
@@ -52,7 +52,7 @@
52
52
  "homepage": "https://github.com/Open-ACP/OpenACP",
53
53
  "author": {
54
54
  "name": "OpenACP",
55
- "url": "https://x.com/Open_ACP"
55
+ "url": "https://x.com/openacp_ai"
56
56
  },
57
57
  "license": "AGPL-3.0",
58
58
  "keywords": [
@@ -1,124 +0,0 @@
1
- import {
2
- UsageBudget,
3
- UsageStore
4
- } from "./chunk-WAAD23KY.js";
5
-
6
- // src/plugins/usage/index.ts
7
- import path from "path";
8
- import os from "os";
9
- function createUsagePlugin() {
10
- let store = null;
11
- return {
12
- name: "@openacp/usage",
13
- version: "1.0.0",
14
- description: "Token usage tracking and budget enforcement",
15
- essential: false,
16
- permissions: ["services:register", "commands:register"],
17
- async install(ctx) {
18
- const { settings, legacyConfig, terminal } = ctx;
19
- if (legacyConfig) {
20
- const usageCfg = legacyConfig.usage;
21
- if (usageCfg) {
22
- await settings.setAll({
23
- enabled: usageCfg.enabled ?? true,
24
- warningThreshold: usageCfg.warningThreshold ?? 0.8,
25
- currency: usageCfg.currency ?? "USD",
26
- retentionDays: usageCfg.retentionDays ?? 90
27
- });
28
- terminal.log.success("Usage settings migrated from legacy config");
29
- return;
30
- }
31
- }
32
- await settings.setAll({
33
- enabled: true,
34
- warningThreshold: 0.8,
35
- currency: "USD",
36
- retentionDays: 90
37
- });
38
- terminal.log.success("Usage defaults saved");
39
- },
40
- async configure(ctx) {
41
- const { terminal, settings } = ctx;
42
- const current = await settings.getAll();
43
- const choice = await terminal.select({
44
- message: "What to configure?",
45
- options: [
46
- { value: "threshold", label: `Warning threshold (current: ${current.warningThreshold ?? 0.8})` },
47
- { value: "retention", label: `Retention days (current: ${current.retentionDays ?? 90})` },
48
- { value: "toggle", label: `${current.enabled ? "Disable" : "Enable"} usage tracking` },
49
- { value: "done", label: "Done" }
50
- ]
51
- });
52
- if (choice === "threshold") {
53
- const val = await terminal.text({
54
- message: "Warning threshold (0-1):",
55
- defaultValue: String(current.warningThreshold ?? 0.8),
56
- validate: (v) => {
57
- const n = Number(v.trim());
58
- if (isNaN(n) || n < 0 || n > 1) return "Must be between 0 and 1";
59
- return void 0;
60
- }
61
- });
62
- await settings.set("warningThreshold", Number(val.trim()));
63
- terminal.log.success("Warning threshold updated");
64
- } else if (choice === "retention") {
65
- const val = await terminal.text({
66
- message: "Retention days:",
67
- defaultValue: String(current.retentionDays ?? 90),
68
- validate: (v) => {
69
- const n = Number(v.trim());
70
- if (isNaN(n) || n < 1) return "Must be a positive number";
71
- return void 0;
72
- }
73
- });
74
- await settings.set("retentionDays", Number(val.trim()));
75
- terminal.log.success("Retention days updated");
76
- } else if (choice === "toggle") {
77
- const newState = !current.enabled;
78
- await settings.set("enabled", newState);
79
- terminal.log.success(`Usage tracking ${newState ? "enabled" : "disabled"}`);
80
- }
81
- },
82
- async uninstall(ctx, opts) {
83
- if (opts.purge) {
84
- await ctx.settings.clear();
85
- ctx.terminal.log.success("Usage settings cleared");
86
- }
87
- },
88
- async setup(ctx) {
89
- const config = ctx.pluginConfig;
90
- const usagePath = path.join(os.homedir(), ".openacp", "usage.json");
91
- const retentionDays = config.retentionDays ?? 30;
92
- store = new UsageStore(usagePath, retentionDays);
93
- const budget = new UsageBudget(store, config);
94
- ctx.registerService("usage", { store, budget });
95
- ctx.registerCommand({
96
- name: "usage",
97
- description: "Show usage summary",
98
- category: "plugin",
99
- handler: async () => {
100
- const status = budget.getStatus();
101
- const lines = [
102
- `Usage (this month):`,
103
- ` Spent: $${status.used.toFixed(2)}`,
104
- ` Budget: $${status.budget.toFixed(2)}`,
105
- ` Status: ${status.status} (${status.percent}%)`
106
- ];
107
- return { type: "text", text: lines.join("\n") };
108
- }
109
- });
110
- ctx.log.info("Usage tracking ready");
111
- },
112
- async teardown() {
113
- if (store) {
114
- store.destroy();
115
- }
116
- }
117
- };
118
- }
119
- var usage_default = createUsagePlugin();
120
-
121
- export {
122
- usage_default
123
- };
124
- //# sourceMappingURL=chunk-2CX4IEEC.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/plugins/usage/index.ts"],"sourcesContent":["import type { OpenACPPlugin, InstallContext } from '../../core/plugin/types.js'\nimport type { UsageConfig } from '../../core/config/config.js'\nimport { UsageStore } from './usage-store.js'\nimport { UsageBudget } from './usage-budget.js'\nimport path from 'node:path'\nimport os from 'node:os'\n\nfunction createUsagePlugin(): OpenACPPlugin {\n let store: UsageStore | null = null\n\n return {\n name: '@openacp/usage',\n version: '1.0.0',\n description: 'Token usage tracking and budget enforcement',\n essential: false,\n permissions: ['services:register', 'commands:register'],\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 usageCfg = legacyConfig.usage as Record<string, unknown> | undefined\n if (usageCfg) {\n await settings.setAll({\n enabled: usageCfg.enabled ?? true,\n warningThreshold: usageCfg.warningThreshold ?? 0.8,\n currency: usageCfg.currency ?? 'USD',\n retentionDays: usageCfg.retentionDays ?? 90,\n })\n terminal.log.success('Usage settings migrated from legacy config')\n return\n }\n }\n\n // Save defaults\n await settings.setAll({\n enabled: true,\n warningThreshold: 0.8,\n currency: 'USD',\n retentionDays: 90,\n })\n terminal.log.success('Usage 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: 'threshold', label: `Warning threshold (current: ${current.warningThreshold ?? 0.8})` },\n { value: 'retention', label: `Retention days (current: ${current.retentionDays ?? 90})` },\n { value: 'toggle', label: `${current.enabled ? 'Disable' : 'Enable'} usage tracking` },\n { value: 'done', label: 'Done' },\n ],\n })\n\n if (choice === 'threshold') {\n const val = await terminal.text({\n message: 'Warning threshold (0-1):',\n defaultValue: String(current.warningThreshold ?? 0.8),\n validate: (v) => {\n const n = Number(v.trim())\n if (isNaN(n) || n < 0 || n > 1) return 'Must be between 0 and 1'\n return undefined\n },\n })\n await settings.set('warningThreshold', Number(val.trim()))\n terminal.log.success('Warning threshold updated')\n } else if (choice === 'retention') {\n const val = await terminal.text({\n message: 'Retention days:',\n defaultValue: String(current.retentionDays ?? 90),\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('retentionDays', Number(val.trim()))\n terminal.log.success('Retention days updated')\n } else if (choice === 'toggle') {\n const newState = !current.enabled\n await settings.set('enabled', newState)\n terminal.log.success(`Usage tracking ${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('Usage settings cleared')\n }\n },\n\n async setup(ctx) {\n const config = ctx.pluginConfig as Record<string, unknown>\n const usagePath = path.join(os.homedir(), '.openacp', 'usage.json')\n const retentionDays = (config.retentionDays as number) ?? 30\n store = new UsageStore(usagePath, retentionDays)\n const budget = new UsageBudget(store, config as unknown as UsageConfig)\n\n ctx.registerService('usage', { store, budget })\n\n ctx.registerCommand({\n name: 'usage',\n description: 'Show usage summary',\n category: 'plugin',\n handler: async () => {\n const status = budget.getStatus()\n const lines = [\n `Usage (this month):`,\n ` Spent: $${status.used.toFixed(2)}`,\n ` Budget: $${status.budget.toFixed(2)}`,\n ` Status: ${status.status} (${status.percent}%)`,\n ]\n return { type: 'text', text: lines.join('\\n') }\n },\n })\n\n ctx.log.info('Usage tracking ready')\n },\n\n async teardown() {\n if (store) {\n store.destroy()\n }\n },\n }\n}\n\nexport default createUsagePlugin()\n"],"mappings":"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","names":[]}