@xopcai/xopc 0.0.20 → 0.0.22

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 (197) hide show
  1. package/dist/extensions/feishu/src/adapters/cli-login.d.ts +8 -0
  2. package/dist/extensions/feishu/src/adapters/cli-login.js +225 -0
  3. package/dist/extensions/feishu/src/adapters/cli-login.js.map +1 -0
  4. package/dist/extensions/feishu/src/adapters/onboard-cli.js +1 -105
  5. package/dist/extensions/feishu/src/adapters/onboard-cli.js.map +1 -1
  6. package/dist/extensions/feishu/src/auth/app-registration.d.ts +47 -0
  7. package/dist/extensions/feishu/src/auth/app-registration.js +122 -0
  8. package/dist/extensions/feishu/src/auth/app-registration.js.map +1 -0
  9. package/dist/extensions/feishu/src/plugin.d.ts +2 -0
  10. package/dist/extensions/feishu/src/plugin.js +2 -0
  11. package/dist/extensions/feishu/src/plugin.js.map +1 -1
  12. package/dist/extensions/telegram/src/inbound-processor.js +1 -1
  13. package/dist/extensions/telegram/src/plugin.d.ts +1 -1
  14. package/dist/extensions/telegram/src/plugin.js +1 -1
  15. package/dist/extensions/telegram/src/routing-integration.js +2 -2
  16. package/dist/extensions/telegram/xopc.extension.json +1 -1
  17. package/dist/extensions/weixin/src/plugin.js +1 -1
  18. package/dist/gateway/static/root/assets/{agents-DbLV2ldC.js → agents-BcLv59-r.js} +2 -2
  19. package/dist/gateway/static/root/assets/{agents-DbLV2ldC.js.map → agents-BcLv59-r.js.map} +1 -1
  20. package/dist/gateway/static/root/assets/{apps-page-CDRSbv3l.js → apps-page-Bl-yxbQo.js} +2 -2
  21. package/dist/gateway/static/root/assets/{apps-page-CDRSbv3l.js.map → apps-page-Bl-yxbQo.js.map} +1 -1
  22. package/dist/gateway/static/root/assets/channels-settings-BGueHxMv.js +9 -0
  23. package/dist/gateway/static/root/assets/channels-settings-BGueHxMv.js.map +1 -0
  24. package/dist/gateway/static/root/assets/{cron-page-D-fhl446.js → cron-page-DsVZzPqv.js} +2 -2
  25. package/dist/gateway/static/root/assets/{cron-page-D-fhl446.js.map → cron-page-DsVZzPqv.js.map} +1 -1
  26. package/dist/gateway/static/root/assets/{cron-utils-DqyPqEDr.js → cron-utils-zbRs2yND.js} +2 -2
  27. package/dist/gateway/static/root/assets/{cron-utils-DqyPqEDr.js.map → cron-utils-zbRs2yND.js.map} +1 -1
  28. package/dist/gateway/static/root/assets/{dist-BTNDXpKu.js → dist-CDA7gR_M.js} +2 -2
  29. package/dist/gateway/static/root/assets/{dist-BTNDXpKu.js.map → dist-CDA7gR_M.js.map} +1 -1
  30. package/dist/gateway/static/root/assets/{extension-debug-page-CiOtMG3X.js → extension-debug-page-CDLp4DAs.js} +2 -2
  31. package/dist/gateway/static/root/assets/{extension-debug-page-CiOtMG3X.js.map → extension-debug-page-CDLp4DAs.js.map} +1 -1
  32. package/dist/gateway/static/root/assets/{extension-page-a59AFw7Q.js → extension-page-DwSCjzHO.js} +2 -2
  33. package/dist/gateway/static/root/assets/{extension-page-a59AFw7Q.js.map → extension-page-DwSCjzHO.js.map} +1 -1
  34. package/dist/gateway/static/root/assets/{extension-settings-page-BQyLvxBY.js → extension-settings-page-Rdmxe24_.js} +2 -2
  35. package/dist/gateway/static/root/assets/{extension-settings-page-BQyLvxBY.js.map → extension-settings-page-Rdmxe24_.js.map} +1 -1
  36. package/dist/gateway/static/root/assets/index-D9Wmfh2f.css +1 -0
  37. package/dist/gateway/static/root/assets/index-DG8WvMbu.js +150 -0
  38. package/dist/gateway/static/root/assets/index-DG8WvMbu.js.map +1 -0
  39. package/dist/gateway/static/root/assets/{logs-page-DMSWW0-k.js → logs-page-ChJ0nsPh.js} +2 -2
  40. package/dist/gateway/static/root/assets/{logs-page-DMSWW0-k.js.map → logs-page-ChJ0nsPh.js.map} +1 -1
  41. package/dist/gateway/static/root/assets/{sessions-page-CL2E3nPk.js → sessions-page-Cle4fPla.js} +2 -2
  42. package/dist/gateway/static/root/assets/{sessions-page-CL2E3nPk.js.map → sessions-page-Cle4fPla.js.map} +1 -1
  43. package/dist/gateway/static/root/assets/settings-page-Dyo2NYdy.js +2 -0
  44. package/dist/gateway/static/root/assets/settings-page-Dyo2NYdy.js.map +1 -0
  45. package/dist/gateway/static/root/assets/{skills-page-0rmNu4AL.js → skills-page-B-smhcB2.js} +2 -2
  46. package/dist/gateway/static/root/assets/{skills-page-0rmNu4AL.js.map → skills-page-B-smhcB2.js.map} +1 -1
  47. package/dist/gateway/static/root/index.html +2 -2
  48. package/dist/package.js +1 -1
  49. package/dist/src/agent/agent-manager.js +6 -6
  50. package/dist/src/agent/context/workspace-seed.js +1 -1
  51. package/dist/src/agent/ipc/bus.js +1 -1
  52. package/dist/src/agent/ipc/inbox.js +1 -1
  53. package/dist/src/agent/ipc/socket.js +1 -1
  54. package/dist/src/agent/memory/builtin-memory-store.d.ts +2 -1
  55. package/dist/src/agent/memory/builtin-memory-store.js +7 -6
  56. package/dist/src/agent/memory/builtin-memory-store.js.map +1 -1
  57. package/dist/src/agent/models/manager.js +1 -1
  58. package/dist/src/agent/prompt/memory/index.d.ts +4 -2
  59. package/dist/src/agent/prompt/memory/index.js +22 -10
  60. package/dist/src/agent/prompt/memory/index.js.map +1 -1
  61. package/dist/src/agent/prompt/service-prompt-builder.js +1 -1
  62. package/dist/src/agent/service.js +5 -5
  63. package/dist/src/agent/skills/index.js +1 -1
  64. package/dist/src/agent/skills/scanner.js +1 -1
  65. package/dist/src/agent/skills/skill-manage-ops.js +1 -1
  66. package/dist/src/agent/skills/skill-manager.js +1 -1
  67. package/dist/src/agent/tools/factory.js +10 -3
  68. package/dist/src/agent/tools/factory.js.map +1 -1
  69. package/dist/src/agent/tools/index.d.ts +1 -1
  70. package/dist/src/agent/tools/memory-tool.d.ts +7 -2
  71. package/dist/src/agent/tools/memory-tool.js +11 -5
  72. package/dist/src/agent/tools/memory-tool.js.map +1 -1
  73. package/dist/src/agent/tools/send-media.js +1 -1
  74. package/dist/src/agent/tools/skill-manage-tool.js +1 -1
  75. package/dist/src/agent/tools/write.js +1 -1
  76. package/dist/src/auth/credentials.js +2 -2
  77. package/dist/src/auth/sync-provider-auth.js +1 -1
  78. package/dist/src/channels/attachments/inbound-persist.js +1 -1
  79. package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
  80. package/dist/src/channels/registry.d.ts +1 -1
  81. package/dist/src/channels/registry.js +25 -1
  82. package/dist/src/channels/registry.js.map +1 -1
  83. package/dist/src/chat-commands/builtins/config.js +3 -3
  84. package/dist/src/chat-commands/builtins/session.js +1 -1
  85. package/dist/src/chat-commands/context.js +1 -1
  86. package/dist/src/chat-commands/index.js +1 -1
  87. package/dist/src/chat-commands/processor.js +1 -1
  88. package/dist/src/cli/commands/agent.js +1 -1
  89. package/dist/src/cli/commands/channels.js +20 -2
  90. package/dist/src/cli/commands/channels.js.map +1 -1
  91. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  92. package/dist/src/cli/commands/gateway/call.d.ts +2 -0
  93. package/dist/src/cli/commands/gateway/call.js +90 -0
  94. package/dist/src/cli/commands/gateway/call.js.map +1 -0
  95. package/dist/src/cli/commands/gateway/health.d.ts +2 -0
  96. package/dist/src/cli/commands/gateway/health.js +77 -0
  97. package/dist/src/cli/commands/gateway/health.js.map +1 -0
  98. package/dist/src/cli/commands/gateway/index.d.ts +3 -0
  99. package/dist/src/cli/commands/gateway/index.js +4 -1
  100. package/dist/src/cli/commands/gateway/probe.d.ts +2 -0
  101. package/dist/src/cli/commands/gateway/probe.js +102 -0
  102. package/dist/src/cli/commands/gateway/probe.js.map +1 -0
  103. package/dist/src/cli/commands/gateway/status.d.ts +0 -3
  104. package/dist/src/cli/commands/gateway/status.js +107 -24
  105. package/dist/src/cli/commands/gateway/status.js.map +1 -1
  106. package/dist/src/cli/commands/gateway.js +7 -1
  107. package/dist/src/cli/commands/gateway.js.map +1 -1
  108. package/dist/src/cli/commands/init.js +3 -3
  109. package/dist/src/cli/commands/update.js +19 -1
  110. package/dist/src/cli/commands/update.js.map +1 -1
  111. package/dist/src/cli/utils/gateway-client.d.ts +28 -0
  112. package/dist/src/cli/utils/gateway-client.js +115 -0
  113. package/dist/src/cli/utils/gateway-client.js.map +1 -0
  114. package/dist/src/config/index.js +2 -2
  115. package/dist/src/config/loader.js +1 -1
  116. package/dist/src/config/models-json.js +1 -1
  117. package/dist/src/config/paths-state.d.ts +4 -0
  118. package/dist/src/config/paths-state.js +9 -1
  119. package/dist/src/config/paths-state.js.map +1 -1
  120. package/dist/src/config/profile.js +2 -2
  121. package/dist/src/config/reload.d.ts +2 -0
  122. package/dist/src/config/reload.js +9 -1
  123. package/dist/src/config/reload.js.map +1 -1
  124. package/dist/src/config/rules.js +12 -2
  125. package/dist/src/config/rules.js.map +1 -1
  126. package/dist/src/cron/executor.js +2 -2
  127. package/dist/src/cron/persistence.js +1 -1
  128. package/dist/src/cron/run-log-store.js +1 -1
  129. package/dist/src/extensions/api.d.ts +6 -1
  130. package/dist/src/extensions/api.js +52 -1
  131. package/dist/src/extensions/api.js.map +1 -1
  132. package/dist/src/extensions/health.js +1 -1
  133. package/dist/src/extensions/loader.d.ts +6 -1
  134. package/dist/src/extensions/loader.js +21 -2
  135. package/dist/src/extensions/loader.js.map +1 -1
  136. package/dist/src/extensions/lockfile.js +1 -1
  137. package/dist/src/extensions/normalize-manifest.js +33 -0
  138. package/dist/src/extensions/normalize-manifest.js.map +1 -1
  139. package/dist/src/extensions/sdk/index.d.ts +1 -1
  140. package/dist/src/extensions/sdk/index.js.map +1 -1
  141. package/dist/src/extensions/types/core.d.ts +35 -1
  142. package/dist/src/extensions/types/manifest.d.ts +14 -0
  143. package/dist/src/gateway/agents-admin.js +1 -1
  144. package/dist/src/gateway/hono/lib/config-payload.d.ts +3 -0
  145. package/dist/src/gateway/hono/lib/config-payload.js +1 -0
  146. package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
  147. package/dist/src/gateway/hono/oauth.js +1 -1
  148. package/dist/src/gateway/hono/routes/channels.js +111 -0
  149. package/dist/src/gateway/hono/routes/channels.js.map +1 -1
  150. package/dist/src/gateway/hono/routes/commands-skills.js +13 -2
  151. package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
  152. package/dist/src/gateway/hono/routes/config.js +82 -1
  153. package/dist/src/gateway/hono/routes/config.js.map +1 -1
  154. package/dist/src/gateway/hono/routes/public-gateway.js +17 -0
  155. package/dist/src/gateway/hono/routes/public-gateway.js.map +1 -1
  156. package/dist/src/gateway/hono/routes/sessions.js +16 -0
  157. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  158. package/dist/src/gateway/hono/routes/status.js +31 -7
  159. package/dist/src/gateway/hono/routes/status.js.map +1 -1
  160. package/dist/src/gateway/hono/routes/update.js +118 -15
  161. package/dist/src/gateway/hono/routes/update.js.map +1 -1
  162. package/dist/src/gateway/hono/routes/workspace.js +2 -2
  163. package/dist/src/gateway/hono/sse.js +2 -2
  164. package/dist/src/gateway/index.js +1 -1
  165. package/dist/src/gateway/server.js +3 -0
  166. package/dist/src/gateway/server.js.map +1 -1
  167. package/dist/src/gateway/service.d.ts +23 -0
  168. package/dist/src/gateway/service.js +111 -4
  169. package/dist/src/gateway/service.js.map +1 -1
  170. package/dist/src/gateway/workspace-heartbeat-path.js +1 -1
  171. package/dist/src/infra/update-check.js +54 -21
  172. package/dist/src/infra/update-check.js.map +1 -1
  173. package/dist/src/infra/update-lock.d.ts +13 -0
  174. package/dist/src/infra/update-lock.js +67 -0
  175. package/dist/src/infra/update-lock.js.map +1 -0
  176. package/dist/src/infra/update-runner.d.ts +6 -5
  177. package/dist/src/infra/update-runner.js +93 -13
  178. package/dist/src/infra/update-runner.js.map +1 -1
  179. package/dist/src/infra/update-startup.js +37 -11
  180. package/dist/src/infra/update-startup.js.map +1 -1
  181. package/dist/src/providers/index.js +2 -2
  182. package/dist/src/providers/model-registry.js +1 -1
  183. package/dist/src/session/config-store.js +1 -1
  184. package/dist/src/session/session-title.js +1 -1
  185. package/dist/src/session/store.js +3 -3
  186. package/dist/src/utils/logger/audit.js +1 -1
  187. package/dist/src/utils/logger/log-store.js +1 -1
  188. package/dist/src/utils/logger/rotation.js +1 -1
  189. package/dist/src/voice/tts/audio.js +1 -1
  190. package/package.json +1 -1
  191. package/dist/gateway/static/root/assets/channels-settings-DyNnMN1-.js +0 -9
  192. package/dist/gateway/static/root/assets/channels-settings-DyNnMN1-.js.map +0 -1
  193. package/dist/gateway/static/root/assets/index-BQNdJlkw.css +0 -1
  194. package/dist/gateway/static/root/assets/index-fGYWcYhm.js +0 -144
  195. package/dist/gateway/static/root/assets/index-fGYWcYhm.js.map +0 -1
  196. package/dist/gateway/static/root/assets/settings-page-CSIVMAJE.js +0 -2
  197. package/dist/gateway/static/root/assets/settings-page-CSIVMAJE.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"memory-tool.js","names":[],"sources":["../../../../src/agent/tools/memory-tool.ts"],"sourcesContent":["// Memory search tools for xopc agent\nimport { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@mariozechner/pi-agent-core';\nimport { memorySearch, memoryGet } from '../prompt/memory/index.js';\n\n// =============================================================================\n// Memory Search Tool\n// =============================================================================\nconst MemorySearchSchema = Type.Object({\n query: Type.String(),\n maxResults: Type.Optional(Type.Number()),\n minScore: Type.Optional(Type.Number()),\n});\n\ntype MemorySearchParams = { query: string; maxResults?: number; minScore?: number };\n\nexport function createMemorySearchTool(workspaceDir: string): AgentTool {\n return {\n name: 'memory_search',\n label: '🔍 Memory Search',\n description:\n 'Mandatory recall step: semantically search bootstrap MEMORY.md, agent-home `memories/*.md`, and workspace `memory/*.md` before answering questions about prior work, decisions, dates, people, preferences, or todos; returns top snippets with path + lines.',\n parameters: MemorySearchSchema,\n\n async execute(\n _toolCallId: string,\n params: any,\n _signal?: AbortSignal,\n ): Promise<AgentToolResult<{}>> {\n const { query, maxResults } = params as MemorySearchParams;\n\n try {\n const results = await memorySearch(workspaceDir, query, { maxResults });\n const withCitations = results.map(entry => ({\n ...entry,\n citation: `${entry.file}#L${entry.lineNumbers[0]}${entry.lineNumbers.length > 1 ? `-L${entry.lineNumbers[entry.lineNumbers.length - 1]}` : ''}`,\n snippet: `${entry.lines.trim()}\\n\\nSource: ${entry.file}#L${entry.lineNumbers[0]}${entry.lineNumbers.length > 1 ? `-L${entry.lineNumbers[entry.lineNumbers.length - 1]}` : ''}`,\n }));\n\n return {\n content: [{ type: 'text', text: JSON.stringify({ results: withCitations, provider: 'simple' }, null, 2) }],\n details: { results: withCitations },\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n content: [{ type: 'text', text: `Search error: ${message}` }],\n details: { error: message },\n };\n }\n },\n } as any;\n}\n\n// =============================================================================\n// Memory Get Tool\n// =============================================================================\nconst MemoryGetSchema = Type.Object({\n path: Type.String(),\n from: Type.Optional(Type.Number()),\n lines: Type.Optional(Type.Number()),\n});\n\ntype MemoryGetParams = { path: string; from?: number; lines?: number };\n\nexport function createMemoryGetTool(workspaceDir: string): AgentTool {\n return {\n name: 'memory_get',\n label: '📄 Memory Get',\n description: 'Safe snippet read from MEMORY.md or memory/*.md with optional from/lines; use after memory_search to pull only the needed lines and keep context small.',\n parameters: MemoryGetSchema,\n\n async execute(\n _toolCallId: string,\n params: any,\n _signal?: AbortSignal,\n ): Promise<AgentToolResult<{}>> {\n const { path, from, lines } = params as MemoryGetParams;\n\n try {\n const result = memoryGet(workspaceDir, path, from, lines);\n if (!result) {\n return {\n content: [{ type: 'text', text: `File not found: ${path}` }],\n details: { path, text: '' },\n };\n }\n return {\n content: [{ type: 'text', text: result.content }],\n details: { path, text: result.content, lineNumbers: result.lineNumbers },\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n content: [{ type: 'text', text: `Read error: ${message}` }],\n details: { error: message },\n };\n }\n },\n } as any;\n}\n"],"mappings":";;;AAQA,MAAM,qBAAqB,KAAK,OAAO;CACrC,OAAO,KAAK,QAAQ;CACpB,YAAY,KAAK,SAAS,KAAK,QAAQ,CAAC;CACxC,UAAU,KAAK,SAAS,KAAK,QAAQ,CAAC;CACvC,CAAC;AAIF,SAAgB,uBAAuB,cAAiC;AACtE,QAAO;EACL,MAAM;EACN,OAAO;EACP,aACE;EACF,YAAY;EAEZ,MAAM,QACJ,aACA,QACA,SAC8B;GAC9B,MAAM,EAAE,OAAO,eAAe;AAE9B,OAAI;IAEF,MAAM,iBAAgB,MADA,aAAa,cAAc,OAAO,EAAE,YAAY,CAAC,EACzC,KAAI,WAAU;KAC1C,GAAG;KACH,UAAU,GAAG,MAAM,KAAK,IAAI,MAAM,YAAY,KAAK,MAAM,YAAY,SAAS,IAAI,KAAK,MAAM,YAAY,MAAM,YAAY,SAAS,OAAO;KAC3I,SAAS,GAAG,MAAM,MAAM,MAAM,CAAC,cAAc,MAAM,KAAK,IAAI,MAAM,YAAY,KAAK,MAAM,YAAY,SAAS,IAAI,KAAK,MAAM,YAAY,MAAM,YAAY,SAAS,OAAO;KAC5K,EAAE;AAEH,WAAO;KACL,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,KAAK,UAAU;OAAE,SAAS;OAAe,UAAU;OAAU,EAAE,MAAM,EAAE;MAAE,CAAC;KAC1G,SAAS,EAAE,SAAS,eAAe;KACpC;YACM,KAAK;IACZ,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAChE,WAAO;KACL,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,iBAAiB;MAAW,CAAC;KAC7D,SAAS,EAAE,OAAO,SAAS;KAC5B;;;EAGN;;AAMH,MAAM,kBAAkB,KAAK,OAAO;CAClC,MAAM,KAAK,QAAQ;CACnB,MAAM,KAAK,SAAS,KAAK,QAAQ,CAAC;CAClC,OAAO,KAAK,SAAS,KAAK,QAAQ,CAAC;CACpC,CAAC;AAIF,SAAgB,oBAAoB,cAAiC;AACnE,QAAO;EACL,MAAM;EACN,OAAO;EACP,aAAa;EACb,YAAY;EAEZ,MAAM,QACJ,aACA,QACA,SAC8B;GAC9B,MAAM,EAAE,MAAM,MAAM,UAAU;AAE9B,OAAI;IACF,MAAM,SAAS,UAAU,cAAc,MAAM,MAAM,MAAM;AACzD,QAAI,CAAC,OACH,QAAO;KACL,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,mBAAmB;MAAQ,CAAC;KAC5D,SAAS;MAAE;MAAM,MAAM;MAAI;KAC5B;AAEH,WAAO;KACL,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,OAAO;MAAS,CAAC;KACjD,SAAS;MAAE;MAAM,MAAM,OAAO;MAAS,aAAa,OAAO;MAAa;KACzE;YACM,KAAK;IACZ,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAChE,WAAO;KACL,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,eAAe;MAAW,CAAC;KAC3D,SAAS,EAAE,OAAO,SAAS;KAC5B;;;EAGN"}
1
+ {"version":3,"file":"memory-tool.js","names":[],"sources":["../../../../src/agent/tools/memory-tool.ts"],"sourcesContent":["// Memory search tools for xopc agent\nimport { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@mariozechner/pi-agent-core';\nimport { memorySearch, memoryGet } from '../prompt/memory/index.js';\n\n// =============================================================================\n// Memory Search Tool\n// =============================================================================\nconst MemorySearchSchema = Type.Object({\n query: Type.String(),\n maxResults: Type.Optional(Type.Number()),\n minScore: Type.Optional(Type.Number()),\n});\n\ntype MemorySearchParams = { query: string; maxResults?: number; minScore?: number };\n\nexport interface MemoryToolOptions {\n workspaceDir: string;\n /** Agent home curated memories dir, e.g. ~/.xopc/agents/<id>/memories/ */\n memoriesDir?: string;\n}\n\nexport function createMemorySearchTool(options: MemoryToolOptions): AgentTool {\n const { workspaceDir, memoriesDir } = options;\n return {\n name: 'memory_search',\n label: '🔍 Memory Search',\n description:\n 'Mandatory recall step: semantically search bootstrap MEMORY.md, agent-home `memories/*.md`, and workspace `memory/*.md` before answering questions about prior work, decisions, dates, people, preferences, or todos; returns top snippets with path + lines.',\n parameters: MemorySearchSchema,\n\n async execute(\n _toolCallId: string,\n params: any,\n _signal?: AbortSignal,\n ): Promise<AgentToolResult<{}>> {\n const { query, maxResults, minScore } = params as MemorySearchParams;\n\n try {\n const results = await memorySearch(workspaceDir, query, { maxResults, minScore, memoriesDir });\n const withCitations = results.map((entry) => ({\n ...entry,\n citation: `${entry.file}#L${entry.lineNumbers[0]}${entry.lineNumbers.length > 1 ? `-L${entry.lineNumbers[entry.lineNumbers.length - 1]}` : ''}`,\n snippet: `${entry.lines.trim()}\\n\\nSource: ${entry.file}#L${entry.lineNumbers[0]}${entry.lineNumbers.length > 1 ? `-L${entry.lineNumbers[entry.lineNumbers.length - 1]}` : ''}`,\n }));\n\n return {\n content: [{ type: 'text', text: JSON.stringify({ results: withCitations, provider: 'simple' }, null, 2) }],\n details: { results: withCitations },\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n content: [{ type: 'text', text: `Search error: ${message}` }],\n details: { error: message },\n };\n }\n },\n } as any;\n}\n\n// =============================================================================\n// Memory Get Tool\n// =============================================================================\nconst MemoryGetSchema = Type.Object({\n path: Type.String(),\n from: Type.Optional(Type.Number()),\n lines: Type.Optional(Type.Number()),\n});\n\ntype MemoryGetParams = { path: string; from?: number; lines?: number };\n\nexport function createMemoryGetTool(options: MemoryToolOptions): AgentTool {\n const { workspaceDir, memoriesDir } = options;\n return {\n name: 'memory_get',\n label: '📄 Memory Get',\n description: 'Safe snippet read from MEMORY.md or memory/*.md with optional from/lines; use after memory_search to pull only the needed lines and keep context small.',\n parameters: MemoryGetSchema,\n\n async execute(\n _toolCallId: string,\n params: any,\n _signal?: AbortSignal,\n ): Promise<AgentToolResult<{}>> {\n const { path, from, lines } = params as MemoryGetParams;\n\n try {\n const result = memoryGet(workspaceDir, path, from, lines, memoriesDir);\n if (!result) {\n return {\n content: [{ type: 'text', text: `File not found: ${path}` }],\n details: { path, text: '' },\n };\n }\n return {\n content: [{ type: 'text', text: result.content }],\n details: { path, text: result.content, lineNumbers: result.lineNumbers },\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n content: [{ type: 'text', text: `Read error: ${message}` }],\n details: { error: message },\n };\n }\n },\n } as any;\n}\n"],"mappings":";;;AAQA,MAAM,qBAAqB,KAAK,OAAO;CACrC,OAAO,KAAK,QAAQ;CACpB,YAAY,KAAK,SAAS,KAAK,QAAQ,CAAC;CACxC,UAAU,KAAK,SAAS,KAAK,QAAQ,CAAC;CACvC,CAAC;AAUF,SAAgB,uBAAuB,SAAuC;CAC5E,MAAM,EAAE,cAAc,gBAAgB;AACtC,QAAO;EACL,MAAM;EACN,OAAO;EACP,aACE;EACF,YAAY;EAEZ,MAAM,QACJ,aACA,QACA,SAC8B;GAC9B,MAAM,EAAE,OAAO,YAAY,aAAa;AAExC,OAAI;IAEF,MAAM,iBAAgB,MADA,aAAa,cAAc,OAAO;KAAE;KAAY;KAAU;KAAa,CAAC,EAChE,KAAK,WAAW;KAC5C,GAAG;KACH,UAAU,GAAG,MAAM,KAAK,IAAI,MAAM,YAAY,KAAK,MAAM,YAAY,SAAS,IAAI,KAAK,MAAM,YAAY,MAAM,YAAY,SAAS,OAAO;KAC3I,SAAS,GAAG,MAAM,MAAM,MAAM,CAAC,cAAc,MAAM,KAAK,IAAI,MAAM,YAAY,KAAK,MAAM,YAAY,SAAS,IAAI,KAAK,MAAM,YAAY,MAAM,YAAY,SAAS,OAAO;KAC5K,EAAE;AAEH,WAAO;KACL,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,KAAK,UAAU;OAAE,SAAS;OAAe,UAAU;OAAU,EAAE,MAAM,EAAE;MAAE,CAAC;KAC1G,SAAS,EAAE,SAAS,eAAe;KACpC;YACM,KAAK;IACZ,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAChE,WAAO;KACL,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,iBAAiB;MAAW,CAAC;KAC7D,SAAS,EAAE,OAAO,SAAS;KAC5B;;;EAGN;;AAMH,MAAM,kBAAkB,KAAK,OAAO;CAClC,MAAM,KAAK,QAAQ;CACnB,MAAM,KAAK,SAAS,KAAK,QAAQ,CAAC;CAClC,OAAO,KAAK,SAAS,KAAK,QAAQ,CAAC;CACpC,CAAC;AAIF,SAAgB,oBAAoB,SAAuC;CACzE,MAAM,EAAE,cAAc,gBAAgB;AACtC,QAAO;EACL,MAAM;EACN,OAAO;EACP,aAAa;EACb,YAAY;EAEZ,MAAM,QACJ,aACA,QACA,SAC8B;GAC9B,MAAM,EAAE,MAAM,MAAM,UAAU;AAE9B,OAAI;IACF,MAAM,SAAS,UAAU,cAAc,MAAM,MAAM,OAAO,YAAY;AACtE,QAAI,CAAC,OACH,QAAO;KACL,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,mBAAmB;MAAQ,CAAC;KAC5D,SAAS;MAAE;MAAM,MAAM;MAAI;KAC5B;AAEH,WAAO;KACL,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,OAAO;MAAS,CAAC;KACjD,SAAS;MAAE;MAAM,MAAM,OAAO;MAAS,aAAa,OAAO;MAAa;KACzE;YACM,KAAK;IACZ,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAChE,WAAO;KACL,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,eAAe;MAAW,CAAC;KAC3D,SAAS,EAAE,OAAO,SAAS;KAC5B;;;EAGN"}
@@ -1,7 +1,7 @@
1
1
  import { checkFileSafety } from "../prompt/safety.js";
2
2
  import { resolvePathUnderWorkspace } from "./tool-paths.js";
3
- import { readFile } from "fs/promises";
4
3
  import { basename } from "node:path";
4
+ import { readFile } from "fs/promises";
5
5
  import { Type } from "@sinclair/typebox";
6
6
  //#region src/agent/tools/send-media.ts
7
7
  const SendMediaSchema = Type.Object({
@@ -2,9 +2,9 @@ import { resolveStateDir } from "../../config/paths-state.js";
2
2
  import { init_paths } from "../../config/paths.js";
3
3
  import { createSkillConfigManager } from "../skills/config.js";
4
4
  import { applyPatchToContent, atomicWriteUtf8, effectiveAgentWritePolicy, ensureCategorySegment, isPathInsideDir, maxSkillMdChars, maxSupportFileBytes, mutatableSkillOrNull, resolveCreateSkillDir, scanSkillDirOrError, validateSkillMdContent, validateSkillNameSegment, validateSupportingRelativePath } from "../skills/skill-manage-ops.js";
5
- import { readFile, rm } from "fs/promises";
6
5
  import { join } from "path";
7
6
  import { existsSync, rmSync } from "fs";
7
+ import { readFile, rm } from "fs/promises";
8
8
  import { Type } from "@sinclair/typebox";
9
9
  //#region src/agent/tools/skill-manage-tool.ts
10
10
  init_paths();
@@ -1,7 +1,7 @@
1
1
  import { checkFileSafety } from "../prompt/safety.js";
2
2
  import { resolvePathUnderWorkspace } from "./tool-paths.js";
3
- import { mkdir, writeFile } from "fs/promises";
4
3
  import { dirname } from "path";
4
+ import { mkdir, writeFile } from "fs/promises";
5
5
  import { Type } from "@sinclair/typebox";
6
6
  //#region src/agent/tools/write.ts
7
7
  const MAX_FILE_SIZE = 10 * 1024 * 1024;
@@ -1,10 +1,10 @@
1
1
  import { __esmMin } from "../../_virtual/_rolldown/runtime.js";
2
2
  import { createLogger } from "../utils/logger/index.js";
3
3
  import { init_logger } from "../utils/logger.js";
4
- import { getApiKeyFromEnv, init_env_keys } from "../providers/env-keys.js";
5
4
  import { init_paths, resolveAgentAuthProfilesPath, resolveAuthProfilesPath, resolveCredentialsDir, resolveOAuthPath } from "../config/paths.js";
6
- import { mkdir, readFile, writeFile } from "fs/promises";
5
+ import { getApiKeyFromEnv, init_env_keys } from "../providers/env-keys.js";
7
6
  import { dirname, join } from "path";
7
+ import { mkdir, readFile, writeFile } from "fs/promises";
8
8
  //#region src/auth/credentials.ts
9
9
  function getCredentialResolver(options) {
10
10
  return new CredentialResolver(options);
@@ -1,7 +1,7 @@
1
1
  import { __esmMin } from "../../_virtual/_rolldown/runtime.js";
2
+ import { getDefaultAgentId, init_resolve_route } from "../routing/resolve-route.js";
2
3
  import { init_paths, resolveAgentAuthProfilesPath, resolveAuthProfilesPath, resolveOAuthPath } from "../config/paths.js";
3
4
  import { init_loader, loadConfig } from "../config/loader.js";
4
- import { getDefaultAgentId, init_resolve_route } from "../routing/resolve-route.js";
5
5
  import { existsSync, readFileSync } from "node:fs";
6
6
  //#region src/auth/sync-provider-auth.ts
7
7
  /**
@@ -1,7 +1,7 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
- import { mkdir, writeFile } from "fs/promises";
4
3
  import { join, resolve } from "path";
4
+ import { mkdir, writeFile } from "fs/promises";
5
5
  import { randomBytes } from "crypto";
6
6
  //#region src/channels/attachments/inbound-persist.ts
7
7
  /**
@@ -1,7 +1,7 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
- import { mkdir, writeFile } from "fs/promises";
4
3
  import { join, resolve } from "path";
4
+ import { mkdir, writeFile } from "fs/promises";
5
5
  import { randomBytes } from "crypto";
6
6
  //#region src/channels/attachments/outbound-tts-persist.ts
7
7
  /**
@@ -3,7 +3,7 @@
3
3
  * Extension-provided channels register separately; keep protocol IDs aligned.
4
4
  */
5
5
  import type { ChannelCapabilities } from './plugin-types.js';
6
- export declare const CHAT_CHANNEL_ORDER: readonly ["telegram", "weixin"];
6
+ export declare const CHAT_CHANNEL_ORDER: readonly ["telegram", "weixin", "feishu"];
7
7
  export type ChatChannelId = (typeof CHAT_CHANNEL_ORDER)[number];
8
8
  export interface ChatChannelMeta {
9
9
  id: ChatChannelId;
@@ -1,5 +1,9 @@
1
1
  //#region src/channels/registry.ts
2
- const CHAT_CHANNEL_ORDER = ["telegram", "weixin"];
2
+ const CHAT_CHANNEL_ORDER = [
3
+ "telegram",
4
+ "weixin",
5
+ "feishu"
6
+ ];
3
7
  const CHAT_CHANNEL_META = {
4
8
  telegram: {
5
9
  id: "telegram",
@@ -41,6 +45,26 @@ const CHAT_CHANNEL_META = {
41
45
  outbound: { textChunkLimit: 4e3 },
42
46
  queue: { debounceMs: 0 }
43
47
  }
48
+ },
49
+ feishu: {
50
+ id: "feishu",
51
+ label: "Feishu",
52
+ description: "Feishu/Lark enterprise messaging (Socket Mode)",
53
+ capabilities: {
54
+ chatTypes: ["direct", "channel"],
55
+ reactions: true,
56
+ threads: true,
57
+ media: true,
58
+ polls: false,
59
+ nativeCommands: false,
60
+ blockStreaming: false,
61
+ edit: true,
62
+ reply: true
63
+ },
64
+ dock: {
65
+ outbound: { textChunkLimit: 4e3 },
66
+ queue: { debounceMs: 0 }
67
+ }
44
68
  }
45
69
  };
46
70
  function getChatChannelMeta(id) {
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","names":[],"sources":["../../../src/channels/registry.ts"],"sourcesContent":["/**\n * Built-in chat channel registry: stable ordering and UI-facing metadata.\n * Extension-provided channels register separately; keep protocol IDs aligned.\n */\n\nimport type { ChannelCapabilities } from './plugin-types.js';\n\nexport const CHAT_CHANNEL_ORDER = ['telegram', 'weixin'] as const;\n\nexport type ChatChannelId = (typeof CHAT_CHANNEL_ORDER)[number];\n\nexport interface ChatChannelMeta {\n id: ChatChannelId;\n label: string;\n description: string;\n capabilities: ChannelCapabilities;\n /** Optional UI/dock tuning (queue debounce, outbound chunk size). */\n dock?: {\n outbound?: { textChunkLimit?: number };\n queue?: { debounceMs?: number };\n };\n}\n\nconst DEFAULT_CAPABILITIES: ChannelCapabilities = {\n chatTypes: ['direct', 'group', 'channel', 'thread'],\n reactions: true,\n threads: true,\n media: true,\n polls: false,\n nativeCommands: true,\n blockStreaming: true,\n};\n\nconst WEIXIN_CAPABILITIES: ChannelCapabilities = {\n chatTypes: ['direct'],\n reactions: false,\n threads: false,\n media: true,\n polls: false,\n nativeCommands: false,\n blockStreaming: true,\n};\n\nconst CHAT_CHANNEL_META: Record<ChatChannelId, ChatChannelMeta> = {\n telegram: {\n id: 'telegram',\n label: 'Telegram',\n description: 'Telegram Bot API (GrammY)',\n capabilities: DEFAULT_CAPABILITIES,\n dock: {\n outbound: { textChunkLimit: 4000 },\n queue: { debounceMs: 300 },\n },\n },\n weixin: {\n id: 'weixin',\n label: 'Weixin',\n description: 'WeChat via Tencent ilink (QR login)',\n capabilities: WEIXIN_CAPABILITIES,\n dock: {\n outbound: { textChunkLimit: 4000 },\n queue: { debounceMs: 0 },\n },\n },\n};\n\nexport function getChatChannelMeta(id: ChatChannelId): ChatChannelMeta {\n return CHAT_CHANNEL_META[id];\n}\n\nexport function isChatChannelId(id: string): id is ChatChannelId {\n return (CHAT_CHANNEL_ORDER as readonly string[]).includes(id);\n}\n\nexport function listChatChannelMeta(): ChatChannelMeta[] {\n return CHAT_CHANNEL_ORDER.map((id) => CHAT_CHANNEL_META[id]);\n}\n"],"mappings":";AAOA,MAAa,qBAAqB,CAAC,YAAY,SAAS;AAoCxD,MAAM,oBAA4D;CAChE,UAAU;EACR,IAAI;EACJ,OAAO;EACP,aAAa;EACb,cAAc;GAxBhB,WAAW;IAAC;IAAU;IAAS;IAAW;IAAS;GACnD,WAAW;GACX,SAAS;GACT,OAAO;GACP,OAAO;GACP,gBAAgB;GAChB,gBAAgB;GAkBoB;EAClC,MAAM;GACJ,UAAU,EAAE,gBAAgB,KAAM;GAClC,OAAO,EAAE,YAAY,KAAK;GAC3B;EACF;CACD,QAAQ;EACN,IAAI;EACJ,OAAO;EACP,aAAa;EACb,cAAc;GAxBhB,WAAW,CAAC,SAAS;GACrB,WAAW;GACX,SAAS;GACT,OAAO;GACP,OAAO;GACP,gBAAgB;GAChB,gBAAgB;GAkBmB;EACjC,MAAM;GACJ,UAAU,EAAE,gBAAgB,KAAM;GAClC,OAAO,EAAE,YAAY,GAAG;GACzB;EACF;CACF;AAED,SAAgB,mBAAmB,IAAoC;AACrE,QAAO,kBAAkB;;AAG3B,SAAgB,gBAAgB,IAAiC;AAC/D,QAAQ,mBAAyC,SAAS,GAAG;;AAG/D,SAAgB,sBAAyC;AACvD,QAAO,mBAAmB,KAAK,OAAO,kBAAkB,IAAI"}
1
+ {"version":3,"file":"registry.js","names":[],"sources":["../../../src/channels/registry.ts"],"sourcesContent":["/**\n * Built-in chat channel registry: stable ordering and UI-facing metadata.\n * Extension-provided channels register separately; keep protocol IDs aligned.\n */\n\nimport type { ChannelCapabilities } from './plugin-types.js';\n\nexport const CHAT_CHANNEL_ORDER = ['telegram', 'weixin', 'feishu'] as const;\n\nexport type ChatChannelId = (typeof CHAT_CHANNEL_ORDER)[number];\n\nexport interface ChatChannelMeta {\n id: ChatChannelId;\n label: string;\n description: string;\n capabilities: ChannelCapabilities;\n /** Optional UI/dock tuning (queue debounce, outbound chunk size). */\n dock?: {\n outbound?: { textChunkLimit?: number };\n queue?: { debounceMs?: number };\n };\n}\n\nconst DEFAULT_CAPABILITIES: ChannelCapabilities = {\n chatTypes: ['direct', 'group', 'channel', 'thread'],\n reactions: true,\n threads: true,\n media: true,\n polls: false,\n nativeCommands: true,\n blockStreaming: true,\n};\n\nconst WEIXIN_CAPABILITIES: ChannelCapabilities = {\n chatTypes: ['direct'],\n reactions: false,\n threads: false,\n media: true,\n polls: false,\n nativeCommands: false,\n blockStreaming: true,\n};\n\nconst FEISHU_CAPABILITIES: ChannelCapabilities = {\n chatTypes: ['direct', 'channel'],\n reactions: true,\n threads: true,\n media: true,\n polls: false,\n nativeCommands: false,\n blockStreaming: false,\n edit: true,\n reply: true,\n};\n\nconst CHAT_CHANNEL_META: Record<ChatChannelId, ChatChannelMeta> = {\n telegram: {\n id: 'telegram',\n label: 'Telegram',\n description: 'Telegram Bot API (GrammY)',\n capabilities: DEFAULT_CAPABILITIES,\n dock: {\n outbound: { textChunkLimit: 4000 },\n queue: { debounceMs: 300 },\n },\n },\n weixin: {\n id: 'weixin',\n label: 'Weixin',\n description: 'WeChat via Tencent ilink (QR login)',\n capabilities: WEIXIN_CAPABILITIES,\n dock: {\n outbound: { textChunkLimit: 4000 },\n queue: { debounceMs: 0 },\n },\n },\n feishu: {\n id: 'feishu',\n label: 'Feishu',\n description: 'Feishu/Lark enterprise messaging (Socket Mode)',\n capabilities: FEISHU_CAPABILITIES,\n dock: {\n outbound: { textChunkLimit: 4000 },\n queue: { debounceMs: 0 },\n },\n },\n};\n\nexport function getChatChannelMeta(id: ChatChannelId): ChatChannelMeta {\n return CHAT_CHANNEL_META[id];\n}\n\nexport function isChatChannelId(id: string): id is ChatChannelId {\n return (CHAT_CHANNEL_ORDER as readonly string[]).includes(id);\n}\n\nexport function listChatChannelMeta(): ChatChannelMeta[] {\n return CHAT_CHANNEL_ORDER.map((id) => CHAT_CHANNEL_META[id]);\n}\n"],"mappings":";AAOA,MAAa,qBAAqB;CAAC;CAAY;CAAU;CAAS;AAgDlE,MAAM,oBAA4D;CAChE,UAAU;EACR,IAAI;EACJ,OAAO;EACP,aAAa;EACb,cAAc;GApChB,WAAW;IAAC;IAAU;IAAS;IAAW;IAAS;GACnD,WAAW;GACX,SAAS;GACT,OAAO;GACP,OAAO;GACP,gBAAgB;GAChB,gBAAgB;GA8BoB;EAClC,MAAM;GACJ,UAAU,EAAE,gBAAgB,KAAM;GAClC,OAAO,EAAE,YAAY,KAAK;GAC3B;EACF;CACD,QAAQ;EACN,IAAI;EACJ,OAAO;EACP,aAAa;EACb,cAAc;GApChB,WAAW,CAAC,SAAS;GACrB,WAAW;GACX,SAAS;GACT,OAAO;GACP,OAAO;GACP,gBAAgB;GAChB,gBAAgB;GA8BmB;EACjC,MAAM;GACJ,UAAU,EAAE,gBAAgB,KAAM;GAClC,OAAO,EAAE,YAAY,GAAG;GACzB;EACF;CACD,QAAQ;EACN,IAAI;EACJ,OAAO;EACP,aAAa;EACb,cAAc;GApChB,WAAW,CAAC,UAAU,UAAU;GAChC,WAAW;GACX,SAAS;GACT,OAAO;GACP,OAAO;GACP,gBAAgB;GAChB,gBAAgB;GAChB,MAAM;GACN,OAAO;GA4B4B;EACjC,MAAM;GACJ,UAAU,EAAE,gBAAgB,KAAM;GAClC,OAAO,EAAE,YAAY,GAAG;GACzB;EACF;CACF;AAED,SAAgB,mBAAmB,IAAoC;AACrE,QAAO,kBAAkB;;AAG3B,SAAgB,gBAAgB,IAAiC;AAC/D,QAAQ,mBAAyC,SAAS,GAAG;;AAG/D,SAAgB,sBAAyC;AACvD,QAAO,mBAAmB,KAAK,OAAO,kBAAkB,IAAI"}
@@ -1,11 +1,11 @@
1
+ import { ConfigSchema, init_schema } from "../../config/schema.js";
2
+ import { init_session_key, parseSessionKey } from "../../routing/session-key.js";
1
3
  import { createLogger } from "../../utils/logger/index.js";
2
4
  import { init_logger } from "../../utils/logger.js";
3
5
  import { init_paths, resolveConfigPath } from "../../config/paths.js";
4
- import { ConfigSchema, init_schema } from "../../config/schema.js";
5
6
  import { init_loader, loadConfig, saveConfig } from "../../config/loader.js";
6
- import { init_session_key, parseSessionKey } from "../../routing/session-key.js";
7
- import { resolveAllowlistMatchSimple } from "../../channels/security.js";
8
7
  import { commandRegistry } from "../registry.js";
8
+ import { resolveAllowlistMatchSimple } from "../../channels/security.js";
9
9
  import { parseConfigValue } from "../config-value.js";
10
10
  import { getConfigValueAtPath, parseConfigPath, setConfigValueAtPath, unsetConfigValueAtPath } from "../config-paths.js";
11
11
  //#region src/chat-commands/builtins/config.ts
@@ -1,5 +1,5 @@
1
- import { getSessionDisplayName } from "../session-key.js";
2
1
  import { commandRegistry } from "../registry.js";
2
+ import { getSessionDisplayName } from "../session-key.js";
3
3
  //#region src/chat-commands/builtins/session.ts
4
4
  const newCommand = {
5
5
  id: "session.new",
@@ -4,8 +4,8 @@ import { init_loader, saveConfig } from "../config/loader.js";
4
4
  import { effectiveWorkspacePathForSession } from "../session/session-workspace.js";
5
5
  import { getRoutingInfo, getSessionDisplayName } from "./session-key.js";
6
6
  import { wrapMarkdownExportAsHtml } from "../session/chat-export.js";
7
- import { mkdir, writeFile } from "fs/promises";
8
7
  import { join } from "path";
8
+ import { mkdir, writeFile } from "fs/promises";
9
9
  //#region src/chat-commands/context.ts
10
10
  init_logger();
11
11
  init_loader();
@@ -1,6 +1,6 @@
1
- import { generateSessionKey, getRoutingInfo, getSessionDisplayName, isValidSessionKey, parseSessionKey } from "./session-key.js";
2
1
  import { normalizeTelegramCommandName, parseSlashCommand } from "./command-parse.js";
3
2
  import { CommandRegistry, commandRegistry } from "./registry.js";
3
+ import { generateSessionKey, getRoutingInfo, getSessionDisplayName, isValidSessionKey, parseSessionKey } from "./session-key.js";
4
4
  import { registerSessionCommands } from "./builtins/session.js";
5
5
  import { registerModelCommands } from "./builtins/model.js";
6
6
  import { registerSystemCommands } from "./builtins/system.js";
@@ -1,7 +1,7 @@
1
1
  import { createLogger } from "../utils/logger/index.js";
2
2
  import { init_logger } from "../utils/logger.js";
3
- import { getRoutingInfo } from "./session-key.js";
4
3
  import { commandRegistry } from "./registry.js";
4
+ import { getRoutingInfo } from "./session-key.js";
5
5
  import { createCommandContext } from "./context.js";
6
6
  //#region src/chat-commands/processor.ts
7
7
  init_logger();
@@ -1,6 +1,6 @@
1
+ import { getWorkspacePath } from "../../config/schema.js";
1
2
  import { createLogger } from "../../utils/logger/index.js";
2
3
  import { init_logger } from "../../utils/logger.js";
3
- import { getWorkspacePath } from "../../config/schema.js";
4
4
  import { loadConfig } from "../../config/loader.js";
5
5
  import { MessageBus, MessageBusShutdownError } from "../../infra/bus/queue.js";
6
6
  import "../../infra/bus/index.js";
@@ -16,11 +16,29 @@ function createChannelsCommand(ctx) {
16
16
  const cmd = new Command("channels").description("Messaging channel login and credentials").addHelpText("after", formatExamples([
17
17
  "xopc channels login",
18
18
  "xopc channels login --channel weixin",
19
+ "xopc channels login --channel feishu",
19
20
  "xopc channels login --account my-bot-id"
20
21
  ]));
21
- cmd.command("login").description("Log in with QR code or channel-specific credentials flow").option("--channel <id>", "Channel id", "weixin").option("--account <id>", "Optional account id when re-logging an existing bot").option("--timeout <ms>", "Max wait for scan (default 480000)", "480000").option("--credentials-only", "Only save token files; do not update xopc.json").action(async (options, command) => {
22
+ cmd.command("login").description("Log in with QR code or channel-specific credentials flow").option("--channel <id>", "Channel id (auto-detected when only one login-capable channel is registered)").option("--account <id>", "Optional account id when re-logging an existing bot").option("--timeout <ms>", "Max wait for scan (default 480000)", "480000").option("--credentials-only", "Only save token files; do not update xopc.json").action(async (options, command) => {
22
23
  ensureChannelRegistryForCli();
23
- const channelId = String(options.channel || "").trim() || "weixin";
24
+ const explicitChannel = options.channel?.trim?.();
25
+ let channelId;
26
+ if (explicitChannel) channelId = explicitChannel;
27
+ else {
28
+ const loginCapable = listChannelPlugins().filter((p) => p.cliLogin);
29
+ if (loginCapable.length === 1) {
30
+ channelId = loginCapable[0].id;
31
+ console.log(`Auto-detected channel: ${channelId}`);
32
+ } else if (loginCapable.length === 0) {
33
+ console.error("No channels with login support found.");
34
+ process.exitCode = 1;
35
+ return;
36
+ } else {
37
+ console.error(`Multiple channels support login: ${loginCapable.map((p) => p.id).join(", ")}. Use --channel <id> to specify.`);
38
+ process.exitCode = 1;
39
+ return;
40
+ }
41
+ }
24
42
  const plugin = getChannelPlugin(channelId);
25
43
  if (!plugin?.cliLogin) {
26
44
  console.error(`Channel "${channelId}" does not support CLI login.`);
@@ -1 +1 @@
1
- {"version":3,"file":"channels.js","names":[],"sources":["../../../../src/cli/commands/channels.ts"],"sourcesContent":["import { Command } from 'commander';\n\nimport { resolveConfigPath } from '../../config/paths.js';\nimport {\n getChannelPlugin,\n listChannelPlugins,\n syncChannelPluginsFromManager,\n} from '../../channels/plugins/registry.js';\nimport { bundledChannelPlugins } from '../../generated/bundled-channel-plugins.js';\nimport { register, formatExamples, type CLIContext } from '../registry.js';\n\nfunction ensureChannelRegistryForCli(): void {\n if (listChannelPlugins().length === 0) {\n syncChannelPluginsFromManager(bundledChannelPlugins);\n }\n}\n\nfunction resolveConfigPathFromCommand(command: Command): string {\n const root =\n command.parent?.parent && command.parent.parent instanceof Command\n ? command.parent.parent\n : command.parent && command.parent instanceof Command\n ? command.parent\n : null;\n const globalOpts = (root && typeof root.opts === 'function'\n ? (root.opts() as { config?: string })\n : {}) as { config?: string };\n return (\n globalOpts.config?.trim() ||\n process.env.XOPC_CONFIG_PATH?.trim() ||\n process.env.XOPC_CONFIG?.trim() ||\n resolveConfigPath()\n );\n}\n\nfunction createChannelsCommand(ctx: CLIContext): Command {\n const cmd = new Command('channels')\n .description('Messaging channel login and credentials')\n .addHelpText(\n 'after',\n formatExamples([\n 'xopc channels login',\n 'xopc channels login --channel weixin',\n 'xopc channels login --account my-bot-id',\n ]),\n );\n\n cmd\n .command('login')\n .description('Log in with QR code or channel-specific credentials flow')\n .option('--channel <id>', 'Channel id', 'weixin')\n .option('--account <id>', 'Optional account id when re-logging an existing bot')\n .option('--timeout <ms>', 'Max wait for scan (default 480000)', '480000')\n .option('--credentials-only', 'Only save token files; do not update xopc.json')\n .action(async (options, command) => {\n ensureChannelRegistryForCli();\n const channelId = String(options.channel || '').trim() || 'weixin';\n const plugin = getChannelPlugin(channelId);\n if (!plugin?.cliLogin) {\n console.error(`Channel \"${channelId}\" does not support CLI login.`);\n const capable = listChannelPlugins()\n .filter((p) => p.cliLogin)\n .map((p) => p.id);\n if (capable.length > 0) {\n console.error(`Channels with login support: ${capable.join(', ')}`);\n }\n process.exitCode = 1;\n return;\n }\n\n const configPath = resolveConfigPathFromCommand(command);\n const timeoutMs = Math.max(60_000, Number.parseInt(String(options.timeout), 10) || 480_000);\n const verbose = ctx.isVerbose;\n\n const result = await plugin.cliLogin.runLogin({\n configPath,\n verbose,\n timeoutMs,\n accountId: options.account?.trim() || undefined,\n writeConfig: !options.credentialsOnly,\n });\n\n if (!result.ok) {\n console.error(result.message || 'Login failed');\n process.exitCode = 1;\n }\n });\n\n return cmd;\n}\n\nregister({\n id: 'channels',\n name: 'channels',\n description: 'Messaging channel login',\n factory: createChannelsCommand,\n metadata: {\n category: 'setup',\n examples: [\n 'xopc channels login',\n 'xopc channels login --account my-account-id',\n ],\n },\n});\n"],"mappings":";;;;;;YAE0D;AAS1D,SAAS,8BAAoC;AAC3C,KAAI,oBAAoB,CAAC,WAAW,EAClC,+BAA8B,sBAAsB;;AAIxD,SAAS,6BAA6B,SAA0B;CAC9D,MAAM,OACJ,QAAQ,QAAQ,UAAU,QAAQ,OAAO,kBAAkB,UACvD,QAAQ,OAAO,SACf,QAAQ,UAAU,QAAQ,kBAAkB,UAC1C,QAAQ,SACR;AAIR,SAHoB,QAAQ,OAAO,KAAK,SAAS,aAC5C,KAAK,MAAM,GACZ,EAAE,EAEO,QAAQ,MAAM,IACzB,QAAQ,IAAI,kBAAkB,MAAM,IACpC,QAAQ,IAAI,aAAa,MAAM,IAC/B,mBAAmB;;AAIvB,SAAS,sBAAsB,KAA0B;CACvD,MAAM,MAAM,IAAI,QAAQ,WAAW,CAChC,YAAY,0CAA0C,CACtD,YACC,SACA,eAAe;EACb;EACA;EACA;EACD,CAAC,CACH;AAEH,KACG,QAAQ,QAAQ,CAChB,YAAY,2DAA2D,CACvE,OAAO,kBAAkB,cAAc,SAAS,CAChD,OAAO,kBAAkB,sDAAsD,CAC/E,OAAO,kBAAkB,sCAAsC,SAAS,CACxE,OAAO,sBAAsB,iDAAiD,CAC9E,OAAO,OAAO,SAAS,YAAY;AAClC,+BAA6B;EAC7B,MAAM,YAAY,OAAO,QAAQ,WAAW,GAAG,CAAC,MAAM,IAAI;EAC1D,MAAM,SAAS,iBAAiB,UAAU;AAC1C,MAAI,CAAC,QAAQ,UAAU;AACrB,WAAQ,MAAM,YAAY,UAAU,+BAA+B;GACnE,MAAM,UAAU,oBAAoB,CACjC,QAAQ,MAAM,EAAE,SAAS,CACzB,KAAK,MAAM,EAAE,GAAG;AACnB,OAAI,QAAQ,SAAS,EACnB,SAAQ,MAAM,gCAAgC,QAAQ,KAAK,KAAK,GAAG;AAErE,WAAQ,WAAW;AACnB;;EAGF,MAAM,aAAa,6BAA6B,QAAQ;EACxD,MAAM,YAAY,KAAK,IAAI,KAAQ,OAAO,SAAS,OAAO,QAAQ,QAAQ,EAAE,GAAG,IAAI,KAAQ;EAC3F,MAAM,UAAU,IAAI;EAEpB,MAAM,SAAS,MAAM,OAAO,SAAS,SAAS;GAC5C;GACA;GACA;GACA,WAAW,QAAQ,SAAS,MAAM,IAAI,KAAA;GACtC,aAAa,CAAC,QAAQ;GACvB,CAAC;AAEF,MAAI,CAAC,OAAO,IAAI;AACd,WAAQ,MAAM,OAAO,WAAW,eAAe;AAC/C,WAAQ,WAAW;;GAErB;AAEJ,QAAO;;AAGT,SAAS;CACP,IAAI;CACJ,MAAM;CACN,aAAa;CACb,SAAS;CACT,UAAU;EACR,UAAU;EACV,UAAU,CACR,uBACA,8CACD;EACF;CACF,CAAC"}
1
+ {"version":3,"file":"channels.js","names":[],"sources":["../../../../src/cli/commands/channels.ts"],"sourcesContent":["import { Command } from 'commander';\n\nimport { resolveConfigPath } from '../../config/paths.js';\nimport {\n getChannelPlugin,\n listChannelPlugins,\n syncChannelPluginsFromManager,\n} from '../../channels/plugins/registry.js';\nimport { bundledChannelPlugins } from '../../generated/bundled-channel-plugins.js';\nimport { register, formatExamples, type CLIContext } from '../registry.js';\n\nfunction ensureChannelRegistryForCli(): void {\n if (listChannelPlugins().length === 0) {\n syncChannelPluginsFromManager(bundledChannelPlugins);\n }\n}\n\nfunction resolveConfigPathFromCommand(command: Command): string {\n const root =\n command.parent?.parent && command.parent.parent instanceof Command\n ? command.parent.parent\n : command.parent && command.parent instanceof Command\n ? command.parent\n : null;\n const globalOpts = (root && typeof root.opts === 'function'\n ? (root.opts() as { config?: string })\n : {}) as { config?: string };\n return (\n globalOpts.config?.trim() ||\n process.env.XOPC_CONFIG_PATH?.trim() ||\n process.env.XOPC_CONFIG?.trim() ||\n resolveConfigPath()\n );\n}\n\nfunction createChannelsCommand(ctx: CLIContext): Command {\n const cmd = new Command('channels')\n .description('Messaging channel login and credentials')\n .addHelpText(\n 'after',\n formatExamples([\n 'xopc channels login',\n 'xopc channels login --channel weixin',\n 'xopc channels login --channel feishu',\n 'xopc channels login --account my-bot-id',\n ]),\n );\n\n cmd\n .command('login')\n .description('Log in with QR code or channel-specific credentials flow')\n .option(\n '--channel <id>',\n 'Channel id (auto-detected when only one login-capable channel is registered)',\n )\n .option('--account <id>', 'Optional account id when re-logging an existing bot')\n .option('--timeout <ms>', 'Max wait for scan (default 480000)', '480000')\n .option('--credentials-only', 'Only save token files; do not update xopc.json')\n .action(async (options, command) => {\n ensureChannelRegistryForCli();\n const explicitChannel = options.channel?.trim?.();\n let channelId: string;\n if (explicitChannel) {\n channelId = explicitChannel;\n } else {\n const loginCapable = listChannelPlugins().filter((p) => p.cliLogin);\n if (loginCapable.length === 1) {\n channelId = loginCapable[0].id;\n console.log(`Auto-detected channel: ${channelId}`);\n } else if (loginCapable.length === 0) {\n console.error('No channels with login support found.');\n process.exitCode = 1;\n return;\n } else {\n console.error(\n `Multiple channels support login: ${loginCapable.map((p) => p.id).join(', ')}. ` +\n 'Use --channel <id> to specify.',\n );\n process.exitCode = 1;\n return;\n }\n }\n const plugin = getChannelPlugin(channelId);\n if (!plugin?.cliLogin) {\n console.error(`Channel \"${channelId}\" does not support CLI login.`);\n const capable = listChannelPlugins()\n .filter((p) => p.cliLogin)\n .map((p) => p.id);\n if (capable.length > 0) {\n console.error(`Channels with login support: ${capable.join(', ')}`);\n }\n process.exitCode = 1;\n return;\n }\n\n const configPath = resolveConfigPathFromCommand(command);\n const timeoutMs = Math.max(60_000, Number.parseInt(String(options.timeout), 10) || 480_000);\n const verbose = ctx.isVerbose;\n\n const result = await plugin.cliLogin.runLogin({\n configPath,\n verbose,\n timeoutMs,\n accountId: options.account?.trim() || undefined,\n writeConfig: !options.credentialsOnly,\n });\n\n if (!result.ok) {\n console.error(result.message || 'Login failed');\n process.exitCode = 1;\n }\n });\n\n return cmd;\n}\n\nregister({\n id: 'channels',\n name: 'channels',\n description: 'Messaging channel login',\n factory: createChannelsCommand,\n metadata: {\n category: 'setup',\n examples: [\n 'xopc channels login',\n 'xopc channels login --account my-account-id',\n ],\n },\n});\n"],"mappings":";;;;;;YAE0D;AAS1D,SAAS,8BAAoC;AAC3C,KAAI,oBAAoB,CAAC,WAAW,EAClC,+BAA8B,sBAAsB;;AAIxD,SAAS,6BAA6B,SAA0B;CAC9D,MAAM,OACJ,QAAQ,QAAQ,UAAU,QAAQ,OAAO,kBAAkB,UACvD,QAAQ,OAAO,SACf,QAAQ,UAAU,QAAQ,kBAAkB,UAC1C,QAAQ,SACR;AAIR,SAHoB,QAAQ,OAAO,KAAK,SAAS,aAC5C,KAAK,MAAM,GACZ,EAAE,EAEO,QAAQ,MAAM,IACzB,QAAQ,IAAI,kBAAkB,MAAM,IACpC,QAAQ,IAAI,aAAa,MAAM,IAC/B,mBAAmB;;AAIvB,SAAS,sBAAsB,KAA0B;CACvD,MAAM,MAAM,IAAI,QAAQ,WAAW,CAChC,YAAY,0CAA0C,CACtD,YACC,SACA,eAAe;EACb;EACA;EACA;EACA;EACD,CAAC,CACH;AAEH,KACG,QAAQ,QAAQ,CAChB,YAAY,2DAA2D,CACvE,OACC,kBACA,+EACD,CACA,OAAO,kBAAkB,sDAAsD,CAC/E,OAAO,kBAAkB,sCAAsC,SAAS,CACxE,OAAO,sBAAsB,iDAAiD,CAC9E,OAAO,OAAO,SAAS,YAAY;AAClC,+BAA6B;EAC7B,MAAM,kBAAkB,QAAQ,SAAS,QAAQ;EACjD,IAAI;AACJ,MAAI,gBACF,aAAY;OACP;GACL,MAAM,eAAe,oBAAoB,CAAC,QAAQ,MAAM,EAAE,SAAS;AACnE,OAAI,aAAa,WAAW,GAAG;AAC7B,gBAAY,aAAa,GAAG;AAC5B,YAAQ,IAAI,0BAA0B,YAAY;cACzC,aAAa,WAAW,GAAG;AACpC,YAAQ,MAAM,wCAAwC;AACtD,YAAQ,WAAW;AACnB;UACK;AACL,YAAQ,MACN,oCAAoC,aAAa,KAAK,MAAM,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC,kCAE9E;AACD,YAAQ,WAAW;AACnB;;;EAGJ,MAAM,SAAS,iBAAiB,UAAU;AAC1C,MAAI,CAAC,QAAQ,UAAU;AACrB,WAAQ,MAAM,YAAY,UAAU,+BAA+B;GACnE,MAAM,UAAU,oBAAoB,CACjC,QAAQ,MAAM,EAAE,SAAS,CACzB,KAAK,MAAM,EAAE,GAAG;AACnB,OAAI,QAAQ,SAAS,EACnB,SAAQ,MAAM,gCAAgC,QAAQ,KAAK,KAAK,GAAG;AAErE,WAAQ,WAAW;AACnB;;EAGF,MAAM,aAAa,6BAA6B,QAAQ;EACxD,MAAM,YAAY,KAAK,IAAI,KAAQ,OAAO,SAAS,OAAO,QAAQ,QAAQ,EAAE,GAAG,IAAI,KAAQ;EAC3F,MAAM,UAAU,IAAI;EAEpB,MAAM,SAAS,MAAM,OAAO,SAAS,SAAS;GAC5C;GACA;GACA;GACA,WAAW,QAAQ,SAAS,MAAM,IAAI,KAAA;GACtC,aAAa,CAAC,QAAQ;GACvB,CAAC;AAEF,MAAI,CAAC,OAAO,IAAI;AACd,WAAQ,MAAM,OAAO,WAAW,eAAe;AAC/C,WAAQ,WAAW;;GAErB;AAEJ,QAAO;;AAGT,SAAS;CACP,IAAI;CACJ,MAAM;CACN,aAAa;CACb,SAAS;CACT,UAAU;EACR,UAAU;EACV,UAAU,CACR,uBACA,8CACD;EACF;CACF,CAAC"}
@@ -1,6 +1,6 @@
1
- import { PROVIDER_ENV_MAP, getApiKeyFromEnv, init_env_keys } from "../../../../providers/env-keys.js";
2
1
  import { getAgentDefaultModelRef, init_schema, parseModelRef } from "../../../../config/schema.js";
3
2
  import { init_loader, loadConfig } from "../../../../config/loader.js";
3
+ import { PROVIDER_ENV_MAP, getApiKeyFromEnv, init_env_keys } from "../../../../providers/env-keys.js";
4
4
  import { existsSync } from "node:fs";
5
5
  //#region src/cli/commands/doctor/checks/provider-auth.ts
6
6
  init_loader();
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function createCallCommand(): Command;
@@ -0,0 +1,90 @@
1
+ import { init_paths, resolveConfigPath } from "../../../config/paths.js";
2
+ import { addGatewayClientOptions, callGatewayApi, parseGatewayClientOptions } from "../../utils/gateway-client.js";
3
+ import { getContextWithOpts } from "../../index.js";
4
+ import { Command } from "commander";
5
+ //#region src/cli/commands/gateway/call.ts
6
+ init_paths();
7
+ const METHOD_ALIASES = {
8
+ health: {
9
+ method: "GET",
10
+ path: "/api/health"
11
+ },
12
+ status: {
13
+ method: "GET",
14
+ path: "/api/status"
15
+ },
16
+ config: {
17
+ method: "GET",
18
+ path: "/api/config"
19
+ },
20
+ sessions: {
21
+ method: "GET",
22
+ path: "/api/sessions"
23
+ },
24
+ models: {
25
+ method: "GET",
26
+ path: "/api/models"
27
+ },
28
+ channels: {
29
+ method: "GET",
30
+ path: "/api/channels/status"
31
+ },
32
+ cron: {
33
+ method: "GET",
34
+ path: "/api/cron"
35
+ },
36
+ logs: {
37
+ method: "GET",
38
+ path: "/api/logs"
39
+ },
40
+ agents: {
41
+ method: "GET",
42
+ path: "/api/agents"
43
+ }
44
+ };
45
+ function createCallCommand() {
46
+ const cmd = new Command("call").description("Call a gateway API method").argument("<method>", `Method name or API path. Built-in aliases: ${Object.keys(METHOD_ALIASES).join(", ")}`).option("--params <json>", "JSON body for POST/PATCH/DELETE requests", "{}").option("--http-method <method>", "HTTP method when using a raw path", "GET");
47
+ addGatewayClientOptions(cmd);
48
+ cmd.action(async (methodArg, options) => {
49
+ const configPath = getContextWithOpts().configPath || resolveConfigPath();
50
+ const clientOpts = {
51
+ ...parseGatewayClientOptions(options),
52
+ configPath
53
+ };
54
+ const alias = METHOD_ALIASES[methodArg.toLowerCase()];
55
+ let httpMethod;
56
+ let apiPath;
57
+ if (alias) {
58
+ httpMethod = alias.method;
59
+ apiPath = alias.path;
60
+ } else if (methodArg.startsWith("/")) {
61
+ httpMethod = options.httpMethod?.toUpperCase() ?? "GET";
62
+ apiPath = methodArg;
63
+ } else {
64
+ httpMethod = options.httpMethod?.toUpperCase() ?? "GET";
65
+ apiPath = `/api/${methodArg}`;
66
+ }
67
+ let body;
68
+ if (httpMethod !== "GET" && options.params && options.params !== "{}") try {
69
+ body = JSON.parse(options.params);
70
+ } catch {
71
+ console.error(`❌ Invalid JSON in --params: ${options.params}`);
72
+ process.exit(1);
73
+ }
74
+ const result = await callGatewayApi(httpMethod, apiPath, clientOpts, body);
75
+ if (clientOpts.json || result.ok) console.log(JSON.stringify(result.ok ? result.data : {
76
+ error: result.error,
77
+ status: result.status,
78
+ durationMs: result.durationMs
79
+ }, null, 2));
80
+ if (!result.ok) {
81
+ if (!clientOpts.json) console.error(`❌ Gateway call failed: ${result.error} (status ${result.status}, ${result.durationMs}ms)`);
82
+ process.exit(1);
83
+ }
84
+ });
85
+ return cmd;
86
+ }
87
+ //#endregion
88
+ export { createCallCommand };
89
+
90
+ //# sourceMappingURL=call.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"call.js","names":[],"sources":["../../../../../src/cli/commands/gateway/call.ts"],"sourcesContent":["import { Command } from 'commander';\n\nimport {\n callGatewayApi,\n addGatewayClientOptions,\n parseGatewayClientOptions,\n} from '../../utils/gateway-client.js';\nimport { getContextWithOpts } from '../../index.js';\nimport { resolveConfigPath } from '../../../config/paths.js';\n\nconst METHOD_ALIASES: Record<string, { method: 'GET' | 'POST'; path: string }> = {\n health: { method: 'GET', path: '/api/health' },\n status: { method: 'GET', path: '/api/status' },\n config: { method: 'GET', path: '/api/config' },\n sessions: { method: 'GET', path: '/api/sessions' },\n models: { method: 'GET', path: '/api/models' },\n channels: { method: 'GET', path: '/api/channels/status' },\n cron: { method: 'GET', path: '/api/cron' },\n logs: { method: 'GET', path: '/api/logs' },\n agents: { method: 'GET', path: '/api/agents' },\n};\n\nexport function createCallCommand(): Command {\n const cmd = new Command('call')\n .description('Call a gateway API method')\n .argument(\n '<method>',\n `Method name or API path. Built-in aliases: ${Object.keys(METHOD_ALIASES).join(', ')}`,\n )\n .option('--params <json>', 'JSON body for POST/PATCH/DELETE requests', '{}')\n .option('--http-method <method>', 'HTTP method when using a raw path', 'GET');\n\n addGatewayClientOptions(cmd);\n\n cmd.action(async (methodArg: string, options: { params?: string; httpMethod?: string }) => {\n const ctx = getContextWithOpts();\n const configPath = ctx.configPath || resolveConfigPath();\n const clientOpts = { ...parseGatewayClientOptions(options as Record<string, unknown>), configPath };\n\n const alias = METHOD_ALIASES[methodArg.toLowerCase()];\n let httpMethod: 'GET' | 'POST' | 'PATCH' | 'DELETE';\n let apiPath: string;\n\n if (alias) {\n httpMethod = alias.method;\n apiPath = alias.path;\n } else if (methodArg.startsWith('/')) {\n httpMethod = (options.httpMethod?.toUpperCase() ?? 'GET') as 'GET' | 'POST' | 'PATCH' | 'DELETE';\n apiPath = methodArg;\n } else {\n httpMethod = (options.httpMethod?.toUpperCase() ?? 'GET') as 'GET' | 'POST' | 'PATCH' | 'DELETE';\n apiPath = `/api/${methodArg}`;\n }\n\n let body: unknown | undefined;\n if (httpMethod !== 'GET' && options.params && options.params !== '{}') {\n try {\n body = JSON.parse(options.params);\n } catch {\n console.error(`❌ Invalid JSON in --params: ${options.params}`);\n process.exit(1);\n }\n }\n\n const result = await callGatewayApi(httpMethod, apiPath, clientOpts, body);\n\n if (clientOpts.json || result.ok) {\n console.log(\n JSON.stringify(\n result.ok\n ? result.data\n : { error: result.error, status: result.status, durationMs: result.durationMs },\n null,\n 2,\n ),\n );\n }\n\n if (!result.ok) {\n if (!clientOpts.json) {\n console.error(`❌ Gateway call failed: ${result.error} (status ${result.status}, ${result.durationMs}ms)`);\n }\n process.exit(1);\n }\n });\n\n return cmd;\n}\n"],"mappings":";;;;;YAQ6D;AAE7D,MAAM,iBAA2E;CAC/E,QAAQ;EAAE,QAAQ;EAAO,MAAM;EAAe;CAC9C,QAAQ;EAAE,QAAQ;EAAO,MAAM;EAAe;CAC9C,QAAQ;EAAE,QAAQ;EAAO,MAAM;EAAe;CAC9C,UAAU;EAAE,QAAQ;EAAO,MAAM;EAAiB;CAClD,QAAQ;EAAE,QAAQ;EAAO,MAAM;EAAe;CAC9C,UAAU;EAAE,QAAQ;EAAO,MAAM;EAAwB;CACzD,MAAM;EAAE,QAAQ;EAAO,MAAM;EAAa;CAC1C,MAAM;EAAE,QAAQ;EAAO,MAAM;EAAa;CAC1C,QAAQ;EAAE,QAAQ;EAAO,MAAM;EAAe;CAC/C;AAED,SAAgB,oBAA6B;CAC3C,MAAM,MAAM,IAAI,QAAQ,OAAO,CAC5B,YAAY,4BAA4B,CACxC,SACC,YACA,8CAA8C,OAAO,KAAK,eAAe,CAAC,KAAK,KAAK,GACrF,CACA,OAAO,mBAAmB,4CAA4C,KAAK,CAC3E,OAAO,0BAA0B,qCAAqC,MAAM;AAE/E,yBAAwB,IAAI;AAE5B,KAAI,OAAO,OAAO,WAAmB,YAAsD;EAEzF,MAAM,aADM,oBACU,CAAC,cAAc,mBAAmB;EACxD,MAAM,aAAa;GAAE,GAAG,0BAA0B,QAAmC;GAAE;GAAY;EAEnG,MAAM,QAAQ,eAAe,UAAU,aAAa;EACpD,IAAI;EACJ,IAAI;AAEJ,MAAI,OAAO;AACT,gBAAa,MAAM;AACnB,aAAU,MAAM;aACP,UAAU,WAAW,IAAI,EAAE;AACpC,gBAAc,QAAQ,YAAY,aAAa,IAAI;AACnD,aAAU;SACL;AACL,gBAAc,QAAQ,YAAY,aAAa,IAAI;AACnD,aAAU,QAAQ;;EAGpB,IAAI;AACJ,MAAI,eAAe,SAAS,QAAQ,UAAU,QAAQ,WAAW,KAC/D,KAAI;AACF,UAAO,KAAK,MAAM,QAAQ,OAAO;UAC3B;AACN,WAAQ,MAAM,+BAA+B,QAAQ,SAAS;AAC9D,WAAQ,KAAK,EAAE;;EAInB,MAAM,SAAS,MAAM,eAAe,YAAY,SAAS,YAAY,KAAK;AAE1E,MAAI,WAAW,QAAQ,OAAO,GAC5B,SAAQ,IACN,KAAK,UACH,OAAO,KACH,OAAO,OACP;GAAE,OAAO,OAAO;GAAO,QAAQ,OAAO;GAAQ,YAAY,OAAO;GAAY,EACjF,MACA,EACD,CACF;AAGH,MAAI,CAAC,OAAO,IAAI;AACd,OAAI,CAAC,WAAW,KACd,SAAQ,MAAM,0BAA0B,OAAO,MAAM,WAAW,OAAO,OAAO,IAAI,OAAO,WAAW,KAAK;AAE3G,WAAQ,KAAK,EAAE;;GAEjB;AAEF,QAAO"}
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function createHealthCommand(): Command;
@@ -0,0 +1,77 @@
1
+ import { init_paths, resolveConfigPath } from "../../../config/paths.js";
2
+ import { addGatewayClientOptions, callGatewayApi, parseGatewayClientOptions } from "../../utils/gateway-client.js";
3
+ import { getContextWithOpts } from "../../index.js";
4
+ import { Command } from "commander";
5
+ //#region src/cli/commands/gateway/health.ts
6
+ init_paths();
7
+ function formatUptime(seconds) {
8
+ if (!seconds || seconds <= 0) return "unknown";
9
+ const days = Math.floor(seconds / 86400);
10
+ const hours = Math.floor(seconds % 86400 / 3600);
11
+ const minutes = Math.floor(seconds % 3600 / 60);
12
+ const parts = [];
13
+ if (days > 0) parts.push(`${days}d`);
14
+ if (hours > 0) parts.push(`${hours}h`);
15
+ parts.push(`${minutes}m`);
16
+ return parts.join(" ");
17
+ }
18
+ function createHealthCommand() {
19
+ const cmd = new Command("health").description("Check gateway health and channel status");
20
+ addGatewayClientOptions(cmd);
21
+ cmd.action(async (options) => {
22
+ const configPath = getContextWithOpts().configPath || resolveConfigPath();
23
+ const clientOpts = {
24
+ ...parseGatewayClientOptions(options),
25
+ configPath
26
+ };
27
+ const healthResult = await callGatewayApi("GET", "/api/health", {
28
+ ...clientOpts,
29
+ timeoutMs: clientOpts.timeoutMs ?? 5e3
30
+ });
31
+ if (!healthResult.ok) {
32
+ if (clientOpts.json) console.log(JSON.stringify({
33
+ status: "unreachable",
34
+ error: healthResult.error,
35
+ durationMs: healthResult.durationMs
36
+ }, null, 2));
37
+ else {
38
+ console.error(`❌ Gateway unreachable: ${healthResult.error}`);
39
+ console.error("");
40
+ console.error("💡 Is the gateway running? Try: xopc gateway");
41
+ }
42
+ process.exit(1);
43
+ }
44
+ const statusResult = await callGatewayApi("GET", "/api/status", clientOpts);
45
+ if (clientOpts.json) {
46
+ console.log(JSON.stringify({
47
+ status: "ok",
48
+ durationMs: healthResult.durationMs,
49
+ health: healthResult.data,
50
+ ...statusResult.ok ? { details: statusResult.data } : {}
51
+ }, null, 2));
52
+ process.exit(0);
53
+ }
54
+ console.log(`✅ Gateway Health: OK (${healthResult.durationMs}ms)`);
55
+ console.log("");
56
+ if (healthResult.data?.version) console.log(` Version: ${healthResult.data.version}`);
57
+ if (healthResult.data?.uptime != null) console.log(` Uptime: ${formatUptime(healthResult.data.uptime)}`);
58
+ if (statusResult.ok && statusResult.data?.channels) {
59
+ console.log("");
60
+ console.log("📡 Channels:");
61
+ for (const [name, info] of Object.entries(statusResult.data.channels)) {
62
+ const statusIcon = info.status === "connected" ? "✅" : info.status === "disabled" ? "⚪" : "❌";
63
+ const accountsLabel = info.accounts != null ? ` (${info.accounts} account(s))` : "";
64
+ console.log(` ${statusIcon} ${name}: ${info.status}${accountsLabel}`);
65
+ }
66
+ } else if (statusResult.status === 401) {
67
+ console.log("");
68
+ console.log("🔒 Detailed status requires authentication. Pass --token <token>.");
69
+ }
70
+ process.exit(0);
71
+ });
72
+ return cmd;
73
+ }
74
+ //#endregion
75
+ export { createHealthCommand };
76
+
77
+ //# sourceMappingURL=health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.js","names":[],"sources":["../../../../../src/cli/commands/gateway/health.ts"],"sourcesContent":["import { Command } from 'commander';\n\nimport {\n callGatewayApi,\n addGatewayClientOptions,\n parseGatewayClientOptions,\n} from '../../utils/gateway-client.js';\nimport { getContextWithOpts } from '../../index.js';\nimport { resolveConfigPath } from '../../../config/paths.js';\n\ninterface HealthResponse {\n status: string;\n version?: string;\n uptime?: number;\n}\n\ninterface StatusResponse {\n status: string;\n version?: string;\n channels?: Record<string, { status: string; accounts?: number }>;\n uptime?: number;\n}\n\nfunction formatUptime(seconds?: number): string {\n if (!seconds || seconds <= 0) return 'unknown';\n const days = Math.floor(seconds / 86400);\n const hours = Math.floor((seconds % 86400) / 3600);\n const minutes = Math.floor((seconds % 3600) / 60);\n const parts: string[] = [];\n if (days > 0) parts.push(`${days}d`);\n if (hours > 0) parts.push(`${hours}h`);\n parts.push(`${minutes}m`);\n return parts.join(' ');\n}\n\nexport function createHealthCommand(): Command {\n const cmd = new Command('health').description('Check gateway health and channel status');\n\n addGatewayClientOptions(cmd);\n\n cmd.action(async (options) => {\n const ctx = getContextWithOpts();\n const configPath = ctx.configPath || resolveConfigPath();\n const clientOpts = { ...parseGatewayClientOptions(options as Record<string, unknown>), configPath };\n\n const healthResult = await callGatewayApi<HealthResponse>('GET', '/api/health', {\n ...clientOpts,\n timeoutMs: clientOpts.timeoutMs ?? 5000,\n });\n\n if (!healthResult.ok) {\n if (clientOpts.json) {\n console.log(\n JSON.stringify(\n {\n status: 'unreachable',\n error: healthResult.error,\n durationMs: healthResult.durationMs,\n },\n null,\n 2,\n ),\n );\n } else {\n console.error(`❌ Gateway unreachable: ${healthResult.error}`);\n console.error('');\n console.error('💡 Is the gateway running? Try: xopc gateway');\n }\n process.exit(1);\n }\n\n const statusResult = await callGatewayApi<StatusResponse>('GET', '/api/status', clientOpts);\n\n if (clientOpts.json) {\n console.log(\n JSON.stringify(\n {\n status: 'ok',\n durationMs: healthResult.durationMs,\n health: healthResult.data,\n ...(statusResult.ok ? { details: statusResult.data } : {}),\n },\n null,\n 2,\n ),\n );\n process.exit(0);\n }\n\n console.log(`✅ Gateway Health: OK (${healthResult.durationMs}ms)`);\n console.log('');\n\n if (healthResult.data?.version) {\n console.log(` Version: ${healthResult.data.version}`);\n }\n if (healthResult.data?.uptime != null) {\n console.log(` Uptime: ${formatUptime(healthResult.data.uptime)}`);\n }\n\n if (statusResult.ok && statusResult.data?.channels) {\n console.log('');\n console.log('📡 Channels:');\n for (const [name, info] of Object.entries(statusResult.data.channels)) {\n const statusIcon = info.status === 'connected' ? '✅' : info.status === 'disabled' ? '⚪' : '❌';\n const accountsLabel = info.accounts != null ? ` (${info.accounts} account(s))` : '';\n console.log(` ${statusIcon} ${name}: ${info.status}${accountsLabel}`);\n }\n } else if (statusResult.status === 401) {\n console.log('');\n console.log('🔒 Detailed status requires authentication. Pass --token <token>.');\n }\n\n process.exit(0);\n });\n\n return cmd;\n}\n"],"mappings":";;;;;YAQ6D;AAe7D,SAAS,aAAa,SAA0B;AAC9C,KAAI,CAAC,WAAW,WAAW,EAAG,QAAO;CACrC,MAAM,OAAO,KAAK,MAAM,UAAU,MAAM;CACxC,MAAM,QAAQ,KAAK,MAAO,UAAU,QAAS,KAAK;CAClD,MAAM,UAAU,KAAK,MAAO,UAAU,OAAQ,GAAG;CACjD,MAAM,QAAkB,EAAE;AAC1B,KAAI,OAAO,EAAG,OAAM,KAAK,GAAG,KAAK,GAAG;AACpC,KAAI,QAAQ,EAAG,OAAM,KAAK,GAAG,MAAM,GAAG;AACtC,OAAM,KAAK,GAAG,QAAQ,GAAG;AACzB,QAAO,MAAM,KAAK,IAAI;;AAGxB,SAAgB,sBAA+B;CAC7C,MAAM,MAAM,IAAI,QAAQ,SAAS,CAAC,YAAY,0CAA0C;AAExF,yBAAwB,IAAI;AAE5B,KAAI,OAAO,OAAO,YAAY;EAE5B,MAAM,aADM,oBACU,CAAC,cAAc,mBAAmB;EACxD,MAAM,aAAa;GAAE,GAAG,0BAA0B,QAAmC;GAAE;GAAY;EAEnG,MAAM,eAAe,MAAM,eAA+B,OAAO,eAAe;GAC9E,GAAG;GACH,WAAW,WAAW,aAAa;GACpC,CAAC;AAEF,MAAI,CAAC,aAAa,IAAI;AACpB,OAAI,WAAW,KACb,SAAQ,IACN,KAAK,UACH;IACE,QAAQ;IACR,OAAO,aAAa;IACpB,YAAY,aAAa;IAC1B,EACD,MACA,EACD,CACF;QACI;AACL,YAAQ,MAAM,0BAA0B,aAAa,QAAQ;AAC7D,YAAQ,MAAM,GAAG;AACjB,YAAQ,MAAM,+CAA+C;;AAE/D,WAAQ,KAAK,EAAE;;EAGjB,MAAM,eAAe,MAAM,eAA+B,OAAO,eAAe,WAAW;AAE3F,MAAI,WAAW,MAAM;AACnB,WAAQ,IACN,KAAK,UACH;IACE,QAAQ;IACR,YAAY,aAAa;IACzB,QAAQ,aAAa;IACrB,GAAI,aAAa,KAAK,EAAE,SAAS,aAAa,MAAM,GAAG,EAAE;IAC1D,EACD,MACA,EACD,CACF;AACD,WAAQ,KAAK,EAAE;;AAGjB,UAAQ,IAAI,yBAAyB,aAAa,WAAW,KAAK;AAClE,UAAQ,IAAI,GAAG;AAEf,MAAI,aAAa,MAAM,QACrB,SAAQ,IAAI,eAAe,aAAa,KAAK,UAAU;AAEzD,MAAI,aAAa,MAAM,UAAU,KAC/B,SAAQ,IAAI,eAAe,aAAa,aAAa,KAAK,OAAO,GAAG;AAGtE,MAAI,aAAa,MAAM,aAAa,MAAM,UAAU;AAClD,WAAQ,IAAI,GAAG;AACf,WAAQ,IAAI,eAAe;AAC3B,QAAK,MAAM,CAAC,MAAM,SAAS,OAAO,QAAQ,aAAa,KAAK,SAAS,EAAE;IACrE,MAAM,aAAa,KAAK,WAAW,cAAc,MAAM,KAAK,WAAW,aAAa,MAAM;IAC1F,MAAM,gBAAgB,KAAK,YAAY,OAAO,KAAK,KAAK,SAAS,gBAAgB;AACjF,YAAQ,IAAI,MAAM,WAAW,GAAG,KAAK,IAAI,KAAK,SAAS,gBAAgB;;aAEhE,aAAa,WAAW,KAAK;AACtC,WAAQ,IAAI,GAAG;AACf,WAAQ,IAAI,oEAAoE;;AAGlF,UAAQ,KAAK,EAAE;GACf;AAEF,QAAO"}
@@ -1,5 +1,8 @@
1
1
  export { createTokenCommand } from './token.js';
2
2
  export { createStatusCommand } from './status.js';
3
+ export { createHealthCommand } from './health.js';
4
+ export { createCallCommand } from './call.js';
5
+ export { createProbeCommand } from './probe.js';
3
6
  export { createStopCommand } from './stop.js';
4
7
  export { createRestartCommand } from './restart.js';
5
8
  export { createLogsCommand } from './logs.js';
@@ -1,7 +1,10 @@
1
1
  import { createTokenCommand } from "./token.js";
2
2
  import { createStatusCommand } from "./status.js";
3
+ import { createHealthCommand } from "./health.js";
4
+ import { createCallCommand } from "./call.js";
5
+ import { createProbeCommand } from "./probe.js";
3
6
  import { createStopCommand } from "./stop.js";
4
7
  import { createRestartCommand } from "./restart.js";
5
8
  import { createLogsCommand } from "./logs.js";
6
9
  import { createInstallCommand, createServiceStartCommand, createServiceStatusCommand, createUninstallCommand } from "./service.js";
7
- export { createInstallCommand, createLogsCommand, createRestartCommand, createServiceStartCommand, createServiceStatusCommand, createStatusCommand, createStopCommand, createTokenCommand, createUninstallCommand };
10
+ export { createCallCommand, createHealthCommand, createInstallCommand, createLogsCommand, createProbeCommand, createRestartCommand, createServiceStartCommand, createServiceStatusCommand, createStatusCommand, createStopCommand, createTokenCommand, createUninstallCommand };
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function createProbeCommand(): Command;