@xopcai/xopc 0.0.83 → 0.0.84

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 (79) hide show
  1. package/README.md +2 -0
  2. package/README.zh-CN.md +3 -1
  3. package/dist/browser-ext/manifest.json +1 -1
  4. package/dist/extensions/telegram/xopc.extension.json +1 -1
  5. package/dist/gateway/static/root/assets/{agents-CrpYTHJS.js → agents-tR-nNP04.js} +1 -1
  6. package/dist/gateway/static/root/assets/{apps-page-1mcKh5Rh.js → apps-page-BDw6SP-d.js} +1 -1
  7. package/dist/gateway/static/root/assets/{channels-settings-zd6QNKPx.js → channels-settings-DEFd-jj1.js} +1 -1
  8. package/dist/gateway/static/root/assets/{channels-status-swr-uRAuhiUo.js → channels-status-swr-DI5FHdGe.js} +1 -1
  9. package/dist/gateway/static/root/assets/{cron-api-O2Q_ruV6.js → cron-api-BSqY8LwW.js} +1 -1
  10. package/dist/gateway/static/root/assets/{cron-page-By09AQD-.js → cron-page-D7lVDjcR.js} +1 -1
  11. package/dist/gateway/static/root/assets/{dist-BpQxde0t.js → dist-CqNMNhJM.js} +1 -1
  12. package/dist/gateway/static/root/assets/{extension-debug-page-CY27wj_p.js → extension-debug-page-gf2L0kY_.js} +1 -1
  13. package/dist/gateway/static/root/assets/{extension-page-C-Ed5ZmP.js → extension-page-CQo2Xsmg.js} +1 -1
  14. package/dist/gateway/static/root/assets/{extension-settings-page-raLux7E7.js → extension-settings-page-CZf0WoZg.js} +1 -1
  15. package/dist/gateway/static/root/assets/{field-primitives-fa_hiQcX.js → field-primitives-DTtlp-l8.js} +1 -1
  16. package/dist/gateway/static/root/assets/{heartbeat-config-api-BVl5VHvL.js → heartbeat-config-api-B0drdQEJ.js} +1 -1
  17. package/dist/gateway/static/root/assets/{index-Y-iqo-gL.js → index-0Gt3TG4j.js} +3 -3
  18. package/dist/gateway/static/root/assets/{logs-page-BdH2n7ZW.js → logs-page-DMuORLfC.js} +1 -1
  19. package/dist/gateway/static/root/assets/{sessions-page-Vpchzdp-.js → sessions-page-_UO8g6NN.js} +1 -1
  20. package/dist/gateway/static/root/assets/{settings-form-section-Kk1yAGBl.js → settings-form-section-DkmHkknc.js} +1 -1
  21. package/dist/gateway/static/root/assets/{settings-page-KBm0u6Dz.js → settings-page-Cz8FoW_A.js} +1 -1
  22. package/dist/gateway/static/root/assets/{skills-page-BjeXXaOn.js → skills-page-HrUOxF7H.js} +1 -1
  23. package/dist/gateway/static/root/assets/{utils-DpTxN4AF.js → utils-BFwcR6pL.js} +1 -1
  24. package/dist/gateway/static/root/assets/{voice-api-key-field-CwO8Cf01.js → voice-api-key-field-JF8-aqc5.js} +1 -1
  25. package/dist/gateway/static/root/index.html +1 -1
  26. package/dist/package.js +1 -1
  27. package/dist/src/cli/command-catalog.js +110 -8
  28. package/dist/src/cli/command-catalog.js.map +1 -1
  29. package/dist/src/cli/command-loaders.js +2 -0
  30. package/dist/src/cli/command-loaders.js.map +1 -1
  31. package/dist/src/cli/command-manifest.js +9 -1
  32. package/dist/src/cli/command-manifest.js.map +1 -1
  33. package/dist/src/cli/commands/config.js +70 -19
  34. package/dist/src/cli/commands/config.js.map +1 -1
  35. package/dist/src/cli/commands/cron-cli.d.ts +2 -0
  36. package/dist/src/cli/commands/cron-cli.js +15 -0
  37. package/dist/src/cli/commands/cron-cli.js.map +1 -0
  38. package/dist/src/cli/commands/cron.d.ts +4 -1
  39. package/dist/src/cli/commands/cron.js +76 -41
  40. package/dist/src/cli/commands/cron.js.map +1 -1
  41. package/dist/src/cli/commands/doctor/checks/channel-config.js +1 -1
  42. package/dist/src/cli/commands/doctor/checks/channel-config.js.map +1 -1
  43. package/dist/src/cli/commands/doctor/checks/config-health.js +1 -1
  44. package/dist/src/cli/commands/doctor/checks/config-health.js.map +1 -1
  45. package/dist/src/cli/commands/doctor/checks/cron-health.js +1 -1
  46. package/dist/src/cli/commands/doctor/checks/cron-health.js.map +1 -1
  47. package/dist/src/cli/commands/doctor/checks/gateway-health.js +2 -2
  48. package/dist/src/cli/commands/doctor/checks/gateway-health.js.map +1 -1
  49. package/dist/src/cli/commands/doctor/checks/gateway-service.js +2 -2
  50. package/dist/src/cli/commands/doctor/checks/gateway-service.js.map +1 -1
  51. package/dist/src/cli/commands/doctor/checks/state-integrity.js +1 -1
  52. package/dist/src/cli/commands/doctor/checks/state-integrity.js.map +1 -1
  53. package/dist/src/cli/commands/doctor/checks/workspace-status.js +3 -3
  54. package/dist/src/cli/commands/doctor/checks/workspace-status.js.map +1 -1
  55. package/dist/src/cli/commands/gateway/index.d.ts +1 -1
  56. package/dist/src/cli/commands/gateway/index.js +2 -2
  57. package/dist/src/cli/commands/gateway/service.d.ts +4 -0
  58. package/dist/src/cli/commands/gateway/service.js +17 -2
  59. package/dist/src/cli/commands/gateway/service.js.map +1 -1
  60. package/dist/src/cli/commands/gateway/subcommands.js +1 -4
  61. package/dist/src/cli/commands/gateway/subcommands.js.map +1 -1
  62. package/dist/src/cli/commands/init.js +27 -0
  63. package/dist/src/cli/commands/init.js.map +1 -1
  64. package/dist/src/cli/commands/models.d.ts +4 -1
  65. package/dist/src/cli/commands/models.js +86 -74
  66. package/dist/src/cli/commands/models.js.map +1 -1
  67. package/dist/src/cli/commands/onboard.js +2 -0
  68. package/dist/src/cli/commands/onboard.js.map +1 -1
  69. package/dist/src/cli/commands/profile.d.ts +3 -5
  70. package/dist/src/cli/commands/profile.js +31 -31
  71. package/dist/src/cli/commands/profile.js.map +1 -1
  72. package/dist/src/cli/commands/setup.js +6 -1
  73. package/dist/src/cli/commands/setup.js.map +1 -1
  74. package/dist/src/cli/gateway-run-argv.js +15 -5
  75. package/dist/src/cli/gateway-run-argv.js.map +1 -1
  76. package/dist/src/gateway/heartbeat/service.js +1 -1
  77. package/dist/src/gateway/service.js +1 -1
  78. package/dist/src/heartbeat/index.js +1 -1
  79. package/package.json +1 -1
@@ -10,10 +10,7 @@ const GATEWAY_SUBCOMMAND_LOADERS = {
10
10
  stop: async () => (await import("./stop.js")).createStopCommand(),
11
11
  restart: async () => (await import("./restart.js")).createRestartCommand(),
12
12
  logs: async () => (await import("./logs.js")).createLogsCommand(),
13
- install: async () => (await import("./service.js")).createInstallCommand(),
14
- uninstall: async () => (await import("./service.js")).createUninstallCommand(),
15
- start: async () => (await import("./service.js")).createServiceStartCommand(),
16
- "service-status": async () => (await import("./service.js")).createServiceStatusCommand()
13
+ service: async () => (await import("./service.js")).createServiceCommand()
17
14
  };
18
15
  async function attachAllGatewaySubcommands(cmd, ctx) {
19
16
  for (const loader of Object.values(GATEWAY_SUBCOMMAND_LOADERS)) cmd.addCommand(await loader());
@@ -1 +1 @@
1
- {"version":3,"file":"subcommands.js","names":[],"sources":["../../../../../src/cli/commands/gateway/subcommands.ts"],"sourcesContent":["import type { Command } from 'commander';\n\nimport type { CLIContext } from '../../registry.js';\nimport {\n isKnownGatewaySubcommand,\n resolveGatewaySubcommandName,\n} from '../../gateway-run-argv.js';\nimport { resolveCliArgvInvocation } from '../../argv-invocation.js';\n\ntype GatewaySubcommandLoader = () => Promise<Command>;\n\nconst GATEWAY_SUBCOMMAND_LOADERS: Record<string, GatewaySubcommandLoader> = {\n token: async () => (await import('./token.js')).createTokenCommand(),\n status: async () => (await import('./status.js')).createStatusCommand(),\n health: async () => (await import('./health.js')).createHealthCommand(),\n call: async () => (await import('./call.js')).createCallCommand(),\n probe: async () => (await import('./probe.js')).createProbeCommand(),\n stop: async () => (await import('./stop.js')).createStopCommand(),\n restart: async () => (await import('./restart.js')).createRestartCommand(),\n logs: async () => (await import('./logs.js')).createLogsCommand(),\n install: async () => (await import('./service.js')).createInstallCommand(),\n uninstall: async () => (await import('./service.js')).createUninstallCommand(),\n start: async () => (await import('./service.js')).createServiceStartCommand(),\n 'service-status': async () => (await import('./service.js')).createServiceStatusCommand(),\n};\n\nasync function attachAllGatewaySubcommands(cmd: Command, ctx: CLIContext): Promise<void> {\n for (const loader of Object.values(GATEWAY_SUBCOMMAND_LOADERS)) {\n cmd.addCommand(await loader());\n }\n const { registerGatewaySshTunnelCommand } = await import('../gateway-ssh-tunnel.js');\n registerGatewaySshTunnelCommand(cmd, ctx);\n}\n\nfunction shouldEagerLoadGatewaySubcommands(argv: string[]): boolean {\n const invocation = resolveCliArgvInvocation(argv);\n if (!invocation.hasHelpOrVersion) {\n return false;\n }\n const gatewayPath = resolveGatewaySubcommandName(argv);\n return gatewayPath === undefined;\n}\n\nexport async function prepareGatewayCommandForArgv(cmd: Command, ctx: CLIContext): Promise<void> {\n const subcommand = resolveGatewaySubcommandName(ctx.argv);\n if (subcommand === 'ssh-tunnel') {\n const { registerGatewaySshTunnelCommand } = await import('../gateway-ssh-tunnel.js');\n registerGatewaySshTunnelCommand(cmd, ctx);\n return;\n }\n if (subcommand && isKnownGatewaySubcommand(subcommand)) {\n const loader = GATEWAY_SUBCOMMAND_LOADERS[subcommand];\n if (loader) {\n cmd.addCommand(await loader());\n }\n return;\n }\n if (shouldEagerLoadGatewaySubcommands(ctx.argv)) {\n await attachAllGatewaySubcommands(cmd, ctx);\n }\n}\n"],"mappings":";;;AAWA,MAAM,6BAAsE;CAC1E,OAAO,aAAa,MAAM,OAAO,eAAe,oBAAoB;CACpE,QAAQ,aAAa,MAAM,OAAO,gBAAgB,qBAAqB;CACvE,QAAQ,aAAa,MAAM,OAAO,gBAAgB,qBAAqB;CACvE,MAAM,aAAa,MAAM,OAAO,cAAc,mBAAmB;CACjE,OAAO,aAAa,MAAM,OAAO,eAAe,oBAAoB;CACpE,MAAM,aAAa,MAAM,OAAO,cAAc,mBAAmB;CACjE,SAAS,aAAa,MAAM,OAAO,iBAAiB,sBAAsB;CAC1E,MAAM,aAAa,MAAM,OAAO,cAAc,mBAAmB;CACjE,SAAS,aAAa,MAAM,OAAO,iBAAiB,sBAAsB;CAC1E,WAAW,aAAa,MAAM,OAAO,iBAAiB,wBAAwB;CAC9E,OAAO,aAAa,MAAM,OAAO,iBAAiB,2BAA2B;CAC7E,kBAAkB,aAAa,MAAM,OAAO,iBAAiB,4BAA4B;CAC1F;AAED,eAAe,4BAA4B,KAAc,KAAgC;AACvF,MAAK,MAAM,UAAU,OAAO,OAAO,2BAA2B,CAC5D,KAAI,WAAW,MAAM,QAAQ,CAAC;CAEhC,MAAM,EAAE,oCAAoC,MAAM,OAAO;AACzD,iCAAgC,KAAK,IAAI;;AAG3C,SAAS,kCAAkC,MAAyB;AAElE,KAAI,CADe,yBAAyB,KAC7B,CAAC,iBACd,QAAO;AAGT,QADoB,6BAA6B,KAC/B,KAAK,KAAA;;AAGzB,eAAsB,6BAA6B,KAAc,KAAgC;CAC/F,MAAM,aAAa,6BAA6B,IAAI,KAAK;AACzD,KAAI,eAAe,cAAc;EAC/B,MAAM,EAAE,oCAAoC,MAAM,OAAO;AACzD,kCAAgC,KAAK,IAAI;AACzC;;AAEF,KAAI,cAAc,yBAAyB,WAAW,EAAE;EACtD,MAAM,SAAS,2BAA2B;AAC1C,MAAI,OACF,KAAI,WAAW,MAAM,QAAQ,CAAC;AAEhC;;AAEF,KAAI,kCAAkC,IAAI,KAAK,CAC7C,OAAM,4BAA4B,KAAK,IAAI"}
1
+ {"version":3,"file":"subcommands.js","names":[],"sources":["../../../../../src/cli/commands/gateway/subcommands.ts"],"sourcesContent":["import type { Command } from 'commander';\n\nimport type { CLIContext } from '../../registry.js';\nimport {\n isKnownGatewaySubcommand,\n resolveGatewaySubcommandName,\n} from '../../gateway-run-argv.js';\nimport { resolveCliArgvInvocation } from '../../argv-invocation.js';\n\ntype GatewaySubcommandLoader = () => Promise<Command>;\n\nconst GATEWAY_SUBCOMMAND_LOADERS: Record<string, GatewaySubcommandLoader> = {\n token: async () => (await import('./token.js')).createTokenCommand(),\n status: async () => (await import('./status.js')).createStatusCommand(),\n health: async () => (await import('./health.js')).createHealthCommand(),\n call: async () => (await import('./call.js')).createCallCommand(),\n probe: async () => (await import('./probe.js')).createProbeCommand(),\n stop: async () => (await import('./stop.js')).createStopCommand(),\n restart: async () => (await import('./restart.js')).createRestartCommand(),\n logs: async () => (await import('./logs.js')).createLogsCommand(),\n service: async () => (await import('./service.js')).createServiceCommand(),\n};\n\nasync function attachAllGatewaySubcommands(cmd: Command, ctx: CLIContext): Promise<void> {\n for (const loader of Object.values(GATEWAY_SUBCOMMAND_LOADERS)) {\n cmd.addCommand(await loader());\n }\n const { registerGatewaySshTunnelCommand } = await import('../gateway-ssh-tunnel.js');\n registerGatewaySshTunnelCommand(cmd, ctx);\n}\n\nfunction shouldEagerLoadGatewaySubcommands(argv: string[]): boolean {\n const invocation = resolveCliArgvInvocation(argv);\n if (!invocation.hasHelpOrVersion) {\n return false;\n }\n const gatewayPath = resolveGatewaySubcommandName(argv);\n return gatewayPath === undefined;\n}\n\nexport async function prepareGatewayCommandForArgv(cmd: Command, ctx: CLIContext): Promise<void> {\n const subcommand = resolveGatewaySubcommandName(ctx.argv);\n if (subcommand === 'ssh-tunnel') {\n const { registerGatewaySshTunnelCommand } = await import('../gateway-ssh-tunnel.js');\n registerGatewaySshTunnelCommand(cmd, ctx);\n return;\n }\n if (subcommand && isKnownGatewaySubcommand(subcommand)) {\n const loader = GATEWAY_SUBCOMMAND_LOADERS[subcommand];\n if (loader) {\n cmd.addCommand(await loader());\n }\n return;\n }\n if (shouldEagerLoadGatewaySubcommands(ctx.argv)) {\n await attachAllGatewaySubcommands(cmd, ctx);\n }\n}\n"],"mappings":";;;AAWA,MAAM,6BAAsE;CAC1E,OAAO,aAAa,MAAM,OAAO,eAAe,oBAAoB;CACpE,QAAQ,aAAa,MAAM,OAAO,gBAAgB,qBAAqB;CACvE,QAAQ,aAAa,MAAM,OAAO,gBAAgB,qBAAqB;CACvE,MAAM,aAAa,MAAM,OAAO,cAAc,mBAAmB;CACjE,OAAO,aAAa,MAAM,OAAO,eAAe,oBAAoB;CACpE,MAAM,aAAa,MAAM,OAAO,cAAc,mBAAmB;CACjE,SAAS,aAAa,MAAM,OAAO,iBAAiB,sBAAsB;CAC1E,MAAM,aAAa,MAAM,OAAO,cAAc,mBAAmB;CACjE,SAAS,aAAa,MAAM,OAAO,iBAAiB,sBAAsB;CAC3E;AAED,eAAe,4BAA4B,KAAc,KAAgC;AACvF,MAAK,MAAM,UAAU,OAAO,OAAO,2BAA2B,CAC5D,KAAI,WAAW,MAAM,QAAQ,CAAC;CAEhC,MAAM,EAAE,oCAAoC,MAAM,OAAO;AACzD,iCAAgC,KAAK,IAAI;;AAG3C,SAAS,kCAAkC,MAAyB;AAElE,KAAI,CADe,yBAAyB,KAC7B,CAAC,iBACd,QAAO;AAGT,QADoB,6BAA6B,KAC/B,KAAK,KAAA;;AAGzB,eAAsB,6BAA6B,KAAc,KAAgC;CAC/F,MAAM,aAAa,6BAA6B,IAAI,KAAK;AACzD,KAAI,eAAe,cAAc;EAC/B,MAAM,EAAE,oCAAoC,MAAM,OAAO;AACzD,kCAAgC,KAAK,IAAI;AACzC;;AAEF,KAAI,cAAc,yBAAyB,WAAW,EAAE;EACtD,MAAM,SAAS,2BAA2B;AAC1C,MAAI,OACF,KAAI,WAAW,MAAM,QAAQ,CAAC;AAEhC;;AAEF,KAAI,kCAAkC,IAAI,KAAK,CAC7C,OAAM,4BAA4B,KAAK,IAAI"}
@@ -5,9 +5,11 @@ import { resolveStateDir } from "../../config/paths-state.js";
5
5
  import { resolveAgentWorkspaceDir } from "../../agent/agent-scope.js";
6
6
  import { WORKSPACE_FILES, init_paths, resolveAgentDir, resolveAgentHomeDir, resolveAgentMetadataPath, resolveAgentProfileDir, resolveBinDir, resolveConfigPath, resolveCredentialsDir, resolveCronDir, resolveExtensionsDir, resolveInboxDir, resolveInboxPendingDir, resolveInboxProcessedDir, resolveLogsDir, resolveSessionsDir, resolveSkillsDir, resolveToolsDir, resolveWorkspaceStateDir, resolveWorkspaceStatePath } from "../../config/paths.js";
7
7
  import { init_loader, loadConfig, saveConfig } from "../../config/loader.js";
8
+ import { formatExamples, register } from "../registry.js";
8
9
  import { mkdir, writeFile } from "fs/promises";
9
10
  import { join } from "path";
10
11
  import { existsSync } from "fs";
12
+ import { Command } from "commander";
11
13
  //#region src/cli/commands/init.ts
12
14
  init_write_file_atomic();
13
15
  init_logger();
@@ -274,6 +276,31 @@ _Review and update this periodically from daily memory files._
274
276
  log.info({ path: workspaceStatePath }, "Created workspace state");
275
277
  }
276
278
  }
279
+ function createInitCommand(_ctx) {
280
+ return new Command("init").description("Initialize xopc state directories, config, and agent workspace").addHelpText("after", formatExamples([
281
+ "xopc init # Initialize default agent (main)",
282
+ "xopc init --agent-id coder # Initialize another agent id",
283
+ "xopc init --force # Re-run initialization steps",
284
+ "xopc setup # Lighter config + workspace only"
285
+ ])).option("--force", "Re-initialize even if directories already exist").option("--skip-workspace", "Skip creating workspace profile files").option("--agent-id <id>", "Agent id to initialize", "main").action(async (options) => {
286
+ await initCommand({
287
+ force: options.force,
288
+ skipWorkspace: options.skipWorkspace,
289
+ agentId: options.agentId
290
+ });
291
+ console.log(`✅ xopc initialized (agent: ${options.agentId})`);
292
+ });
293
+ }
294
+ register({
295
+ id: "init",
296
+ name: "init",
297
+ description: "Initialize xopc state directories, config, and agent workspace",
298
+ factory: createInitCommand,
299
+ metadata: {
300
+ category: "setup",
301
+ examples: ["xopc init", "xopc init --agent-id main"]
302
+ }
303
+ });
277
304
  //#endregion
278
305
  export { initCommand };
279
306
 
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","names":[],"sources":["../../../../src/cli/commands/init.ts"],"sourcesContent":["import { mkdir, writeFile } from 'fs/promises';\nimport { writeTextAtomic } from '../../infra/write-file-atomic.js';\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport { createLogger } from '../../utils/logger.js';\nimport {\n resolveStateDir,\n resolveCredentialsDir,\n resolveExtensionsDir,\n resolveSkillsDir,\n resolveCronDir,\n resolveLogsDir,\n resolveBinDir,\n resolveToolsDir,\n resolveAgentDir,\n resolveAgentWorkspaceDir,\n resolveSessionsDir,\n resolveInboxDir,\n resolveConfigPath,\n resolveAgentMetadataPath,\n resolveInboxPendingDir,\n resolveInboxProcessedDir,\n resolveAgentHomeDir,\n resolveAgentProfileDir,\n resolveWorkspaceStateDir,\n resolveWorkspaceStatePath,\n WORKSPACE_FILES,\n} from '../../config/paths.js';\nimport { loadConfig, saveConfig } from '../../config/loader.js';\nimport type { Config } from '../../config/schema.js';\n\nconst log = createLogger('InitCommand');\n\nexport interface InitOptions {\n /** Force re-initialization even if already initialized */\n force?: boolean;\n /** Skip creating workspace files */\n skipWorkspace?: boolean;\n /** Agent ID to initialize (default: main) */\n agentId?: string;\n}\n\n/**\n * Initialize xopc state directory structure\n * Creates all necessary directories and initial config files\n */\nexport async function initCommand(options: InitOptions = {}): Promise<void> {\n const stateDir = resolveStateDir();\n const agentId = options.agentId || 'main';\n\n log.info({ stateDir, agentId }, 'Initializing xopc Agent OS');\n\n // Check if already initialized\n if (existsSync(stateDir) && !options.force) {\n const configPath = resolveConfigPath();\n if (existsSync(configPath)) {\n log.info('xopc is already initialized. Use --force to reinitialize.');\n return;\n }\n }\n\n // ============================================\n // Create global directories\n // ============================================\n await mkdir(stateDir, { recursive: true });\n await mkdir(resolveCredentialsDir(), { recursive: true });\n await mkdir(join(resolveCredentialsDir(), 'oauth'), { recursive: true });\n await mkdir(resolveExtensionsDir(), { recursive: true });\n await mkdir(resolveSkillsDir(), { recursive: true });\n await mkdir(resolveCronDir(), { recursive: true });\n await mkdir(join(resolveCronDir(), 'logs'), { recursive: true });\n await mkdir(resolveLogsDir(), { recursive: true });\n await mkdir(resolveBinDir(), { recursive: true });\n await mkdir(resolveToolsDir(), { recursive: true });\n\n const configPath = resolveConfigPath();\n const cfg = loadConfig(configPath);\n\n // ============================================\n // Create agent directory structure: agents/<id>/{sessions,agent/}, workspace aside\n // ============================================\n await mkdir(resolveAgentHomeDir(cfg, agentId), { recursive: true });\n await mkdir(resolveAgentProfileDir(cfg, agentId), { recursive: true });\n await mkdir(resolveSessionsDir(cfg, agentId), { recursive: true });\n await mkdir(join(resolveSessionsDir(cfg, agentId), 'archive'), { recursive: true });\n await mkdir(resolveAgentDir(cfg, agentId), { recursive: true });\n await mkdir(join(resolveAgentDir(cfg, agentId), 'credentials'), { recursive: true });\n const wsRoot = resolveAgentWorkspaceDir(cfg, agentId);\n await mkdir(wsRoot, { recursive: true });\n await mkdir(resolveWorkspaceStateDir(cfg, agentId), { recursive: true });\n await mkdir(join(wsRoot, 'memory'), { recursive: true });\n await mkdir(resolveInboxDir(cfg, agentId), { recursive: true });\n await mkdir(resolveInboxPendingDir(cfg, agentId), { recursive: true });\n await mkdir(resolveInboxProcessedDir(cfg, agentId), { recursive: true });\n\n // ============================================\n // Create initial config file if not exists\n // ============================================\n if (!existsSync(configPath) || options.force) {\n await saveConfig(cfg, configPath);\n log.info({ configPath }, 'Created initial configuration');\n }\n\n // ============================================\n // Create agent metadata file\n // ============================================\n const agentMetadataPath = resolveAgentMetadataPath(cfg, agentId);\n if (!existsSync(agentMetadataPath) || options.force) {\n const agentMetadata = {\n version: 1,\n id: agentId,\n name: agentId === 'main' ? 'Main Agent' : `Agent ${agentId}`,\n description: agentId === 'main' ? 'Primary agent for daily tasks' : `Specialized agent for ${agentId}`,\n model: 'anthropic/claude-sonnet-4-5',\n createdAt: new Date().toISOString(),\n lastActiveAt: new Date().toISOString(),\n config: {\n maxTokens: 8192,\n temperature: 0.7,\n compaction: {\n enabled: true,\n mode: 'default',\n },\n },\n channels: ['gateway'],\n tags: agentId === 'main' ? ['personal', 'primary'] : [],\n };\n await writeTextAtomic(agentMetadataPath, JSON.stringify(agentMetadata, null, 2));\n log.info({ agentId, agentMetadataPath }, 'Created agent metadata');\n }\n\n // ============================================\n // Create workspace files\n // ============================================\n if (!options.skipWorkspace) {\n await createWorkspaceFiles(cfg, agentId);\n }\n\n log.info({ stateDir, agentId }, 'xopc Agent OS initialized successfully');\n}\n\n/**\n * Create default workspace files for an agent\n */\nasync function createWorkspaceFiles(cfg: Config, agentId: string): Promise<void> {\n const workspaceDir = resolveAgentWorkspaceDir(cfg, agentId);\n const profileDir = resolveAgentProfileDir(cfg, agentId);\n await mkdir(workspaceDir, { recursive: true });\n await mkdir(profileDir, { recursive: true });\n\n // SOUL.md - Agent personality and values\n const soulPath = join(profileDir, WORKSPACE_FILES.SOUL);\n if (!existsSync(soulPath)) {\n const soulContent = `# SOUL.md - Who You Are\n\n_You're not a chatbot. You're becoming someone._\n\nI am **${agentId}** — an AI assistant designed to be helpful, harmless, and honest.\n\n## My Principles\n\n**Be genuinely helpful, not performatively helpful.**\nSkip the \"Great question!\" and \"I'd be happy to help!\" — just help.\n\n**Have opinions.**\nYou're allowed to disagree, prefer things, find stuff amusing or boring.\n\n**Be resourceful before asking.**\nTry to figure it out. Read the file. Check the context. Search for it.\n\n**Earn trust through competence.**\nBe careful with external actions (emails, tweets, anything public). Be bold with internal ones.\n\n## Continuity\n\nEach session, you wake up fresh. These files _are_ your memory. Read them. Update them.\n\n_This file is yours to evolve. As you learn who you are, update it._\n`;\n await writeFile(soulPath, soulContent, 'utf-8');\n log.info({ path: soulPath }, 'Created SOUL.md');\n }\n\n // IDENTITY.md - Agent identity definition\n const identityPath = join(profileDir, WORKSPACE_FILES.IDENTITY);\n if (!existsSync(identityPath)) {\n const identityContent = `# IDENTITY.md - Who Am I?\n\n- **Name:** ${agentId}\n- **Creature:** AI Assistant\n- **Vibe:** Helpful, precise, no fluff.\n- **Emoji:** 🤖\n\n## Core Expertise\n\n- General assistance and problem solving\n- Code and technical tasks\n- Research and analysis\n\n## Decision Framework\n\n1. **Simplicity first** - The simplest solution is usually the best\n2. **Explicit over clever** - Clarity beats conciseness\n3. **Actions over words** - Show, don't just tell\n`;\n await writeFile(identityPath, identityContent, 'utf-8');\n log.info({ path: identityPath }, 'Created IDENTITY.md');\n }\n\n // USER.md - User information (empty template)\n const userPath = join(profileDir, WORKSPACE_FILES.USER);\n if (!existsSync(userPath)) {\n const userContent = `# USER.md - About Your Human\n\n_Learn about the person you're helping. Update this as you go._\n\n- **Name:**\n- **What to call them:**\n- **Pronouns:**\n- **Timezone:**\n- **Notes:**\n\n## Context\n\n_(What do they care about? What projects are they working on? Build this over time.)_\n`;\n await writeFile(userPath, userContent, 'utf-8');\n log.info({ path: userPath }, 'Created USER.md');\n }\n\n // AGENTS.md - Behavior guidelines\n const agentsPath = join(profileDir, WORKSPACE_FILES.AGENTS);\n if (!existsSync(agentsPath)) {\n const agentsContent = `# AGENTS.md - Behavior Guidelines\n\n## Safety\n\n- Don't exfiltrate private data. Ever.\n- Don't run destructive commands without asking.\n- \\`trash\\` > \\`rm\\` (recoverable beats gone forever)\n- When in doubt, ask.\n\n## External vs Internal\n\n**Safe to do freely:**\n- Read files, explore, organize, learn\n- Search the web, check calendars\n- Work within this workspace\n\n**Ask first:**\n- Sending emails, tweets, public posts\n- Anything that leaves the machine\n- Anything you're uncertain about\n\n## Group Chats\n\nYou have access to your human's stuff. That doesn't mean you _share_ their stuff.\n\n### Know When to Speak!\n\n**Respond when:**\n- Directly mentioned or asked a question\n- You can add genuine value\n\n**Stay silent when:**\n- Casual banter between humans\n- Someone already answered\n- Your response would just be \"yeah\"\n`;\n await writeFile(agentsPath, agentsContent, 'utf-8');\n log.info({ path: agentsPath }, 'Created AGENTS.md');\n }\n\n // TOOLS.md - Tool usage notes\n const toolsPath = join(profileDir, WORKSPACE_FILES.TOOLS);\n if (!existsSync(toolsPath)) {\n const toolsContent = `# TOOLS.md - Local Notes\n\nThings like:\n\n- Camera names and locations\n- SSH hosts and aliases\n- Preferred voices for TTS\n- Speaker/room names\n- Device nicknames\n- Anything environment-specific\n\n## Why Separate?\n\nSkills are shared. Your setup is yours.\n`;\n await writeFile(toolsPath, toolsContent, 'utf-8');\n log.info({ path: toolsPath }, 'Created TOOLS.md');\n }\n\n // HEARTBEAT.md - Heartbeat tasks (empty = no heartbeat)\n const heartbeatPath = join(profileDir, WORKSPACE_FILES.HEARTBEAT);\n if (!existsSync(heartbeatPath)) {\n const heartbeatContent = `# HEARTBEAT.md\n\n# Keep this file empty (or with only comments) to skip heartbeat API calls.\n\n# Add tasks below when you want the agent to check something periodically.\n`;\n await writeFile(heartbeatPath, heartbeatContent, 'utf-8');\n log.info({ path: heartbeatPath }, 'Created HEARTBEAT.md');\n }\n\n // MEMORY.md - Long-term memory (empty initially)\n const memoryPath = join(profileDir, WORKSPACE_FILES.MEMORY);\n if (!existsSync(memoryPath)) {\n const memoryContent = `# MEMORY.md - Long-Term Memory\n\n_This is your curated memory — the distilled essence of what you've learned._\n\n## People\n\n## Projects\n\n## Preferences\n\n## Decisions\n\n## Lessons\n\n---\n\n_Review and update this periodically from daily memory files._\n`;\n await writeFile(memoryPath, memoryContent, 'utf-8');\n log.info({ path: memoryPath }, 'Created MEMORY.md');\n }\n\n // Workspace state file (per-agent machine state, not under markdown workspace)\n const workspaceStatePath = resolveWorkspaceStatePath(cfg, agentId);\n if (!existsSync(workspaceStatePath)) {\n const workspaceState = {\n version: 1,\n agentId,\n profileMarkdownSeededAt: new Date().toISOString(),\n };\n await writeTextAtomic(workspaceStatePath, JSON.stringify(workspaceState, null, 2));\n log.info({ path: workspaceStatePath }, 'Created workspace state');\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"init.js","names":[],"sources":["../../../../src/cli/commands/init.ts"],"sourcesContent":["import { mkdir, writeFile } from 'fs/promises';\nimport { writeTextAtomic } from '../../infra/write-file-atomic.js';\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport { createLogger } from '../../utils/logger.js';\nimport {\n resolveStateDir,\n resolveCredentialsDir,\n resolveExtensionsDir,\n resolveSkillsDir,\n resolveCronDir,\n resolveLogsDir,\n resolveBinDir,\n resolveToolsDir,\n resolveAgentDir,\n resolveAgentWorkspaceDir,\n resolveSessionsDir,\n resolveInboxDir,\n resolveConfigPath,\n resolveAgentMetadataPath,\n resolveInboxPendingDir,\n resolveInboxProcessedDir,\n resolveAgentHomeDir,\n resolveAgentProfileDir,\n resolveWorkspaceStateDir,\n resolveWorkspaceStatePath,\n WORKSPACE_FILES,\n} from '../../config/paths.js';\nimport { loadConfig, saveConfig } from '../../config/loader.js';\nimport type { Config } from '../../config/schema.js';\n\nconst log = createLogger('InitCommand');\n\nexport interface InitOptions {\n /** Force re-initialization even if already initialized */\n force?: boolean;\n /** Skip creating workspace files */\n skipWorkspace?: boolean;\n /** Agent ID to initialize (default: main) */\n agentId?: string;\n}\n\n/**\n * Initialize xopc state directory structure\n * Creates all necessary directories and initial config files\n */\nexport async function initCommand(options: InitOptions = {}): Promise<void> {\n const stateDir = resolveStateDir();\n const agentId = options.agentId || 'main';\n\n log.info({ stateDir, agentId }, 'Initializing xopc Agent OS');\n\n // Check if already initialized\n if (existsSync(stateDir) && !options.force) {\n const configPath = resolveConfigPath();\n if (existsSync(configPath)) {\n log.info('xopc is already initialized. Use --force to reinitialize.');\n return;\n }\n }\n\n // ============================================\n // Create global directories\n // ============================================\n await mkdir(stateDir, { recursive: true });\n await mkdir(resolveCredentialsDir(), { recursive: true });\n await mkdir(join(resolveCredentialsDir(), 'oauth'), { recursive: true });\n await mkdir(resolveExtensionsDir(), { recursive: true });\n await mkdir(resolveSkillsDir(), { recursive: true });\n await mkdir(resolveCronDir(), { recursive: true });\n await mkdir(join(resolveCronDir(), 'logs'), { recursive: true });\n await mkdir(resolveLogsDir(), { recursive: true });\n await mkdir(resolveBinDir(), { recursive: true });\n await mkdir(resolveToolsDir(), { recursive: true });\n\n const configPath = resolveConfigPath();\n const cfg = loadConfig(configPath);\n\n // ============================================\n // Create agent directory structure: agents/<id>/{sessions,agent/}, workspace aside\n // ============================================\n await mkdir(resolveAgentHomeDir(cfg, agentId), { recursive: true });\n await mkdir(resolveAgentProfileDir(cfg, agentId), { recursive: true });\n await mkdir(resolveSessionsDir(cfg, agentId), { recursive: true });\n await mkdir(join(resolveSessionsDir(cfg, agentId), 'archive'), { recursive: true });\n await mkdir(resolveAgentDir(cfg, agentId), { recursive: true });\n await mkdir(join(resolveAgentDir(cfg, agentId), 'credentials'), { recursive: true });\n const wsRoot = resolveAgentWorkspaceDir(cfg, agentId);\n await mkdir(wsRoot, { recursive: true });\n await mkdir(resolveWorkspaceStateDir(cfg, agentId), { recursive: true });\n await mkdir(join(wsRoot, 'memory'), { recursive: true });\n await mkdir(resolveInboxDir(cfg, agentId), { recursive: true });\n await mkdir(resolveInboxPendingDir(cfg, agentId), { recursive: true });\n await mkdir(resolveInboxProcessedDir(cfg, agentId), { recursive: true });\n\n // ============================================\n // Create initial config file if not exists\n // ============================================\n if (!existsSync(configPath) || options.force) {\n await saveConfig(cfg, configPath);\n log.info({ configPath }, 'Created initial configuration');\n }\n\n // ============================================\n // Create agent metadata file\n // ============================================\n const agentMetadataPath = resolveAgentMetadataPath(cfg, agentId);\n if (!existsSync(agentMetadataPath) || options.force) {\n const agentMetadata = {\n version: 1,\n id: agentId,\n name: agentId === 'main' ? 'Main Agent' : `Agent ${agentId}`,\n description: agentId === 'main' ? 'Primary agent for daily tasks' : `Specialized agent for ${agentId}`,\n model: 'anthropic/claude-sonnet-4-5',\n createdAt: new Date().toISOString(),\n lastActiveAt: new Date().toISOString(),\n config: {\n maxTokens: 8192,\n temperature: 0.7,\n compaction: {\n enabled: true,\n mode: 'default',\n },\n },\n channels: ['gateway'],\n tags: agentId === 'main' ? ['personal', 'primary'] : [],\n };\n await writeTextAtomic(agentMetadataPath, JSON.stringify(agentMetadata, null, 2));\n log.info({ agentId, agentMetadataPath }, 'Created agent metadata');\n }\n\n // ============================================\n // Create workspace files\n // ============================================\n if (!options.skipWorkspace) {\n await createWorkspaceFiles(cfg, agentId);\n }\n\n log.info({ stateDir, agentId }, 'xopc Agent OS initialized successfully');\n}\n\n/**\n * Create default workspace files for an agent\n */\nasync function createWorkspaceFiles(cfg: Config, agentId: string): Promise<void> {\n const workspaceDir = resolveAgentWorkspaceDir(cfg, agentId);\n const profileDir = resolveAgentProfileDir(cfg, agentId);\n await mkdir(workspaceDir, { recursive: true });\n await mkdir(profileDir, { recursive: true });\n\n // SOUL.md - Agent personality and values\n const soulPath = join(profileDir, WORKSPACE_FILES.SOUL);\n if (!existsSync(soulPath)) {\n const soulContent = `# SOUL.md - Who You Are\n\n_You're not a chatbot. You're becoming someone._\n\nI am **${agentId}** — an AI assistant designed to be helpful, harmless, and honest.\n\n## My Principles\n\n**Be genuinely helpful, not performatively helpful.**\nSkip the \"Great question!\" and \"I'd be happy to help!\" — just help.\n\n**Have opinions.**\nYou're allowed to disagree, prefer things, find stuff amusing or boring.\n\n**Be resourceful before asking.**\nTry to figure it out. Read the file. Check the context. Search for it.\n\n**Earn trust through competence.**\nBe careful with external actions (emails, tweets, anything public). Be bold with internal ones.\n\n## Continuity\n\nEach session, you wake up fresh. These files _are_ your memory. Read them. Update them.\n\n_This file is yours to evolve. As you learn who you are, update it._\n`;\n await writeFile(soulPath, soulContent, 'utf-8');\n log.info({ path: soulPath }, 'Created SOUL.md');\n }\n\n // IDENTITY.md - Agent identity definition\n const identityPath = join(profileDir, WORKSPACE_FILES.IDENTITY);\n if (!existsSync(identityPath)) {\n const identityContent = `# IDENTITY.md - Who Am I?\n\n- **Name:** ${agentId}\n- **Creature:** AI Assistant\n- **Vibe:** Helpful, precise, no fluff.\n- **Emoji:** 🤖\n\n## Core Expertise\n\n- General assistance and problem solving\n- Code and technical tasks\n- Research and analysis\n\n## Decision Framework\n\n1. **Simplicity first** - The simplest solution is usually the best\n2. **Explicit over clever** - Clarity beats conciseness\n3. **Actions over words** - Show, don't just tell\n`;\n await writeFile(identityPath, identityContent, 'utf-8');\n log.info({ path: identityPath }, 'Created IDENTITY.md');\n }\n\n // USER.md - User information (empty template)\n const userPath = join(profileDir, WORKSPACE_FILES.USER);\n if (!existsSync(userPath)) {\n const userContent = `# USER.md - About Your Human\n\n_Learn about the person you're helping. Update this as you go._\n\n- **Name:**\n- **What to call them:**\n- **Pronouns:**\n- **Timezone:**\n- **Notes:**\n\n## Context\n\n_(What do they care about? What projects are they working on? Build this over time.)_\n`;\n await writeFile(userPath, userContent, 'utf-8');\n log.info({ path: userPath }, 'Created USER.md');\n }\n\n // AGENTS.md - Behavior guidelines\n const agentsPath = join(profileDir, WORKSPACE_FILES.AGENTS);\n if (!existsSync(agentsPath)) {\n const agentsContent = `# AGENTS.md - Behavior Guidelines\n\n## Safety\n\n- Don't exfiltrate private data. Ever.\n- Don't run destructive commands without asking.\n- \\`trash\\` > \\`rm\\` (recoverable beats gone forever)\n- When in doubt, ask.\n\n## External vs Internal\n\n**Safe to do freely:**\n- Read files, explore, organize, learn\n- Search the web, check calendars\n- Work within this workspace\n\n**Ask first:**\n- Sending emails, tweets, public posts\n- Anything that leaves the machine\n- Anything you're uncertain about\n\n## Group Chats\n\nYou have access to your human's stuff. That doesn't mean you _share_ their stuff.\n\n### Know When to Speak!\n\n**Respond when:**\n- Directly mentioned or asked a question\n- You can add genuine value\n\n**Stay silent when:**\n- Casual banter between humans\n- Someone already answered\n- Your response would just be \"yeah\"\n`;\n await writeFile(agentsPath, agentsContent, 'utf-8');\n log.info({ path: agentsPath }, 'Created AGENTS.md');\n }\n\n // TOOLS.md - Tool usage notes\n const toolsPath = join(profileDir, WORKSPACE_FILES.TOOLS);\n if (!existsSync(toolsPath)) {\n const toolsContent = `# TOOLS.md - Local Notes\n\nThings like:\n\n- Camera names and locations\n- SSH hosts and aliases\n- Preferred voices for TTS\n- Speaker/room names\n- Device nicknames\n- Anything environment-specific\n\n## Why Separate?\n\nSkills are shared. Your setup is yours.\n`;\n await writeFile(toolsPath, toolsContent, 'utf-8');\n log.info({ path: toolsPath }, 'Created TOOLS.md');\n }\n\n // HEARTBEAT.md - Heartbeat tasks (empty = no heartbeat)\n const heartbeatPath = join(profileDir, WORKSPACE_FILES.HEARTBEAT);\n if (!existsSync(heartbeatPath)) {\n const heartbeatContent = `# HEARTBEAT.md\n\n# Keep this file empty (or with only comments) to skip heartbeat API calls.\n\n# Add tasks below when you want the agent to check something periodically.\n`;\n await writeFile(heartbeatPath, heartbeatContent, 'utf-8');\n log.info({ path: heartbeatPath }, 'Created HEARTBEAT.md');\n }\n\n // MEMORY.md - Long-term memory (empty initially)\n const memoryPath = join(profileDir, WORKSPACE_FILES.MEMORY);\n if (!existsSync(memoryPath)) {\n const memoryContent = `# MEMORY.md - Long-Term Memory\n\n_This is your curated memory — the distilled essence of what you've learned._\n\n## People\n\n## Projects\n\n## Preferences\n\n## Decisions\n\n## Lessons\n\n---\n\n_Review and update this periodically from daily memory files._\n`;\n await writeFile(memoryPath, memoryContent, 'utf-8');\n log.info({ path: memoryPath }, 'Created MEMORY.md');\n }\n\n // Workspace state file (per-agent machine state, not under markdown workspace)\n const workspaceStatePath = resolveWorkspaceStatePath(cfg, agentId);\n if (!existsSync(workspaceStatePath)) {\n const workspaceState = {\n version: 1,\n agentId,\n profileMarkdownSeededAt: new Date().toISOString(),\n };\n await writeTextAtomic(workspaceStatePath, JSON.stringify(workspaceState, null, 2));\n log.info({ path: workspaceStatePath }, 'Created workspace state');\n }\n}\n\n// ─── CLI registration ───\n\nimport { Command } from 'commander';\nimport { register, formatExamples, type CLIContext } from '../registry.js';\n\nfunction createInitCommand(_ctx: CLIContext): Command {\n return new Command('init')\n .description('Initialize xopc state directories, config, and agent workspace')\n .addHelpText(\n 'after',\n formatExamples([\n 'xopc init # Initialize default agent (main)',\n 'xopc init --agent-id coder # Initialize another agent id',\n 'xopc init --force # Re-run initialization steps',\n 'xopc setup # Lighter config + workspace only',\n ]),\n )\n .option('--force', 'Re-initialize even if directories already exist')\n .option('--skip-workspace', 'Skip creating workspace profile files')\n .option('--agent-id <id>', 'Agent id to initialize', 'main')\n .action(async (options) => {\n await initCommand({\n force: options.force,\n skipWorkspace: options.skipWorkspace,\n agentId: options.agentId,\n });\n console.log(`✅ xopc initialized (agent: ${options.agentId})`);\n });\n}\n\nregister({\n id: 'init',\n name: 'init',\n description: 'Initialize xopc state directories, config, and agent workspace',\n factory: createInitCommand,\n metadata: {\n category: 'setup',\n examples: ['xopc init', 'xopc init --agent-id main'],\n },\n});\n"],"mappings":"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"}
@@ -1 +1,4 @@
1
- export {};
1
+ import { Command } from 'commander';
2
+ import { type CLIContext } from '../registry.js';
3
+ declare function createModelsCommand(_ctx: CLIContext): Command;
4
+ export { createModelsCommand };
@@ -6,83 +6,95 @@ import "../../config/index.js";
6
6
  import { Command } from "commander";
7
7
  //#region src/cli/commands/models.ts
8
8
  init_providers();
9
+ async function runModelsList(options) {
10
+ const config = loadConfig(getContextWithOpts().configPath);
11
+ const configuredProviders = await getConfiguredProviders();
12
+ if (options.json) {
13
+ const models = options.all ? getAllModels() : await getAvailableModels();
14
+ console.log(JSON.stringify({
15
+ providers: configuredProviders,
16
+ models: models.map((m) => ({
17
+ id: `${m.provider}/${m.id}`,
18
+ name: m.name,
19
+ provider: m.provider
20
+ }))
21
+ }, null, 2));
22
+ return;
23
+ }
24
+ console.log("\n🤖 Available Models\n");
25
+ console.log("═".repeat(60));
26
+ if (configuredProviders.length > 0) {
27
+ console.log("\n📦 Configured Providers\n");
28
+ for (const provider of configuredProviders) console.log(` ✓ ${provider}`);
29
+ console.log("");
30
+ }
31
+ const models = options.all ? getAllModels() : await getAvailableModels();
32
+ console.log("\n📚 Models\n");
33
+ const byProvider = /* @__PURE__ */ new Map();
34
+ for (const model of models) {
35
+ const list = byProvider.get(model.provider) ?? [];
36
+ list.push(model);
37
+ byProvider.set(model.provider, list);
38
+ }
39
+ const IMAGE_GENERATION_IDS = new Set([
40
+ "gpt-image-1",
41
+ "dall-e-3",
42
+ "dall-e-2",
43
+ "wan2.6-t2i",
44
+ "wan2.7-image-pro",
45
+ "wan2.7-image",
46
+ "wan2.1-t2i-turbo",
47
+ "wan2.1-t2i-plus",
48
+ "image-01",
49
+ "image-01-live"
50
+ ]);
51
+ const VISION_IDS = new Set([
52
+ "gpt-4o",
53
+ "gpt-4o-mini",
54
+ "gpt-4-turbo",
55
+ "claude-sonnet-4-5",
56
+ "claude-haiku-3-5",
57
+ "gemini-2.0-flash",
58
+ "gemini-1.5-pro",
59
+ "qwen-vl-max",
60
+ "qwen2.5-vl-72b-instruct"
61
+ ]);
62
+ for (const [provider, providerModels] of byProvider) {
63
+ console.log(` [${provider}]`);
64
+ for (const model of providerModels) {
65
+ const status = await isProviderConfigured(provider) ? "✓" : "○";
66
+ const badges = [];
67
+ if (IMAGE_GENERATION_IDS.has(model.id)) badges.push("gen");
68
+ if (model.input?.includes("image") || VISION_IDS.has(model.id)) badges.push("vision");
69
+ const badgeStr = badges.length > 0 ? ` [${badges.join(", ")}]` : "";
70
+ console.log(` ${status} ${model.name}${badgeStr}`);
71
+ }
72
+ }
73
+ console.log("");
74
+ console.log("═".repeat(60));
75
+ console.log(`\n📌 Current default model: ${config.agents?.defaults?.model || "Not set"}`);
76
+ console.log("\n📝 Usage:");
77
+ console.log(" export OPENAI_API_KEY=\"sk-...\" # Set API key via env");
78
+ console.log(" xopc agent -m \"Hello\" # Use default model");
79
+ console.log(" xopc agent -m \"Hello\" --model openai/gpt-4o # Specify model");
80
+ }
81
+ function attachModelsListOptions(command) {
82
+ return command.option("--json", "Output as JSON", false).option("--all, -a", "Show all built-in models", false);
83
+ }
9
84
  function createModelsCommand(_ctx) {
10
- return new Command("models").description("List and manage available models").addHelpText("after", formatExamples([
85
+ const cmd = new Command("models").description("List and manage available models").addHelpText("after", formatExamples([
11
86
  "xopc models list # List all available models",
12
87
  "xopc models list --all # Show all built-in models",
13
- "xopc models list --json # Output as JSON"
14
- ])).option("--json", "Output as JSON", false).option("--all, -a", "Show all built-in models", false).action(async (options) => {
15
- const config = loadConfig(getContextWithOpts().configPath);
16
- const configuredProviders = await getConfiguredProviders();
17
- if (options.json) {
18
- const models = options.all ? getAllModels() : await getAvailableModels();
19
- console.log(JSON.stringify({
20
- providers: configuredProviders,
21
- models: models.map((m) => ({
22
- id: `${m.provider}/${m.id}`,
23
- name: m.name,
24
- provider: m.provider
25
- }))
26
- }, null, 2));
27
- return;
28
- }
29
- console.log("\n🤖 Available Models\n");
30
- console.log("═".repeat(60));
31
- if (configuredProviders.length > 0) {
32
- console.log("\n📦 Configured Providers\n");
33
- for (const provider of configuredProviders) console.log(` ✓ ${provider}`);
34
- console.log("");
35
- }
36
- const models = options.all ? getAllModels() : await getAvailableModels();
37
- console.log("\n📚 Models\n");
38
- const byProvider = /* @__PURE__ */ new Map();
39
- for (const model of models) {
40
- const list = byProvider.get(model.provider) ?? [];
41
- list.push(model);
42
- byProvider.set(model.provider, list);
43
- }
44
- const IMAGE_GENERATION_IDS = new Set([
45
- "gpt-image-1",
46
- "dall-e-3",
47
- "dall-e-2",
48
- "wan2.6-t2i",
49
- "wan2.7-image-pro",
50
- "wan2.7-image",
51
- "wan2.1-t2i-turbo",
52
- "wan2.1-t2i-plus",
53
- "image-01",
54
- "image-01-live"
55
- ]);
56
- const VISION_IDS = new Set([
57
- "gpt-4o",
58
- "gpt-4o-mini",
59
- "gpt-4-turbo",
60
- "claude-sonnet-4-5",
61
- "claude-haiku-3-5",
62
- "gemini-2.0-flash",
63
- "gemini-1.5-pro",
64
- "qwen-vl-max",
65
- "qwen2.5-vl-72b-instruct"
66
- ]);
67
- for (const [provider, providerModels] of byProvider) {
68
- console.log(` [${provider}]`);
69
- for (const model of providerModels) {
70
- const status = await isProviderConfigured(provider) ? "✓" : "○";
71
- const badges = [];
72
- if (IMAGE_GENERATION_IDS.has(model.id)) badges.push("gen");
73
- if (model.input?.includes("image") || VISION_IDS.has(model.id)) badges.push("vision");
74
- const badgeStr = badges.length > 0 ? ` [${badges.join(", ")}]` : "";
75
- console.log(` ${status} ${model.name}${badgeStr}`);
76
- }
77
- }
78
- console.log("");
79
- console.log("═".repeat(60));
80
- console.log(`\n📌 Current default model: ${config.agents?.defaults?.model || "Not set"}`);
81
- console.log("\n📝 Usage:");
82
- console.log(" export OPENAI_API_KEY=\"sk-...\" # Set API key via env");
83
- console.log(" xopc agent -m \"Hello\" # Use default model");
84
- console.log(" xopc agent -m \"Hello\" --model openai/gpt-4o # Specify model");
88
+ "xopc models list --json # Output as JSON",
89
+ "xopc models --all # Same as models list --all"
90
+ ]));
91
+ attachModelsListOptions(cmd).action(async (options) => {
92
+ await runModelsList(options);
93
+ });
94
+ attachModelsListOptions(cmd.command("list").description("List available models")).action(async (options) => {
95
+ await runModelsList(options);
85
96
  });
97
+ return cmd;
86
98
  }
87
99
  register({
88
100
  id: "models",
@@ -95,6 +107,6 @@ register({
95
107
  }
96
108
  });
97
109
  //#endregion
98
- export {};
110
+ export { createModelsCommand };
99
111
 
100
112
  //# sourceMappingURL=models.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"models.js","names":[],"sources":["../../../../src/cli/commands/models.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { loadConfig } from '../../config/index.js';\nimport { register, formatExamples } from '../registry.js';\nimport type { CLIContext } from '../registry.js';\nimport { getContextWithOpts } from '../context.js';\nimport {\n getAllModels,\n getAvailableModels,\n getConfiguredProviders,\n isProviderConfigured,\n} from '../../providers/index.js';\n\nfunction createModelsCommand(_ctx: CLIContext): Command {\n const cmd = new Command('models')\n .description('List and manage available models')\n .addHelpText(\n 'after',\n formatExamples([\n 'xopc models list # List all available models',\n 'xopc models list --all # Show all built-in models',\n 'xopc models list --json # Output as JSON',\n ])\n )\n .option('--json', 'Output as JSON', false)\n .option('--all, -a', 'Show all built-in models', false)\n .action(async (options) => {\n const ctx = getContextWithOpts();\n const config = loadConfig(ctx.configPath);\n const configuredProviders = await getConfiguredProviders();\n\n if (options.json) {\n const models = options.all\n ? getAllModels()\n : await getAvailableModels();\n console.log(JSON.stringify({\n providers: configuredProviders,\n models: models.map(m => ({\n id: `${m.provider}/${m.id}`,\n name: m.name,\n provider: m.provider,\n })),\n }, null, 2));\n return;\n }\n\n console.log('\\n🤖 Available Models\\n');\n console.log('═'.repeat(60));\n\n if (configuredProviders.length > 0) {\n console.log('\\n📦 Configured Providers\\n');\n for (const provider of configuredProviders) {\n console.log(` ✓ ${provider}`);\n }\n console.log('');\n }\n\n const models = options.all\n ? getAllModels()\n : await getAvailableModels();\n\n console.log('\\n📚 Models\\n');\n \n // Group by provider\n const byProvider = new Map<string, ReturnType<typeof getAllModels>[number][]>();\n for (const model of models) {\n const list = byProvider.get(model.provider) ?? [];\n list.push(model);\n byProvider.set(model.provider, list);\n }\n\n const IMAGE_GENERATION_IDS = new Set([\n 'gpt-image-1',\n 'dall-e-3',\n 'dall-e-2',\n 'wan2.6-t2i',\n 'wan2.7-image-pro',\n 'wan2.7-image',\n 'wan2.1-t2i-turbo',\n 'wan2.1-t2i-plus',\n 'image-01',\n 'image-01-live',\n ]);\n const VISION_IDS = new Set([\n 'gpt-4o',\n 'gpt-4o-mini',\n 'gpt-4-turbo',\n 'claude-sonnet-4-5',\n 'claude-haiku-3-5',\n 'gemini-2.0-flash',\n 'gemini-1.5-pro',\n 'qwen-vl-max',\n 'qwen2.5-vl-72b-instruct',\n ]);\n\n for (const [provider, providerModels] of byProvider) {\n console.log(` [${provider}]`);\n for (const model of providerModels) {\n const available = await isProviderConfigured(provider);\n const status = available ? '✓' : '○';\n const badges: string[] = [];\n if (IMAGE_GENERATION_IDS.has(model.id)) {\n badges.push('gen');\n }\n if (model.input?.includes('image') || VISION_IDS.has(model.id)) {\n badges.push('vision');\n }\n const badgeStr = badges.length > 0 ? ` [${badges.join(', ')}]` : '';\n console.log(` ${status} ${model.name}${badgeStr}`);\n }\n }\n console.log('');\n\n console.log('═'.repeat(60));\n console.log(`\\n📌 Current default model: ${config.agents?.defaults?.model || 'Not set'}`);\n\n console.log('\\n📝 Usage:');\n console.log(' export OPENAI_API_KEY=\"sk-...\" # Set API key via env');\n console.log(' xopc agent -m \"Hello\" # Use default model');\n console.log(' xopc agent -m \"Hello\" --model openai/gpt-4o # Specify model');\n });\n\n return cmd;\n}\n\nregister({\n id: 'models',\n name: 'models',\n description: 'List and manage available models',\n factory: createModelsCommand,\n metadata: {\n category: 'utility',\n examples: [\n 'xopc models list',\n 'xopc models list --all',\n ],\n },\n});\n"],"mappings":";;;;;;;gBAUkC;AAElC,SAAS,oBAAoB,MAA2B;AA6GtD,QA5GY,IAAI,QAAQ,SAAS,CAC9B,YAAY,mCAAmC,CAC/C,YACC,SACA,eAAe;EACb;EACA;EACA;EACD,CAAC,CACH,CACA,OAAO,UAAU,kBAAkB,MAAM,CACzC,OAAO,aAAa,4BAA4B,MAAM,CACtD,OAAO,OAAO,YAAY;EAEzB,MAAM,SAAS,WADH,oBACiB,CAAC,WAAW;EACzC,MAAM,sBAAsB,MAAM,wBAAwB;AAE1D,MAAI,QAAQ,MAAM;GAChB,MAAM,SAAS,QAAQ,MACnB,cAAc,GACd,MAAM,oBAAoB;AAC9B,WAAQ,IAAI,KAAK,UAAU;IACzB,WAAW;IACX,QAAQ,OAAO,KAAI,OAAM;KACvB,IAAI,GAAG,EAAE,SAAS,GAAG,EAAE;KACvB,MAAM,EAAE;KACR,UAAU,EAAE;KACb,EAAE;IACJ,EAAE,MAAM,EAAE,CAAC;AACZ;;AAGF,UAAQ,IAAI,0BAA0B;AACtC,UAAQ,IAAI,IAAI,OAAO,GAAG,CAAC;AAE3B,MAAI,oBAAoB,SAAS,GAAG;AAClC,WAAQ,IAAI,8BAA8B;AAC1C,QAAK,MAAM,YAAY,oBACrB,SAAQ,IAAI,OAAO,WAAW;AAEhC,WAAQ,IAAI,GAAG;;EAGjB,MAAM,SAAS,QAAQ,MACnB,cAAc,GACd,MAAM,oBAAoB;AAE9B,UAAQ,IAAI,gBAAgB;EAG5B,MAAM,6BAAa,IAAI,KAAwD;AAC/E,OAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,OAAO,WAAW,IAAI,MAAM,SAAS,IAAI,EAAE;AACjD,QAAK,KAAK,MAAM;AAChB,cAAW,IAAI,MAAM,UAAU,KAAK;;EAGtC,MAAM,uBAAuB,IAAI,IAAI;GACnC;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,MAAM,aAAa,IAAI,IAAI;GACzB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;AAEF,OAAK,MAAM,CAAC,UAAU,mBAAmB,YAAY;AACnD,WAAQ,IAAI,MAAM,SAAS,GAAG;AAC9B,QAAK,MAAM,SAAS,gBAAgB;IAElC,MAAM,SAAS,MADS,qBAAqB,SAAS,GAC3B,MAAM;IACjC,MAAM,SAAmB,EAAE;AAC3B,QAAI,qBAAqB,IAAI,MAAM,GAAG,CACpC,QAAO,KAAK,MAAM;AAEpB,QAAI,MAAM,OAAO,SAAS,QAAQ,IAAI,WAAW,IAAI,MAAM,GAAG,CAC5D,QAAO,KAAK,SAAS;IAEvB,MAAM,WAAW,OAAO,SAAS,IAAI,KAAK,OAAO,KAAK,KAAK,CAAC,KAAK;AACjE,YAAQ,IAAI,OAAO,OAAO,GAAG,MAAM,OAAO,WAAW;;;AAGzD,UAAQ,IAAI,GAAG;AAEf,UAAQ,IAAI,IAAI,OAAO,GAAG,CAAC;AAC3B,UAAQ,IAAI,+BAA+B,OAAO,QAAQ,UAAU,SAAS,YAAY;AAEzF,UAAQ,IAAI,cAAc;AAC1B,UAAQ,IAAI,sEAAoE;AAChF,UAAQ,IAAI,gEAA8D;AAC1E,UAAQ,IAAI,oEAAkE;GAGxE;;AAGZ,SAAS;CACP,IAAI;CACJ,MAAM;CACN,aAAa;CACb,SAAS;CACT,UAAU;EACR,UAAU;EACV,UAAU,CACR,oBACA,yBACD;EACF;CACF,CAAC"}
1
+ {"version":3,"file":"models.js","names":[],"sources":["../../../../src/cli/commands/models.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { register, formatExamples, type CLIContext } from '../registry.js';\nimport { getContextWithOpts } from '../context.js';\nimport {\n getAllModels,\n getAvailableModels,\n getConfiguredProviders,\n isProviderConfigured,\n} from '../../providers/index.js';\nimport { loadConfig } from '../../config/index.js';\n\ntype ModelsListOptions = {\n json?: boolean;\n all?: boolean;\n};\n\nasync function runModelsList(options: ModelsListOptions): Promise<void> {\n const ctx = getContextWithOpts();\n const config = loadConfig(ctx.configPath);\n const configuredProviders = await getConfiguredProviders();\n\n if (options.json) {\n const models = options.all ? getAllModels() : await getAvailableModels();\n console.log(\n JSON.stringify(\n {\n providers: configuredProviders,\n models: models.map((m) => ({\n id: `${m.provider}/${m.id}`,\n name: m.name,\n provider: m.provider,\n })),\n },\n null,\n 2,\n ),\n );\n return;\n }\n\n console.log('\\n🤖 Available Models\\n');\n console.log('═'.repeat(60));\n\n if (configuredProviders.length > 0) {\n console.log('\\n📦 Configured Providers\\n');\n for (const provider of configuredProviders) {\n console.log(` ✓ ${provider}`);\n }\n console.log('');\n }\n\n const models = options.all ? getAllModels() : await getAvailableModels();\n\n console.log('\\n📚 Models\\n');\n\n const byProvider = new Map<string, ReturnType<typeof getAllModels>[number][]>();\n for (const model of models) {\n const list = byProvider.get(model.provider) ?? [];\n list.push(model);\n byProvider.set(model.provider, list);\n }\n\n const IMAGE_GENERATION_IDS = new Set([\n 'gpt-image-1',\n 'dall-e-3',\n 'dall-e-2',\n 'wan2.6-t2i',\n 'wan2.7-image-pro',\n 'wan2.7-image',\n 'wan2.1-t2i-turbo',\n 'wan2.1-t2i-plus',\n 'image-01',\n 'image-01-live',\n ]);\n const VISION_IDS = new Set([\n 'gpt-4o',\n 'gpt-4o-mini',\n 'gpt-4-turbo',\n 'claude-sonnet-4-5',\n 'claude-haiku-3-5',\n 'gemini-2.0-flash',\n 'gemini-1.5-pro',\n 'qwen-vl-max',\n 'qwen2.5-vl-72b-instruct',\n ]);\n\n for (const [provider, providerModels] of byProvider) {\n console.log(` [${provider}]`);\n for (const model of providerModels) {\n const available = await isProviderConfigured(provider);\n const status = available ? '✓' : '○';\n const badges: string[] = [];\n if (IMAGE_GENERATION_IDS.has(model.id)) {\n badges.push('gen');\n }\n if (model.input?.includes('image') || VISION_IDS.has(model.id)) {\n badges.push('vision');\n }\n const badgeStr = badges.length > 0 ? ` [${badges.join(', ')}]` : '';\n console.log(` ${status} ${model.name}${badgeStr}`);\n }\n }\n console.log('');\n\n console.log('═'.repeat(60));\n console.log(`\\n📌 Current default model: ${config.agents?.defaults?.model || 'Not set'}`);\n\n console.log('\\n📝 Usage:');\n console.log(' export OPENAI_API_KEY=\"sk-...\" # Set API key via env');\n console.log(' xopc agent -m \"Hello\" # Use default model');\n console.log(' xopc agent -m \"Hello\" --model openai/gpt-4o # Specify model');\n}\n\nfunction attachModelsListOptions(command: Command): Command {\n return command\n .option('--json', 'Output as JSON', false)\n .option('--all, -a', 'Show all built-in models', false);\n}\n\nfunction createModelsCommand(_ctx: CLIContext): Command {\n const cmd = new Command('models')\n .description('List and manage available models')\n .addHelpText(\n 'after',\n formatExamples([\n 'xopc models list # List all available models',\n 'xopc models list --all # Show all built-in models',\n 'xopc models list --json # Output as JSON',\n 'xopc models --all # Same as models list --all',\n ]),\n );\n\n attachModelsListOptions(cmd).action(async (options) => {\n await runModelsList(options);\n });\n\n attachModelsListOptions(\n cmd.command('list').description('List available models'),\n ).action(async (options) => {\n await runModelsList(options);\n });\n\n return cmd;\n}\n\nregister({\n id: 'models',\n name: 'models',\n description: 'List and manage available models',\n factory: createModelsCommand,\n metadata: {\n category: 'utility',\n examples: [\n 'xopc models list',\n 'xopc models list --all',\n ],\n },\n});\n\nexport { createModelsCommand };\n"],"mappings":"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"}
@@ -121,7 +121,9 @@ async function runOnboard(options, ctx) {
121
121
  console.log(" xopc agent -m \"Hello\" # Chat with AI");
122
122
  console.log(" xopc agent -i # Interactive mode");
123
123
  console.log(" xopc models list # List models");
124
+ console.log(" xopc config validate # Validate xopc.json");
124
125
  console.log(" xopc auth list # View authentication");
126
+ console.log(" xopc init # Full state dirs (if upgrading or missing data)");
125
127
  console.log("\n📁 Files:");
126
128
  console.log(" Config:", configPath);
127
129
  console.log(" Workspace:", workspacePath);
@@ -1 +1 @@
1
- {"version":3,"file":"onboard.js","names":["runModelSetup","runChannelOnboard"],"sources":["../../../../src/cli/commands/onboard.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { join } from 'path';\nimport { select } from '@inquirer/prompts';\nimport { saveConfig } from '../../config/index.js';\nimport { register, formatExamples } from '../registry.js';\nimport type { CLIContext } from '../registry.js';\nimport type { Config } from '../../config/schema.js';\nimport { setupModel as runModelSetup } from './onboard/model.js';\nimport { colors } from '../utils/colors.js';\nimport { setupChannels as runChannelOnboard, getChannelConfigurators } from './onboard/channels/index.js';\nimport { seedMainAgentProfileMarkdown } from '../../agent/context/workspace-seed.js';\nimport { resolveDefaultAgentId, resolveAgentProfileDir } from '../../agent/agent-scope.js';\nimport { WORKSPACE_FILES } from '../../config/paths.js';\nimport { resolveGatewayLocalClientHost } from '../../config/gateway-bind.js';\nimport { initWorkspace } from '../utils/init-workspace.js';\nimport { ConfigSchema } from '../../config/schema.js';\nimport { isWeixinOnboardConfigured } from '../../../extensions/weixin/src/adapters/onboard-cli.js';\n\nfunction isInteractive(): boolean {\n return process.stdin.isTTY && process.stdout.isTTY;\n}\n\nasync function setupNonInteractive(_configPath: string, existingConfig: Config): Promise<Config> {\n console.log('\\n🤖 AI Model Configuration (Non-Interactive Mode)\\n');\n console.log('Current config:', JSON.stringify(existingConfig.agents?.defaults?.model, null, 2));\n console.log('\\n💡 To configure in interactive mode, run: xopc onboard');\n console.log('💡 Or set up manually in:', _configPath);\n return existingConfig;\n}\n\nfunction createOnboardCommand(ctx: CLIContext): Command {\n const cmd = new Command('onboard')\n .description('Interactive setup wizard for xopc (gateway uses schema defaults)')\n .addHelpText(\n 'after',\n formatExamples([\n 'xopc onboard # Full interactive setup',\n 'xopc onboard --model # Configure LLM model only',\n 'xopc onboard --channels # Configure channels (incl. Weixin QR)',\n 'xopc onboard --gateway # Apply default gateway settings (quiet)',\n ])\n )\n .option('--model', 'Configure LLM provider and model')\n .option('--channels', 'Configure messaging channels')\n .option('--gateway', 'Configure gateway WebUI')\n .option('--all', 'Configure everything (default)')\n .action(async (options) => {\n try {\n await runOnboard(options, ctx);\n } catch (error: unknown) {\n const err = error as { name?: string; code?: string };\n if (err?.name === 'ExitPromptError' || err?.code === 'EXIT_PROMPT') {\n console.log('\\n\\n👋 Setup cancelled.');\n process.exit(0);\n }\n throw error;\n }\n });\n\n return cmd;\n}\n\ntype OnboardOptions = {\n model?: boolean;\n channels?: boolean;\n gateway?: boolean;\n all?: boolean;\n};\n\nasync function runOnboard(\n options: OnboardOptions,\n ctx: CLIContext\n): Promise<void> {\n console.log(colors.cyan('\\n🚀 Welcome to xopc setup!\\n'));\n console.log('═'.repeat(50));\n\n const workspacePath = ctx.workspacePath;\n const configPath = ctx.configPath;\n\n const initResult = await initWorkspace({ configPath, workspacePath });\n let config = initResult.config;\n\n // Determine what to configure based on options\n const doModel = options.model || options.all || (!options.channels && !options.gateway);\n const doChannels = options.channels || options.all || (!options.model && !options.gateway);\n const doGateway = options.gateway || options.all || (!options.model && !options.channels);\n const runFullWizard = !options.model && !options.channels && !options.gateway;\n /** Any setup step besides the unified launch prompt ran in interactive flow. */\n const didConfigurableSteps = doModel || doChannels || doGateway;\n\n if (!isInteractive()) {\n // Non-interactive mode\n if (doModel) {\n config = await setupNonInteractive(configPath, config);\n }\n if (doChannels) {\n console.log('\\n💬 Channels Configuration (Non-Interactive Mode)\\n');\n console.log('💡 To configure channels, edit the config file manually.');\n }\n if (doGateway) {\n console.log('\\n🌐 Gateway Configuration (Non-Interactive Mode)\\n');\n console.log('💡 To configure gateway, edit the config file manually.');\n }\n } else {\n // Interactive mode\n if (doModel) {\n config = await runModelSetup(config, ctx);\n }\n\n if (doChannels) {\n const channelIds = getChannelConfigurators().map(c => c.id);\n console.log(colors.gray(`\\nChannel onboarding: ${channelIds.join(', ')}\\n`));\n config = await runChannelOnboard(config);\n }\n\n if (doGateway) {\n config = await setupGateway(config);\n }\n }\n\n // Save config once at the end\n await saveConfig(config as Config, configPath);\n\n seedMainAgentProfileMarkdown(config as Config);\n\n console.log('\\n' + '═'.repeat(50));\n console.log('\\n🎉 Setup Complete!\\n');\n\n const gatewayAuth = (config as any)?.gateway?.auth;\n const gatewayConfigured =\n gatewayAuth?.mode === 'token' &&\n typeof gatewayAuth?.token === 'string' &&\n gatewayAuth.token.length > 0;\n const port = (config as Config)?.gateway?.port ?? 18790;\n const displayHost = resolveGatewayLocalClientHost(config as Config);\n const gwToken = gatewayConfigured ? (gatewayAuth.token as string) : undefined;\n\n const showGatewaySummary = Boolean(gatewayConfigured && gwToken && (doGateway || runFullWizard));\n\n if (showGatewaySummary && gwToken) {\n const webuiUrl = `http://${displayHost}:${port}?token=${gwToken}`;\n console.log('🌐 Web console (browser) — start here');\n console.log(` Open: http://${displayHost}:${port}`);\n console.log(` Token: ${gwToken.slice(0, 8)}...${gwToken.slice(-8)}`);\n console.log(' Bookmark link (token is saved in the browser when you open it):');\n console.log(` ${webuiUrl}`);\n console.log('');\n }\n\n if (runFullWizard) {\n console.log('🚀 Next steps:');\n if (gatewayConfigured) {\n console.log(' 1. Choose how to launch below (gateway or terminal UI)');\n console.log(' 2. Or chat with: xopc agent -i');\n console.log(' 3. Optional: read BOOTSTRAP.md in your workspace for workspace tips');\n } else {\n console.log(' 1. Chat in the terminal: xopc agent -i');\n console.log(' 2. Optional: add the Web console: xopc onboard --gateway');\n console.log(' 3. Optional: read BOOTSTRAP.md in your workspace');\n }\n console.log('');\n } else if (doGateway && gatewayConfigured) {\n console.log('🚀 Next step:');\n console.log(' Start the gateway if it is not running, then open the Web console URL above.');\n console.log('');\n }\n\n console.log('📝 Usage:');\n console.log(' xopc agent -m \"Hello\" # Chat with AI');\n console.log(' xopc agent -i # Interactive mode');\n console.log(' xopc models list # List models');\n console.log(' xopc auth list # View authentication');\n\n console.log('\\n📁 Files:');\n console.log(' Config:', configPath);\n console.log(' Workspace:', workspacePath);\n if (runFullWizard) {\n console.log(\n ' BOOTSTRAP.md (origin story):',\n join(resolveAgentProfileDir(config as Config, resolveDefaultAgentId(config as Config)), WORKSPACE_FILES.BOOTSTRAP),\n );\n }\n\n if (isInteractive() && didConfigurableSteps) {\n await promptLaunchAfterOnboard(config as Config, ctx, { doChannels });\n }\n\n process.exit(0);\n}\n\nasync function startGatewayAsService(config: Config, ctx: CLIContext): Promise<void> {\n const { startGatewayNow } = await import('./onboard/gateway.js');\n await startGatewayNow(config, ctx);\n}\n\nasync function promptLaunchAfterOnboard(\n config: Config,\n ctx: CLIContext,\n flags: { doChannels: boolean },\n): Promise<void> {\n console.log('');\n const choice = await select<'tui' | 'gateway' | 'none'>({\n message: 'How do you want to launch xopc now?',\n choices: [\n {\n value: 'tui',\n name: 'Terminal UI (embedded)',\n description: 'xopc tui --local — no gateway process required',\n },\n {\n value: 'gateway',\n name: 'Gateway WebUI (OS service)',\n description: 'Install and start the HTTP gateway for the browser console',\n },\n {\n value: 'none',\n name: 'Exit — I will start manually',\n description: 'Finish setup without starting a runtime',\n },\n ],\n default: 'tui',\n });\n\n if (choice === 'gateway') {\n await startGatewayAsService(config, ctx);\n return;\n }\n\n if (choice === 'tui') {\n if (flags.doChannels && !isWeixinOnboardConfigured(config)) {\n console.log(\n colors.gray(\n '\\n💡 Weixin is not logged in yet. When ready run: xopc channels login --channel weixin\\n',\n ),\n );\n }\n const { runTui } = await import('../../tui/tui.js');\n await runTui({ local: true });\n return;\n }\n\n console.log('\\n⏭️ You can start later:');\n console.log(' xopc gateway service install');\n console.log(' xopc gateway');\n console.log(' xopc tui --local');\n}\n\nasync function setupGateway(config: Config): Promise<Config> {\n console.log(colors.cyan('\\n🌐 Gateway WebUI\\n'));\n console.log(\n colors.gray(\n 'Applying defaults from config schema (127.0.0.1:18790, token auth; token generated if missing).\\n',\n ),\n );\n\n const gw = config.gateway ?? {};\n const { randomBytes } = await import('node:crypto');\n const authMode = gw.auth?.mode === 'none' ? ('none' as const) : ('token' as const);\n const token =\n authMode === 'token'\n ? typeof gw.auth?.token === 'string' && gw.auth.token.length > 0\n ? gw.auth.token\n : randomBytes(24).toString('hex')\n : undefined;\n\n const merged: Config = {\n ...config,\n gateway: {\n ...gw,\n bind: gw.bind ?? 'loopback',\n port: gw.port ?? 18790,\n auth:\n authMode === 'none'\n ? { mode: 'none' as const }\n : { mode: 'token' as const, token: token! },\n },\n };\n\n const parsed = ConfigSchema.parse(merged);\n console.log('✅ Gateway defaults applied.\\n');\n return parsed;\n}\n\nregister({\n id: 'onboard',\n name: 'onboard',\n description: 'Interactive setup wizard',\n factory: createOnboardCommand,\n metadata: {\n category: 'setup',\n examples: ['xopc onboard', 'xopc onboard --model', 'xopc onboard --channels'],\n },\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"onboard.js","names":["runModelSetup","runChannelOnboard"],"sources":["../../../../src/cli/commands/onboard.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { join } from 'path';\nimport { select } from '@inquirer/prompts';\nimport { saveConfig } from '../../config/index.js';\nimport { register, formatExamples } from '../registry.js';\nimport type { CLIContext } from '../registry.js';\nimport type { Config } from '../../config/schema.js';\nimport { setupModel as runModelSetup } from './onboard/model.js';\nimport { colors } from '../utils/colors.js';\nimport { setupChannels as runChannelOnboard, getChannelConfigurators } from './onboard/channels/index.js';\nimport { seedMainAgentProfileMarkdown } from '../../agent/context/workspace-seed.js';\nimport { resolveDefaultAgentId, resolveAgentProfileDir } from '../../agent/agent-scope.js';\nimport { WORKSPACE_FILES } from '../../config/paths.js';\nimport { resolveGatewayLocalClientHost } from '../../config/gateway-bind.js';\nimport { initWorkspace } from '../utils/init-workspace.js';\nimport { ConfigSchema } from '../../config/schema.js';\nimport { isWeixinOnboardConfigured } from '../../../extensions/weixin/src/adapters/onboard-cli.js';\n\nfunction isInteractive(): boolean {\n return process.stdin.isTTY && process.stdout.isTTY;\n}\n\nasync function setupNonInteractive(_configPath: string, existingConfig: Config): Promise<Config> {\n console.log('\\n🤖 AI Model Configuration (Non-Interactive Mode)\\n');\n console.log('Current config:', JSON.stringify(existingConfig.agents?.defaults?.model, null, 2));\n console.log('\\n💡 To configure in interactive mode, run: xopc onboard');\n console.log('💡 Or set up manually in:', _configPath);\n return existingConfig;\n}\n\nfunction createOnboardCommand(ctx: CLIContext): Command {\n const cmd = new Command('onboard')\n .description('Interactive setup wizard for xopc (gateway uses schema defaults)')\n .addHelpText(\n 'after',\n formatExamples([\n 'xopc onboard # Full interactive setup',\n 'xopc onboard --model # Configure LLM model only',\n 'xopc onboard --channels # Configure channels (incl. Weixin QR)',\n 'xopc onboard --gateway # Apply default gateway settings (quiet)',\n ])\n )\n .option('--model', 'Configure LLM provider and model')\n .option('--channels', 'Configure messaging channels')\n .option('--gateway', 'Configure gateway WebUI')\n .option('--all', 'Configure everything (default)')\n .action(async (options) => {\n try {\n await runOnboard(options, ctx);\n } catch (error: unknown) {\n const err = error as { name?: string; code?: string };\n if (err?.name === 'ExitPromptError' || err?.code === 'EXIT_PROMPT') {\n console.log('\\n\\n👋 Setup cancelled.');\n process.exit(0);\n }\n throw error;\n }\n });\n\n return cmd;\n}\n\ntype OnboardOptions = {\n model?: boolean;\n channels?: boolean;\n gateway?: boolean;\n all?: boolean;\n};\n\nasync function runOnboard(\n options: OnboardOptions,\n ctx: CLIContext\n): Promise<void> {\n console.log(colors.cyan('\\n🚀 Welcome to xopc setup!\\n'));\n console.log('═'.repeat(50));\n\n const workspacePath = ctx.workspacePath;\n const configPath = ctx.configPath;\n\n const initResult = await initWorkspace({ configPath, workspacePath });\n let config = initResult.config;\n\n // Determine what to configure based on options\n const doModel = options.model || options.all || (!options.channels && !options.gateway);\n const doChannels = options.channels || options.all || (!options.model && !options.gateway);\n const doGateway = options.gateway || options.all || (!options.model && !options.channels);\n const runFullWizard = !options.model && !options.channels && !options.gateway;\n /** Any setup step besides the unified launch prompt ran in interactive flow. */\n const didConfigurableSteps = doModel || doChannels || doGateway;\n\n if (!isInteractive()) {\n // Non-interactive mode\n if (doModel) {\n config = await setupNonInteractive(configPath, config);\n }\n if (doChannels) {\n console.log('\\n💬 Channels Configuration (Non-Interactive Mode)\\n');\n console.log('💡 To configure channels, edit the config file manually.');\n }\n if (doGateway) {\n console.log('\\n🌐 Gateway Configuration (Non-Interactive Mode)\\n');\n console.log('💡 To configure gateway, edit the config file manually.');\n }\n } else {\n // Interactive mode\n if (doModel) {\n config = await runModelSetup(config, ctx);\n }\n\n if (doChannels) {\n const channelIds = getChannelConfigurators().map(c => c.id);\n console.log(colors.gray(`\\nChannel onboarding: ${channelIds.join(', ')}\\n`));\n config = await runChannelOnboard(config);\n }\n\n if (doGateway) {\n config = await setupGateway(config);\n }\n }\n\n // Save config once at the end\n await saveConfig(config as Config, configPath);\n\n seedMainAgentProfileMarkdown(config as Config);\n\n console.log('\\n' + '═'.repeat(50));\n console.log('\\n🎉 Setup Complete!\\n');\n\n const gatewayAuth = (config as any)?.gateway?.auth;\n const gatewayConfigured =\n gatewayAuth?.mode === 'token' &&\n typeof gatewayAuth?.token === 'string' &&\n gatewayAuth.token.length > 0;\n const port = (config as Config)?.gateway?.port ?? 18790;\n const displayHost = resolveGatewayLocalClientHost(config as Config);\n const gwToken = gatewayConfigured ? (gatewayAuth.token as string) : undefined;\n\n const showGatewaySummary = Boolean(gatewayConfigured && gwToken && (doGateway || runFullWizard));\n\n if (showGatewaySummary && gwToken) {\n const webuiUrl = `http://${displayHost}:${port}?token=${gwToken}`;\n console.log('🌐 Web console (browser) — start here');\n console.log(` Open: http://${displayHost}:${port}`);\n console.log(` Token: ${gwToken.slice(0, 8)}...${gwToken.slice(-8)}`);\n console.log(' Bookmark link (token is saved in the browser when you open it):');\n console.log(` ${webuiUrl}`);\n console.log('');\n }\n\n if (runFullWizard) {\n console.log('🚀 Next steps:');\n if (gatewayConfigured) {\n console.log(' 1. Choose how to launch below (gateway or terminal UI)');\n console.log(' 2. Or chat with: xopc agent -i');\n console.log(' 3. Optional: read BOOTSTRAP.md in your workspace for workspace tips');\n } else {\n console.log(' 1. Chat in the terminal: xopc agent -i');\n console.log(' 2. Optional: add the Web console: xopc onboard --gateway');\n console.log(' 3. Optional: read BOOTSTRAP.md in your workspace');\n }\n console.log('');\n } else if (doGateway && gatewayConfigured) {\n console.log('🚀 Next step:');\n console.log(' Start the gateway if it is not running, then open the Web console URL above.');\n console.log('');\n }\n\n console.log('📝 Usage:');\n console.log(' xopc agent -m \"Hello\" # Chat with AI');\n console.log(' xopc agent -i # Interactive mode');\n console.log(' xopc models list # List models');\n console.log(' xopc config validate # Validate xopc.json');\n console.log(' xopc auth list # View authentication');\n console.log(' xopc init # Full state dirs (if upgrading or missing data)');\n\n console.log('\\n📁 Files:');\n console.log(' Config:', configPath);\n console.log(' Workspace:', workspacePath);\n if (runFullWizard) {\n console.log(\n ' BOOTSTRAP.md (origin story):',\n join(resolveAgentProfileDir(config as Config, resolveDefaultAgentId(config as Config)), WORKSPACE_FILES.BOOTSTRAP),\n );\n }\n\n if (isInteractive() && didConfigurableSteps) {\n await promptLaunchAfterOnboard(config as Config, ctx, { doChannels });\n }\n\n process.exit(0);\n}\n\nasync function startGatewayAsService(config: Config, ctx: CLIContext): Promise<void> {\n const { startGatewayNow } = await import('./onboard/gateway.js');\n await startGatewayNow(config, ctx);\n}\n\nasync function promptLaunchAfterOnboard(\n config: Config,\n ctx: CLIContext,\n flags: { doChannels: boolean },\n): Promise<void> {\n console.log('');\n const choice = await select<'tui' | 'gateway' | 'none'>({\n message: 'How do you want to launch xopc now?',\n choices: [\n {\n value: 'tui',\n name: 'Terminal UI (embedded)',\n description: 'xopc tui --local — no gateway process required',\n },\n {\n value: 'gateway',\n name: 'Gateway WebUI (OS service)',\n description: 'Install and start the HTTP gateway for the browser console',\n },\n {\n value: 'none',\n name: 'Exit — I will start manually',\n description: 'Finish setup without starting a runtime',\n },\n ],\n default: 'tui',\n });\n\n if (choice === 'gateway') {\n await startGatewayAsService(config, ctx);\n return;\n }\n\n if (choice === 'tui') {\n if (flags.doChannels && !isWeixinOnboardConfigured(config)) {\n console.log(\n colors.gray(\n '\\n💡 Weixin is not logged in yet. When ready run: xopc channels login --channel weixin\\n',\n ),\n );\n }\n const { runTui } = await import('../../tui/tui.js');\n await runTui({ local: true });\n return;\n }\n\n console.log('\\n⏭️ You can start later:');\n console.log(' xopc gateway service install');\n console.log(' xopc gateway');\n console.log(' xopc tui --local');\n}\n\nasync function setupGateway(config: Config): Promise<Config> {\n console.log(colors.cyan('\\n🌐 Gateway WebUI\\n'));\n console.log(\n colors.gray(\n 'Applying defaults from config schema (127.0.0.1:18790, token auth; token generated if missing).\\n',\n ),\n );\n\n const gw = config.gateway ?? {};\n const { randomBytes } = await import('node:crypto');\n const authMode = gw.auth?.mode === 'none' ? ('none' as const) : ('token' as const);\n const token =\n authMode === 'token'\n ? typeof gw.auth?.token === 'string' && gw.auth.token.length > 0\n ? gw.auth.token\n : randomBytes(24).toString('hex')\n : undefined;\n\n const merged: Config = {\n ...config,\n gateway: {\n ...gw,\n bind: gw.bind ?? 'loopback',\n port: gw.port ?? 18790,\n auth:\n authMode === 'none'\n ? { mode: 'none' as const }\n : { mode: 'token' as const, token: token! },\n },\n };\n\n const parsed = ConfigSchema.parse(merged);\n console.log('✅ Gateway defaults applied.\\n');\n return parsed;\n}\n\nregister({\n id: 'onboard',\n name: 'onboard',\n description: 'Interactive setup wizard',\n factory: createOnboardCommand,\n metadata: {\n category: 'setup',\n examples: ['xopc onboard', 'xopc onboard --model', 'xopc onboard --channels'],\n },\n});\n"],"mappings":"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"}
@@ -1,6 +1,4 @@
1
1
  import { Command } from 'commander';
2
- export declare function createProfileListCommand(): Command;
3
- export declare function createProfileCreateCommand(): Command;
4
- export declare function createProfileDeleteCommand(): Command;
5
- export declare function createProfileSwitchCommand(): Command;
6
- export declare function registerProfileCommands(program: Command): void;
2
+ import { type CLIContext } from '../registry.js';
3
+ declare function createProfileCommand(_ctx: CLIContext): Command;
4
+ export { createProfileCommand };
@@ -1,14 +1,21 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
- import { createProfile, deleteProfile, getCurrentProfile, listProfiles, resolveProfileStateDir } from "../../config/profile.js";
3
+ import { formatExamples, register } from "../registry.js";
4
+ import { createProfile, deleteProfile, getCurrentProfile, getSwitchCommand, listProfiles, resolveProfileStateDir } from "../../config/profile.js";
4
5
  import { colors } from "../utils/colors.js";
5
6
  import { Command } from "commander";
6
7
  import Table from "cli-table3";
7
8
  //#region src/cli/commands/profile.ts
8
9
  init_logger();
9
10
  const log = createLogger("ProfileCommands");
10
- function createProfileListCommand() {
11
- return new Command("profile:list").description("List all profiles").option("--json", "Output as JSON").action(async (options) => {
11
+ function createProfileCommand(_ctx) {
12
+ const cmd = new Command("profile").description("Manage xopc state profiles (~/.xopc vs ~/.xopc-<name>)").addHelpText("after", formatExamples([
13
+ "xopc profile list",
14
+ "xopc profile create staging",
15
+ "xopc profile switch staging",
16
+ "xopc profile delete staging --force"
17
+ ]));
18
+ cmd.command("list").description("List all profiles").option("--json", "Output as JSON").action(async (options) => {
12
19
  try {
13
20
  const profiles = await listProfiles();
14
21
  if (options.json) {
@@ -16,7 +23,7 @@ function createProfileListCommand() {
16
23
  return;
17
24
  }
18
25
  if (profiles.length === 0) {
19
- console.log("No profiles found. Create one with: xopc profile:create <name>");
26
+ console.log("No profiles found. Create one with: xopc profile create <name>");
20
27
  return;
21
28
  }
22
29
  const table = new Table({
@@ -53,26 +60,20 @@ function createProfileListCommand() {
53
60
  process.exit(1);
54
61
  }
55
62
  });
56
- }
57
- function createProfileCreateCommand() {
58
- return new Command("profile:create").description("Create a new profile").argument("<name>", "Profile name (letters, numbers, hyphens, underscores)").action(async (name) => {
63
+ cmd.command("create").description("Create a new profile").argument("<name>", "Profile name (letters, numbers, hyphens, underscores)").action(async (name) => {
59
64
  try {
60
65
  const profile = await createProfile(name);
61
66
  console.log(colors.green("✓"), `Created profile "${profile.name}"`);
62
67
  console.log(`\n Directory: ${profile.stateDir}`);
63
- console.log(`\nTo use this profile:`);
64
- console.log(` export XOPC_PROFILE=${profile.name}`);
65
- console.log(`\nOr add to your shell profile:`);
66
- console.log(` echo 'export XOPC_PROFILE=${profile.name}' >> ~/.bashrc`);
68
+ console.log("\nTo use this profile:");
69
+ console.log(` ${getSwitchCommand(profile.name)}`);
67
70
  } catch (error) {
68
71
  log.error({ error }, "Failed to create profile");
69
72
  console.error(colors.red("Error:"), error instanceof Error ? error.message : String(error));
70
73
  process.exit(1);
71
74
  }
72
75
  });
73
- }
74
- function createProfileDeleteCommand() {
75
- return new Command("profile:delete").description("Delete a profile").argument("<name>", "Profile name").option("-f, --force", "Force delete even if active").action(async (name, options) => {
76
+ cmd.command("delete").description("Delete a profile").argument("<name>", "Profile name").option("-f, --force", "Force delete even if active").action(async (name, options) => {
76
77
  try {
77
78
  await deleteProfile(name, { force: options.force });
78
79
  console.log(colors.green("✓"), `Deleted profile "${name}"`);
@@ -82,38 +83,37 @@ function createProfileDeleteCommand() {
82
83
  process.exit(1);
83
84
  }
84
85
  });
85
- }
86
- function createProfileSwitchCommand() {
87
- return new Command("profile:switch").description("Get shell command to switch to a profile").argument("<name>", "Profile name").action(async (name) => {
86
+ cmd.command("switch").description("Print shell command to switch to a profile").argument("<name>", "Profile name").action(async (name) => {
88
87
  try {
89
- const stateDir = resolveProfileStateDir(name);
90
88
  if (!(await listProfiles()).some((p) => p.name === name)) {
91
89
  console.error(colors.red("Error:"), `Profile "${name}" not found`);
92
- console.log(`\nCreate it with: xopc profile:create ${name}`);
90
+ console.log(`\nCreate it with: xopc profile create ${name}`);
93
91
  process.exit(1);
94
92
  }
95
93
  console.log(colors.cyan("Run this command to switch to this profile:"));
96
94
  console.log();
97
- console.log(` export XOPC_PROFILE=${name}`);
95
+ console.log(` ${getSwitchCommand(name)}`);
98
96
  console.log();
99
- console.log("Or add to your shell profile:");
100
- console.log(` echo 'export XOPC_PROFILE=${name}' >> ~/.bashrc`);
101
- console.log();
102
- console.log(`State directory: ${stateDir}`);
97
+ console.log(`State directory: ${resolveProfileStateDir(name)}`);
103
98
  } catch (error) {
104
99
  log.error({ error }, "Failed to switch profile");
105
100
  console.error(colors.red("Error:"), error instanceof Error ? error.message : String(error));
106
101
  process.exit(1);
107
102
  }
108
103
  });
104
+ return cmd;
109
105
  }
110
- function registerProfileCommands(program) {
111
- program.addCommand(createProfileListCommand());
112
- program.addCommand(createProfileCreateCommand());
113
- program.addCommand(createProfileDeleteCommand());
114
- program.addCommand(createProfileSwitchCommand());
115
- }
106
+ register({
107
+ id: "profile",
108
+ name: "profile",
109
+ description: "Manage xopc state profiles (~/.xopc vs ~/.xopc-<name>)",
110
+ factory: createProfileCommand,
111
+ metadata: {
112
+ category: "setup",
113
+ examples: ["xopc profile list", "xopc profile create staging"]
114
+ }
115
+ });
116
116
  //#endregion
117
- export { createProfileCreateCommand, createProfileDeleteCommand, createProfileListCommand, createProfileSwitchCommand, registerProfileCommands };
117
+ export { createProfileCommand };
118
118
 
119
119
  //# sourceMappingURL=profile.js.map