@openacp/cli 2026.326.2 → 2026.326.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.
package/dist/cli.js CHANGED
@@ -4,6 +4,10 @@ import {
4
4
  readApiPort,
5
5
  removeStalePortFile
6
6
  } from "./chunk-4WXALZA3.js";
7
+ import {
8
+ PLUGINS_DIR
9
+ } from "./chunk-QVMEF6FB.js";
10
+ import "./chunk-XMMAGAT4.js";
7
11
  import {
8
12
  checkAndPromptUpdate,
9
13
  compareVersions,
@@ -11,10 +15,6 @@ import {
11
15
  getLatestVersion,
12
16
  runUpdate
13
17
  } from "./chunk-S64CB6J3.js";
14
- import {
15
- PLUGINS_DIR
16
- } from "./chunk-QVMEF6FB.js";
17
- import "./chunk-XMMAGAT4.js";
18
18
  import "./chunk-VUNV25KB.js";
19
19
 
20
20
  // src/cli.ts
@@ -302,7 +302,7 @@ async function cmdPlugin(args2 = []) {
302
302
  return;
303
303
  }
304
304
  case "create": {
305
- const { cmdPluginCreate } = await import("./plugin-create-JVCVUG6V.js");
305
+ const { cmdPluginCreate } = await import("./plugin-create-LYF5PP5W.js");
306
306
  await cmdPluginCreate();
307
307
  return;
308
308
  }
@@ -1,10 +1,12 @@
1
+ import {
2
+ getCurrentVersion
3
+ } from "./chunk-S64CB6J3.js";
1
4
  import "./chunk-VUNV25KB.js";
2
5
 
3
6
  // src/cli/commands/plugin-create.ts
4
7
  import * as p from "@clack/prompts";
5
8
  import fs from "fs";
6
9
  import path from "path";
7
- import { createRequire } from "module";
8
10
  async function cmdPluginCreate() {
9
11
  p.intro("Create a new OpenACP plugin");
10
12
  const result = await p.group(
@@ -55,13 +57,7 @@ async function cmdPluginCreate() {
55
57
  const spinner2 = p.spinner();
56
58
  spinner2.start("Scaffolding plugin...");
57
59
  fs.mkdirSync(path.join(targetDir, "src", "__tests__"), { recursive: true });
58
- let cliVersion = "0.6.10";
59
- try {
60
- const require2 = createRequire(import.meta.url);
61
- const rootPkg = require2("../../../package.json");
62
- cliVersion = rootPkg.version ?? cliVersion;
63
- } catch {
64
- }
60
+ const cliVersion = getCurrentVersion();
65
61
  const packageJson = {
66
62
  name: pluginName,
67
63
  version: "0.1.0",
@@ -328,4 +324,4 @@ describe('${pluginName}', () => {
328
324
  export {
329
325
  cmdPluginCreate
330
326
  };
331
- //# sourceMappingURL=plugin-create-JVCVUG6V.js.map
327
+ //# sourceMappingURL=plugin-create-LYF5PP5W.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/commands/plugin-create.ts"],"sourcesContent":["import * as p from '@clack/prompts'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport { getCurrentVersion } from '../version.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 // Detect CLI version for dependency pinning\n const cliVersion = getCurrentVersion()\n\n // package.json\n const packageJson = {\n name: pluginName,\n version: '0.1.0',\n description: result.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: result.author || '',\n license: result.license as string,\n keywords: ['openacp', 'openacp-plugin'],\n peerDependencies: {\n '@openacp/cli': `>=${cliVersion}`,\n },\n devDependencies: {\n '@openacp/plugin-sdk': cliVersion,\n typescript: '^5.4.0',\n vitest: '^3.0.0',\n },\n }\n fs.writeFileSync(\n path.join(targetDir, 'package.json'),\n JSON.stringify(packageJson, null, 2) + '\\n',\n )\n\n // tsconfig.json\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 fs.writeFileSync(\n path.join(targetDir, 'tsconfig.json'),\n JSON.stringify(tsconfig, null, 2) + '\\n',\n )\n\n // .gitignore\n fs.writeFileSync(\n path.join(targetDir, '.gitignore'),\n ['node_modules/', 'dist/', '*.tsbuildinfo', '.DS_Store', ''].join('\\n'),\n )\n\n // .npmignore\n fs.writeFileSync(\n path.join(targetDir, '.npmignore'),\n ['src/', 'tsconfig.json', '.editorconfig', '.gitignore', '*.test.ts', '__tests__/', ''].join('\\n'),\n )\n\n // .editorconfig\n fs.writeFileSync(\n path.join(targetDir, '.editorconfig'),\n [\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\n // README.md\n fs.writeFileSync(\n path.join(targetDir, 'README.md'),\n [\n `# ${pluginName}`,\n '',\n result.description || 'An OpenACP plugin.',\n '',\n '## Installation',\n '',\n '```bash',\n `openacp plugin add ${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 result.license as string,\n '',\n ].join('\\n'),\n )\n\n // src/index.ts — full plugin template with all hooks\n const pluginVarName = dirName.replace(/-([a-z])/g, (_, c) => c.toUpperCase())\n fs.writeFileSync(\n path.join(targetDir, 'src', 'index.ts'),\n `import type { OpenACPPlugin, PluginContext, InstallContext, MigrateContext } from '@openacp/plugin-sdk'\n\nconst plugin: OpenACPPlugin = {\n name: '${pluginName}',\n version: '0.1.0',\n description: '${(result.description || '').replace(/'/g, \"\\\\'\")}',\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 ${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 ${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 ${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 ${pluginName}\\`.\n * Re-run configuration prompts to update settings.\n */\n async configure(ctx: InstallContext): Promise<void> {\n ctx.terminal.log.info('Configuring ${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 ${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 ${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\n // src/__tests__/index.test.ts\n fs.writeFileSync(\n path.join(targetDir, 'src', '__tests__', 'index.test.ts'),\n `import { describe, it, expect } from 'vitest'\nimport { createTestContext } from '@openacp/plugin-sdk/testing'\nimport plugin from '../index.js'\n\ndescribe('${pluginName}', () => {\n it('has correct metadata', () => {\n expect(plugin.name).toBe('${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({ pluginName: '${pluginName}' })\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 = createTestContext({ pluginName: '${pluginName}' })\n await expect(plugin.install(ctx as any)).resolves.not.toThrow()\n }\n })\n})\n`,\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"],"mappings":"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","names":["spinner"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openacp/cli",
3
- "version": "2026.0326.2",
3
+ "version": "2026.0326.3",
4
4
  "description": "Self-hosted bridge for AI coding agents via ACP protocol",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/cli/commands/plugin-create.ts"],"sourcesContent":["import * as p from '@clack/prompts'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport { createRequire } from 'node:module'\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 // Detect CLI version for dependency pinning\n let cliVersion = '0.6.10'\n try {\n const require = createRequire(import.meta.url)\n const rootPkg = require('../../../package.json')\n cliVersion = rootPkg.version ?? cliVersion\n } catch { /* fallback to hardcoded */ }\n\n // package.json\n const packageJson = {\n name: pluginName,\n version: '0.1.0',\n description: result.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: result.author || '',\n license: result.license as string,\n keywords: ['openacp', 'openacp-plugin'],\n peerDependencies: {\n '@openacp/cli': `>=${cliVersion}`,\n },\n devDependencies: {\n '@openacp/plugin-sdk': cliVersion,\n typescript: '^5.4.0',\n vitest: '^3.0.0',\n },\n }\n fs.writeFileSync(\n path.join(targetDir, 'package.json'),\n JSON.stringify(packageJson, null, 2) + '\\n',\n )\n\n // tsconfig.json\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 fs.writeFileSync(\n path.join(targetDir, 'tsconfig.json'),\n JSON.stringify(tsconfig, null, 2) + '\\n',\n )\n\n // .gitignore\n fs.writeFileSync(\n path.join(targetDir, '.gitignore'),\n ['node_modules/', 'dist/', '*.tsbuildinfo', '.DS_Store', ''].join('\\n'),\n )\n\n // .npmignore\n fs.writeFileSync(\n path.join(targetDir, '.npmignore'),\n ['src/', 'tsconfig.json', '.editorconfig', '.gitignore', '*.test.ts', '__tests__/', ''].join('\\n'),\n )\n\n // .editorconfig\n fs.writeFileSync(\n path.join(targetDir, '.editorconfig'),\n [\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\n // README.md\n fs.writeFileSync(\n path.join(targetDir, 'README.md'),\n [\n `# ${pluginName}`,\n '',\n result.description || 'An OpenACP plugin.',\n '',\n '## Installation',\n '',\n '```bash',\n `openacp plugin add ${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 result.license as string,\n '',\n ].join('\\n'),\n )\n\n // src/index.ts — full plugin template with all hooks\n const pluginVarName = dirName.replace(/-([a-z])/g, (_, c) => c.toUpperCase())\n fs.writeFileSync(\n path.join(targetDir, 'src', 'index.ts'),\n `import type { OpenACPPlugin, PluginContext, InstallContext, MigrateContext } from '@openacp/plugin-sdk'\n\nconst plugin: OpenACPPlugin = {\n name: '${pluginName}',\n version: '0.1.0',\n description: '${(result.description || '').replace(/'/g, \"\\\\'\")}',\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 ${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 ${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 ${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 ${pluginName}\\`.\n * Re-run configuration prompts to update settings.\n */\n async configure(ctx: InstallContext): Promise<void> {\n ctx.terminal.log.info('Configuring ${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 ${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 ${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\n // src/__tests__/index.test.ts\n fs.writeFileSync(\n path.join(targetDir, 'src', '__tests__', 'index.test.ts'),\n `import { describe, it, expect } from 'vitest'\nimport { createTestContext } from '@openacp/plugin-sdk/testing'\nimport plugin from '../index.js'\n\ndescribe('${pluginName}', () => {\n it('has correct metadata', () => {\n expect(plugin.name).toBe('${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({ pluginName: '${pluginName}' })\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 = createTestContext({ pluginName: '${pluginName}' })\n await expect(plugin.install(ctx as any)).resolves.not.toThrow()\n }\n })\n})\n`,\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"],"mappings":";;;AAAA,YAAY,OAAO;AACnB,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAE9B,eAAsB,kBAAiC;AACrD,EAAE,QAAM,6BAA6B;AAErC,QAAM,SAAS,MAAQ;AAAA,IACrB;AAAA,MACE,MAAM,MACF,OAAK;AAAA,QACL,SAAS;AAAA,QACT,aAAa;AAAA,QACb,UAAU,CAAC,UAA8B;AACvC,cAAI,CAAC,SAAS,CAAC,MAAM,KAAK,EAAG,QAAO;AACpC,cAAI,CAAC,+BAA+B,KAAK,MAAM,KAAK,CAAC,GAAG;AACtD,mBAAO;AAAA,UACT;AACA,iBAAO;AAAA,QACT;AAAA,MACF,CAAC;AAAA,MACH,aAAa,MACT,OAAK;AAAA,QACL,SAAS;AAAA,QACT,aAAa;AAAA,MACf,CAAC;AAAA,MACH,QAAQ,MACJ,OAAK;AAAA,QACL,SAAS;AAAA,QACT,aAAa;AAAA,MACf,CAAC;AAAA,MACH,SAAS,MACL,SAAO;AAAA,QACP,SAAS;AAAA,QACT,SAAS;AAAA,UACP,EAAE,OAAO,OAAO,OAAO,MAAM;AAAA,UAC7B,EAAE,OAAO,cAAc,OAAO,aAAa;AAAA,UAC3C,EAAE,OAAO,OAAO,OAAO,MAAM;AAAA,UAC7B,EAAE,OAAO,cAAc,OAAO,uBAAuB;AAAA,QACvD;AAAA,MACF,CAAC;AAAA,IACL;AAAA,IACA;AAAA,MACE,UAAU,MAAM;AACd,QAAE,SAAO,4BAA4B;AACrC,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,aAAa,OAAO,KAAK,KAAK;AACpC,QAAM,UAAU,WAAW,QAAQ,aAAa,EAAE;AAClD,QAAM,YAAY,KAAK,QAAQ,QAAQ,IAAI,GAAG,OAAO;AAErD,MAAI,GAAG,WAAW,SAAS,GAAG;AAC5B,IAAE,SAAO,cAAc,OAAO,mBAAmB;AACjD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAMA,WAAY,UAAQ;AAC1B,EAAAA,SAAQ,MAAM,uBAAuB;AAGrC,KAAG,UAAU,KAAK,KAAK,WAAW,OAAO,WAAW,GAAG,EAAE,WAAW,KAAK,CAAC;AAG1E,MAAI,aAAa;AACjB,MAAI;AACF,UAAMC,WAAU,cAAc,YAAY,GAAG;AAC7C,UAAM,UAAUA,SAAQ,uBAAuB;AAC/C,iBAAa,QAAQ,WAAW;AAAA,EAClC,QAAQ;AAAA,EAA8B;AAGtC,QAAM,cAAc;AAAA,IAClB,MAAM;AAAA,IACN,SAAS;AAAA,IACT,aAAa,OAAO,eAAe;AAAA,IACnC,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,IACP,SAAS;AAAA,MACP,OAAO;AAAA,MACP,KAAK;AAAA,MACL,MAAM;AAAA,MACN,gBAAgB;AAAA,IAClB;AAAA,IACA,QAAQ,OAAO,UAAU;AAAA,IACzB,SAAS,OAAO;AAAA,IAChB,UAAU,CAAC,WAAW,gBAAgB;AAAA,IACtC,kBAAkB;AAAA,MAChB,gBAAgB,KAAK,UAAU;AAAA,IACjC;AAAA,IACA,iBAAiB;AAAA,MACf,uBAAuB;AAAA,MACvB,YAAY;AAAA,MACZ,QAAQ;AAAA,IACV;AAAA,EACF;AACA,KAAG;AAAA,IACD,KAAK,KAAK,WAAW,cAAc;AAAA,IACnC,KAAK,UAAU,aAAa,MAAM,CAAC,IAAI;AAAA,EACzC;AAGA,QAAM,WAAW;AAAA,IACf,iBAAiB;AAAA,MACf,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,kBAAkB;AAAA,MAClB,aAAa;AAAA,MACb,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,iBAAiB;AAAA,MACjB,cAAc;AAAA,MACd,kCAAkC;AAAA,IACpC;AAAA,IACA,SAAS,CAAC,KAAK;AAAA,IACf,SAAS,CAAC,gBAAgB,QAAQ,kBAAkB;AAAA,EACtD;AACA,KAAG;AAAA,IACD,KAAK,KAAK,WAAW,eAAe;AAAA,IACpC,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI;AAAA,EACtC;AAGA,KAAG;AAAA,IACD,KAAK,KAAK,WAAW,YAAY;AAAA,IACjC,CAAC,iBAAiB,SAAS,iBAAiB,aAAa,EAAE,EAAE,KAAK,IAAI;AAAA,EACxE;AAGA,KAAG;AAAA,IACD,KAAK,KAAK,WAAW,YAAY;AAAA,IACjC,CAAC,QAAQ,iBAAiB,iBAAiB,cAAc,aAAa,cAAc,EAAE,EAAE,KAAK,IAAI;AAAA,EACnG;AAGA,KAAG;AAAA,IACD,KAAK,KAAK,WAAW,eAAe;AAAA,IACpC;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI;AAAA,EACb;AAGA,KAAG;AAAA,IACD,KAAK,KAAK,WAAW,WAAW;AAAA,IAChC;AAAA,MACE,KAAK,UAAU;AAAA,MACf;AAAA,MACA,OAAO,eAAe;AAAA,MACtB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,sBAAsB,UAAU;AAAA,MAChC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO;AAAA,MACP;AAAA,IACF,EAAE,KAAK,IAAI;AAAA,EACb;AAGA,QAAM,gBAAgB,QAAQ,QAAQ,aAAa,CAAC,GAAG,MAAM,EAAE,YAAY,CAAC;AAC5E,KAAG;AAAA,IACD,KAAK,KAAK,WAAW,OAAO,UAAU;AAAA,IACtC;AAAA;AAAA;AAAA,WAGO,UAAU;AAAA;AAAA,mBAEF,OAAO,eAAe,IAAI,QAAQ,MAAM,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sDAgCX,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kDAiBd,UAAU;AAAA;AAAA;AAAA;AAAA,wCAIpB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wDAaM,UAAU;AAAA;AAAA;AAAA;AAAA,yCAIzB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qDAoBE,UAAU;AAAA;AAAA;AAAA;AAAA,0CAIrB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUlD;AAGA,KAAG;AAAA,IACD,KAAK,KAAK,WAAW,OAAO,aAAa,eAAe;AAAA,IACxD;AAAA;AAAA;AAAA;AAAA,YAIQ,UAAU;AAAA;AAAA,gCAEU,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mDAMS,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qDAYR,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM7D;AAEA,EAAAD,SAAQ,KAAK,oBAAoB;AAEjC,EAAE;AAAA,IACA;AAAA,MACE,MAAM,OAAO;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI;AAAA,IACX;AAAA,EACF;AAEA,EAAE,QAAM,UAAU,UAAU,iBAAiB,OAAO,EAAE;AACxD;","names":["spinner","require"]}