@xopcai/xopc 0.0.8 → 0.0.11

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 (242) hide show
  1. package/dist/extensions/telegram/src/plugin.js +1 -1
  2. package/dist/extensions/telegram/src/routing-integration.js +2 -2
  3. package/dist/extensions/weixin/src/plugin.js +1 -1
  4. package/dist/gateway/static/root/assets/{agents-BSNzJWbQ.js → agents-BdC4Y-HX.js} +2 -2
  5. package/dist/gateway/static/root/assets/agents-BdC4Y-HX.js.map +1 -0
  6. package/dist/gateway/static/root/assets/{apps-page-BKk9SB4D.js → apps-page-C-oaSHkm.js} +2 -2
  7. package/dist/gateway/static/root/assets/{apps-page-BKk9SB4D.js.map → apps-page-C-oaSHkm.js.map} +1 -1
  8. package/dist/gateway/static/root/assets/attachment-load-BDDlItdE.js +1 -0
  9. package/dist/gateway/static/root/assets/{channels-settings-_J6cQN6G.js → channels-settings-BqEUppPO.js} +2 -2
  10. package/dist/gateway/static/root/assets/{channels-settings-_J6cQN6G.js.map → channels-settings-BqEUppPO.js.map} +1 -1
  11. package/dist/gateway/static/root/assets/{chat-agents-api-DPb_0O8M.js → chat-agents-api-BhqjQ7iL.js} +2 -2
  12. package/dist/gateway/static/root/assets/{chat-agents-api-DPb_0O8M.js.map → chat-agents-api-BhqjQ7iL.js.map} +1 -1
  13. package/dist/gateway/static/root/assets/{cron-page-BUJOuuKX.js → cron-page-Cli49RKR.js} +2 -2
  14. package/dist/gateway/static/root/assets/{cron-page-BUJOuuKX.js.map → cron-page-Cli49RKR.js.map} +1 -1
  15. package/dist/gateway/static/root/assets/{cron-utils-Cn0YVg8x.js → cron-utils-Dkj-Ldpf.js} +2 -2
  16. package/dist/gateway/static/root/assets/{cron-utils-Cn0YVg8x.js.map → cron-utils-Dkj-Ldpf.js.map} +1 -1
  17. package/dist/gateway/static/root/assets/{electron-env-D9bm1FIu.js → electron-env-BDtJw9AY.js} +2 -2
  18. package/dist/gateway/static/root/assets/{electron-env-D9bm1FIu.js.map → electron-env-BDtJw9AY.js.map} +1 -1
  19. package/dist/gateway/static/root/assets/{extension-debug-page-DTz4O5Ua.js → extension-debug-page-BMcZlaxF.js} +2 -2
  20. package/dist/gateway/static/root/assets/{extension-debug-page-DTz4O5Ua.js.map → extension-debug-page-BMcZlaxF.js.map} +1 -1
  21. package/dist/gateway/static/root/assets/{extension-iframe-host-Cs1Kde9o.js → extension-iframe-host-D5HEF0KR.js} +2 -2
  22. package/dist/gateway/static/root/assets/{extension-iframe-host-Cs1Kde9o.js.map → extension-iframe-host-D5HEF0KR.js.map} +1 -1
  23. package/dist/gateway/static/root/assets/{extension-page-G52iX0Bo.js → extension-page-CXdCSSPl.js} +2 -2
  24. package/dist/gateway/static/root/assets/{extension-page-G52iX0Bo.js.map → extension-page-CXdCSSPl.js.map} +1 -1
  25. package/dist/gateway/static/root/assets/{extension-provider-CO2jxBA9.js → extension-provider-DZCZgQE2.js} +2 -2
  26. package/dist/gateway/static/root/assets/{extension-provider-CO2jxBA9.js.map → extension-provider-DZCZgQE2.js.map} +1 -1
  27. package/dist/gateway/static/root/assets/{extension-settings-page-D9Ul8uSt.js → extension-settings-page-CX6STpx3.js} +2 -2
  28. package/dist/gateway/static/root/assets/{extension-settings-page-D9Ul8uSt.js.map → extension-settings-page-CX6STpx3.js.map} +1 -1
  29. package/dist/gateway/static/root/assets/{gateway-config-swr-Bc8SVD15.js → gateway-config-swr-Cph02QZn.js} +2 -2
  30. package/dist/gateway/static/root/assets/{gateway-config-swr-Bc8SVD15.js.map → gateway-config-swr-Cph02QZn.js.map} +1 -1
  31. package/dist/gateway/static/root/assets/index-Bty3m0mS.css +2 -0
  32. package/dist/gateway/static/root/assets/{index-BXUJbteW.js → index-iTUyfzNr.js} +10 -10
  33. package/dist/gateway/static/root/assets/index-iTUyfzNr.js.map +1 -0
  34. package/dist/gateway/static/root/assets/{logs-page-5V25JkQY.js → logs-page-B9O5l3I8.js} +2 -2
  35. package/dist/gateway/static/root/assets/{logs-page-5V25JkQY.js.map → logs-page-B9O5l3I8.js.map} +1 -1
  36. package/dist/gateway/static/root/assets/{model-selector-he3aQfme.js → model-selector-BLiY_O25.js} +2 -2
  37. package/dist/gateway/static/root/assets/{model-selector-he3aQfme.js.map → model-selector-BLiY_O25.js.map} +1 -1
  38. package/dist/gateway/static/root/assets/page-header-store-BFpnFTed.js +2 -0
  39. package/dist/gateway/static/root/assets/{page-header-store-DJHD9Ean.js.map → page-header-store-BFpnFTed.js.map} +1 -1
  40. package/dist/gateway/static/root/assets/{session-api-n-4O5d9U.js → session-api-DEhQXWJg.js} +2 -2
  41. package/dist/gateway/static/root/assets/{session-api-n-4O5d9U.js.map → session-api-DEhQXWJg.js.map} +1 -1
  42. package/dist/gateway/static/root/assets/{session-working-directory-control-B6dHLvbr.js → session-working-directory-control-DKOtWs3-.js} +2 -2
  43. package/dist/gateway/static/root/assets/{session-working-directory-control-B6dHLvbr.js.map → session-working-directory-control-DKOtWs3-.js.map} +1 -1
  44. package/dist/gateway/static/root/assets/{sessions-page-rBUfTdm3.js → sessions-page-BYlWP1ep.js} +2 -2
  45. package/dist/gateway/static/root/assets/{sessions-page-rBUfTdm3.js.map → sessions-page-BYlWP1ep.js.map} +1 -1
  46. package/dist/gateway/static/root/assets/{settings-page-B3QrJm-E.js → settings-page-oCnIavdg.js} +2 -2
  47. package/dist/gateway/static/root/assets/settings-page-oCnIavdg.js.map +1 -0
  48. package/dist/gateway/static/root/assets/{skill-api-vxtE8kI6.js → skill-api-DWrn8Az0.js} +2 -2
  49. package/dist/gateway/static/root/assets/{skill-api-vxtE8kI6.js.map → skill-api-DWrn8Az0.js.map} +1 -1
  50. package/dist/gateway/static/root/assets/{skills-page-D36_O2Ub.js → skills-page-C59WQpM1.js} +2 -2
  51. package/dist/gateway/static/root/assets/{skills-page-D36_O2Ub.js.map → skills-page-C59WQpM1.js.map} +1 -1
  52. package/dist/gateway/static/root/assets/{theme-store-CmiSsYBd.js → theme-store-CywXkKml.js} +2 -2
  53. package/dist/gateway/static/root/assets/{theme-store-CmiSsYBd.js.map → theme-store-CywXkKml.js.map} +1 -1
  54. package/dist/gateway/static/root/assets/url-D7yWllI8.js +2 -0
  55. package/dist/gateway/static/root/assets/url-D7yWllI8.js.map +1 -0
  56. package/dist/gateway/static/root/assets/{useTranslation-DYORQ7x6.js → useTranslation-CACj0DBJ.js} +2 -2
  57. package/dist/gateway/static/root/assets/{useTranslation-DYORQ7x6.js.map → useTranslation-CACj0DBJ.js.map} +1 -1
  58. package/dist/gateway/static/root/index.html +15 -15
  59. package/dist/package.js +1 -1
  60. package/dist/src/agent/agent-manager.d.ts +1 -0
  61. package/dist/src/agent/agent-manager.js +20 -12
  62. package/dist/src/agent/agent-manager.js.map +1 -1
  63. package/dist/src/agent/background-review/run-background-review.js +2 -0
  64. package/dist/src/agent/background-review/run-background-review.js.map +1 -1
  65. package/dist/src/agent/child-agent-factory.js +2 -0
  66. package/dist/src/agent/child-agent-factory.js.map +1 -1
  67. package/dist/src/agent/context/expand-at-file-mentions.d.ts +4 -0
  68. package/dist/src/agent/context/expand-at-file-mentions.js +69 -0
  69. package/dist/src/agent/context/expand-at-file-mentions.js.map +1 -0
  70. package/dist/src/agent/context/workspace-seed.js +1 -1
  71. package/dist/src/agent/image/understanding/pi-ai-provider.js.map +1 -1
  72. package/dist/src/agent/ipc/bus.js +1 -1
  73. package/dist/src/agent/ipc/inbox.js +1 -1
  74. package/dist/src/agent/ipc/socket.js +1 -1
  75. package/dist/src/agent/memory/compaction.d.ts +1 -1
  76. package/dist/src/agent/memory/compaction.js +38 -11
  77. package/dist/src/agent/memory/compaction.js.map +1 -1
  78. package/dist/src/agent/messaging/command-handler.d.ts +13 -0
  79. package/dist/src/agent/messaging/command-handler.js +14 -2
  80. package/dist/src/agent/messaging/command-handler.js.map +1 -1
  81. package/dist/src/agent/models/manager.js +1 -1
  82. package/dist/src/agent/orchestration/agent-orchestrator.js +6 -1
  83. package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
  84. package/dist/src/agent/prompt/service-prompt-builder.js +1 -1
  85. package/dist/src/agent/service.d.ts +16 -1
  86. package/dist/src/agent/service.js +178 -20
  87. package/dist/src/agent/service.js.map +1 -1
  88. package/dist/src/agent/skills/format-skills-prompt.js.map +1 -1
  89. package/dist/src/agent/skills/index.js +1 -1
  90. package/dist/src/agent/skills/scanner.js +1 -1
  91. package/dist/src/agent/skills/skill-manage-ops.js +1 -1
  92. package/dist/src/agent/skills/skill-manage-ops.js.map +1 -1
  93. package/dist/src/agent/skills/skill-manager.js +1 -1
  94. package/dist/src/agent/tools/browser/tools.js.map +1 -1
  95. package/dist/src/agent/tools/factory.js +1 -1
  96. package/dist/src/agent/tools/image-tool.js.map +1 -1
  97. package/dist/src/agent/tools/send-media.js +1 -1
  98. package/dist/src/agent/tools/skill-manage-tool.js +1 -1
  99. package/dist/src/agent/tools/write.js +1 -1
  100. package/dist/src/auth/credentials.js +2 -2
  101. package/dist/src/auth/sync-provider-auth.js +1 -1
  102. package/dist/src/channels/attachments/inbound-persist.js +1 -1
  103. package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
  104. package/dist/src/channels/index.d.ts +1 -1
  105. package/dist/src/channels/index.js +2 -2
  106. package/dist/src/channels/pipeline.d.ts +8 -1
  107. package/dist/src/channels/pipeline.js +49 -4
  108. package/dist/src/channels/pipeline.js.map +1 -1
  109. package/dist/src/chat-commands/builtins/config.d.ts +4 -0
  110. package/dist/src/chat-commands/builtins/config.js +197 -0
  111. package/dist/src/chat-commands/builtins/config.js.map +1 -0
  112. package/dist/src/chat-commands/builtins/context.d.ts +4 -0
  113. package/dist/src/chat-commands/builtins/context.js +44 -0
  114. package/dist/src/chat-commands/builtins/context.js.map +1 -0
  115. package/dist/src/chat-commands/builtins/session.js +111 -0
  116. package/dist/src/chat-commands/builtins/session.js.map +1 -1
  117. package/dist/src/chat-commands/builtins/thinking.js +49 -21
  118. package/dist/src/chat-commands/builtins/thinking.js.map +1 -1
  119. package/dist/src/chat-commands/config-paths.d.ts +10 -0
  120. package/dist/src/chat-commands/config-paths.js +45 -0
  121. package/dist/src/chat-commands/config-paths.js.map +1 -0
  122. package/dist/src/chat-commands/config-value.d.ts +12 -0
  123. package/dist/src/chat-commands/config-value.js +53 -0
  124. package/dist/src/chat-commands/config-value.js.map +1 -0
  125. package/dist/src/chat-commands/context.d.ts +24 -1
  126. package/dist/src/chat-commands/context.js +41 -0
  127. package/dist/src/chat-commands/context.js.map +1 -1
  128. package/dist/src/chat-commands/index.d.ts +2 -0
  129. package/dist/src/chat-commands/index.js +5 -1
  130. package/dist/src/chat-commands/index.js.map +1 -1
  131. package/dist/src/chat-commands/types.d.ts +33 -1
  132. package/dist/src/cli/commands/agent/interactive.js +1 -1
  133. package/dist/src/cli/commands/agent/interactive.js.map +1 -1
  134. package/dist/src/cli/commands/agent.js +22 -10
  135. package/dist/src/cli/commands/agent.js.map +1 -1
  136. package/dist/src/cli/commands/auth.js.map +1 -1
  137. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  138. package/dist/src/cli/commands/extension.js +10 -0
  139. package/dist/src/cli/commands/extension.js.map +1 -1
  140. package/dist/src/cli/commands/init.js +3 -4
  141. package/dist/src/cli/commands/init.js.map +1 -1
  142. package/dist/src/cli/commands/session/utils.js.map +1 -1
  143. package/dist/src/cli/commands/update.d.ts +1 -0
  144. package/dist/src/cli/commands/update.js +171 -0
  145. package/dist/src/cli/commands/update.js.map +1 -0
  146. package/dist/src/cli/index.d.ts +1 -1
  147. package/dist/src/cli/index.js +3 -1
  148. package/dist/src/cli/index.js.map +1 -1
  149. package/dist/src/config/index.d.ts +1 -0
  150. package/dist/src/config/index.js +5 -4
  151. package/dist/src/config/index.js.map +1 -1
  152. package/dist/src/config/loader.js +1 -1
  153. package/dist/src/config/models-json.js +1 -1
  154. package/dist/src/config/paths.js.map +1 -1
  155. package/dist/src/config/profile.js +2 -2
  156. package/dist/src/config/runtime-overrides.d.ts +8 -0
  157. package/dist/src/config/runtime-overrides.js +40 -0
  158. package/dist/src/config/runtime-overrides.js.map +1 -0
  159. package/dist/src/config/schema.d.ts +35 -0
  160. package/dist/src/config/schema.js +19 -3
  161. package/dist/src/config/schema.js.map +1 -1
  162. package/dist/src/cron/executor.js +2 -2
  163. package/dist/src/cron/persistence.js +1 -1
  164. package/dist/src/cron/run-log-store.js +1 -1
  165. package/dist/src/extensions/health.js +1 -1
  166. package/dist/src/extensions/loader.d.ts +1 -1
  167. package/dist/src/extensions/loader.js +6 -9
  168. package/dist/src/extensions/loader.js.map +1 -1
  169. package/dist/src/extensions/lockfile.js +1 -1
  170. package/dist/src/extensions/sdk/index.js +6 -1
  171. package/dist/src/extensions/sdk/index.js.map +1 -0
  172. package/dist/src/gateway/agents-admin.js +2 -2
  173. package/dist/src/gateway/agents-admin.js.map +1 -1
  174. package/dist/src/gateway/hono/oauth.js +1 -1
  175. package/dist/src/gateway/hono/routes/config.js +1 -1
  176. package/dist/src/gateway/hono/routes/index.js +2 -0
  177. package/dist/src/gateway/hono/routes/index.js.map +1 -1
  178. package/dist/src/gateway/hono/routes/models.js +64 -11
  179. package/dist/src/gateway/hono/routes/models.js.map +1 -1
  180. package/dist/src/gateway/hono/routes/public-gateway.js +10 -0
  181. package/dist/src/gateway/hono/routes/public-gateway.js.map +1 -1
  182. package/dist/src/gateway/hono/routes/update.d.ts +3 -0
  183. package/dist/src/gateway/hono/routes/update.js +141 -0
  184. package/dist/src/gateway/hono/routes/update.js.map +1 -0
  185. package/dist/src/gateway/hono/routes/workspace.js +84 -4
  186. package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
  187. package/dist/src/gateway/hono/sse.js +2 -2
  188. package/dist/src/gateway/service.d.ts +1 -0
  189. package/dist/src/gateway/service.js +16 -4
  190. package/dist/src/gateway/service.js.map +1 -1
  191. package/dist/src/gateway/workspace-fs-file-list.d.ts +5 -0
  192. package/dist/src/gateway/workspace-fs-file-list.js +56 -0
  193. package/dist/src/gateway/workspace-fs-file-list.js.map +1 -0
  194. package/dist/src/gateway/workspace-heartbeat-path.js +1 -1
  195. package/dist/src/gateway/workspace-ripgrep.d.ts +5 -0
  196. package/dist/src/gateway/workspace-ripgrep.js +88 -4
  197. package/dist/src/gateway/workspace-ripgrep.js.map +1 -1
  198. package/dist/src/infra/update-channels.d.ts +14 -0
  199. package/dist/src/infra/update-channels.js +30 -0
  200. package/dist/src/infra/update-channels.js.map +1 -0
  201. package/dist/src/infra/update-check.d.ts +53 -0
  202. package/dist/src/infra/update-check.js +155 -0
  203. package/dist/src/infra/update-check.js.map +1 -0
  204. package/dist/src/infra/update-runner.d.ts +18 -0
  205. package/dist/src/infra/update-runner.js +112 -0
  206. package/dist/src/infra/update-runner.js.map +1 -0
  207. package/dist/src/infra/update-startup.d.ts +20 -0
  208. package/dist/src/infra/update-startup.js +246 -0
  209. package/dist/src/infra/update-startup.js.map +1 -0
  210. package/dist/src/providers/extension-stream-bridge.d.ts +3 -0
  211. package/dist/src/providers/extension-stream-bridge.js +239 -0
  212. package/dist/src/providers/extension-stream-bridge.js.map +1 -0
  213. package/dist/src/providers/index.d.ts +7 -2
  214. package/dist/src/providers/index.js +77 -14
  215. package/dist/src/providers/index.js.map +1 -1
  216. package/dist/src/providers/model-registry.js +1 -1
  217. package/dist/src/providers/plugin-registry.js +92 -87
  218. package/dist/src/providers/plugin-registry.js.map +1 -1
  219. package/dist/src/session/chat-export.d.ts +5 -0
  220. package/dist/src/session/chat-export.js +35 -0
  221. package/dist/src/session/chat-export.js.map +1 -0
  222. package/dist/src/session/config-store.js +1 -1
  223. package/dist/src/session/manager.d.ts +1 -1
  224. package/dist/src/session/manager.js +2 -2
  225. package/dist/src/session/manager.js.map +1 -1
  226. package/dist/src/session/session-title.js +1 -1
  227. package/dist/src/session/store.d.ts +1 -1
  228. package/dist/src/session/store.js +5 -5
  229. package/dist/src/session/store.js.map +1 -1
  230. package/dist/src/utils/logger/audit.js +1 -1
  231. package/dist/src/utils/logger/log-store.js +1 -1
  232. package/dist/src/utils/logger/rotation.js +1 -1
  233. package/dist/src/voice/tts/audio.js +1 -1
  234. package/package.json +2 -1
  235. package/dist/gateway/static/root/assets/agents-BSNzJWbQ.js.map +0 -1
  236. package/dist/gateway/static/root/assets/attachment-load-DXcJLSWT.js +0 -1
  237. package/dist/gateway/static/root/assets/index-BXUJbteW.js.map +0 -1
  238. package/dist/gateway/static/root/assets/index-CQLMxWSA.css +0 -2
  239. package/dist/gateway/static/root/assets/page-header-store-DJHD9Ean.js +0 -2
  240. package/dist/gateway/static/root/assets/settings-page-B3QrJm-E.js.map +0 -1
  241. package/dist/gateway/static/root/assets/url-CtSqjF9J.js +0 -2
  242. package/dist/gateway/static/root/assets/url-CtSqjF9J.js.map +0 -1
@@ -2,12 +2,31 @@ import { resolveModelsJsonPath } from "../../../config/paths.js";
2
2
  import { init_resolve_config_value, testApiKeyResolution } from "../../../config/resolve-config-value.js";
3
3
  import { init_models_json, loadModelsJson, saveModelsJson, validateModelsConfig } from "../../../config/models-json.js";
4
4
  import { getModelRegistry } from "../../../providers/model-registry.js";
5
+ import { getProviderRegistry, init_plugin_registry } from "../../../providers/plugin-registry.js";
5
6
  import { PROVIDER_META, getAllModels, getAllProviders, getAvailableModels, getProviderActiveKeySource, init_providers, isProviderConfigured } from "../../../providers/index.js";
6
7
  import { listImageGenerationProvidersSummary } from "../../../agent/image/generation/runtime.js";
7
8
  //#region src/gateway/hono/routes/models.ts
8
9
  init_models_json();
9
10
  init_resolve_config_value();
10
11
  init_providers();
12
+ init_plugin_registry();
13
+ function mapPluginModel(providerId, model, available) {
14
+ return {
15
+ id: `${providerId}/${model.id}`,
16
+ name: model.name,
17
+ provider: providerId,
18
+ contextWindow: model.contextWindow ?? 128e3,
19
+ maxTokens: model.maxOutputTokens ?? 4096,
20
+ reasoning: false,
21
+ vision: model.supportsImages ?? false,
22
+ cost: {
23
+ input: model.pricing?.input ?? 0,
24
+ output: model.pricing?.output ?? 0
25
+ },
26
+ available,
27
+ source: "extension"
28
+ };
29
+ }
11
30
  function registerModelsRoutes(authenticated, deps) {
12
31
  const { service } = deps;
13
32
  authenticated.get("/api/models-json", async (c) => {
@@ -76,6 +95,7 @@ function registerModelsRoutes(authenticated, deps) {
76
95
  });
77
96
  });
78
97
  authenticated.get("/api/models", async (c) => {
98
+ const pluginRegistry = getProviderRegistry();
79
99
  const models = (await getAvailableModels()).map((m) => ({
80
100
  id: `${m.provider}/${m.id}`,
81
101
  name: m.name,
@@ -87,8 +107,17 @@ function registerModelsRoutes(authenticated, deps) {
87
107
  cost: {
88
108
  input: m.cost?.input ?? 0,
89
109
  output: m.cost?.output ?? 0
90
- }
110
+ },
111
+ ...pluginRegistry.has(m.provider) ? { source: "extension" } : {}
91
112
  }));
113
+ const existingIds = new Set(models.map((m) => m.id));
114
+ for (const plugin of pluginRegistry.listAll()) for (const model of plugin.models) {
115
+ const compositeId = `${plugin.id}/${model.id}`;
116
+ if (!existingIds.has(compositeId)) {
117
+ models.push(mapPluginModel(plugin.id, model, true));
118
+ existingIds.add(compositeId);
119
+ }
120
+ }
92
121
  models.sort((a, b) => {
93
122
  if (a.provider !== b.provider) return a.provider.localeCompare(b.provider);
94
123
  return a.name.localeCompare(b.name);
@@ -106,6 +135,7 @@ function registerModelsRoutes(authenticated, deps) {
106
135
  });
107
136
  });
108
137
  authenticated.get("/api/providers", async (c) => {
138
+ const pluginRegistry = getProviderRegistry();
109
139
  const allModels = getAllModels();
110
140
  const availableModels = await getAvailableModels();
111
141
  const configured = new Set(availableModels.map((m) => `${m.provider}/${m.id}`));
@@ -121,8 +151,17 @@ function registerModelsRoutes(authenticated, deps) {
121
151
  input: m.cost?.input ?? 0,
122
152
  output: m.cost?.output ?? 0
123
153
  },
124
- available: configured.has(`${m.provider}/${m.id}`)
154
+ available: configured.has(`${m.provider}/${m.id}`),
155
+ ...pluginRegistry.has(m.provider) ? { source: "extension" } : {}
125
156
  }));
157
+ const existingIds = new Set(models.map((m) => m.id));
158
+ for (const plugin of pluginRegistry.listAll()) for (const model of plugin.models) {
159
+ const compositeId = `${plugin.id}/${model.id}`;
160
+ if (!existingIds.has(compositeId)) {
161
+ models.push(mapPluginModel(plugin.id, model, configured.has(compositeId)));
162
+ existingIds.add(compositeId);
163
+ }
164
+ }
126
165
  models.sort((a, b) => {
127
166
  if (a.provider !== b.provider) return a.provider.localeCompare(b.provider);
128
167
  return a.name.localeCompare(b.name);
@@ -134,15 +173,29 @@ function registerModelsRoutes(authenticated, deps) {
134
173
  });
135
174
  authenticated.get("/api/providers/meta", async (c) => {
136
175
  const providers = getAllProviders();
137
- const meta = await Promise.all(providers.map(async (provider) => ({
138
- id: provider,
139
- name: PROVIDER_META[provider]?.name || provider,
140
- category: PROVIDER_META[provider]?.category || "specialty",
141
- supportsOAuth: PROVIDER_META[provider]?.supportsOAuth ?? false,
142
- supportsApiKey: PROVIDER_META[provider]?.supportsApiKey ?? true,
143
- configured: await isProviderConfigured(provider),
144
- activeKeySource: await getProviderActiveKeySource(provider)
145
- })));
176
+ const pluginRegistry = getProviderRegistry();
177
+ const meta = await Promise.all(providers.map(async (provider) => {
178
+ const plugin = pluginRegistry.get(provider);
179
+ return {
180
+ id: provider,
181
+ name: plugin?.name ?? PROVIDER_META[provider]?.name ?? provider,
182
+ category: plugin ? "extension" : PROVIDER_META[provider]?.category || "specialty",
183
+ supportsOAuth: plugin ? false : PROVIDER_META[provider]?.supportsOAuth ?? false,
184
+ supportsApiKey: plugin ? true : PROVIDER_META[provider]?.supportsApiKey ?? true,
185
+ configured: await isProviderConfigured(provider),
186
+ activeKeySource: await getProviderActiveKeySource(provider)
187
+ };
188
+ }));
189
+ const knownProviderIds = new Set(providers);
190
+ for (const plugin of pluginRegistry.listAll()) if (!knownProviderIds.has(plugin.id)) meta.push({
191
+ id: plugin.id,
192
+ name: plugin.name,
193
+ category: "extension",
194
+ supportsOAuth: false,
195
+ supportsApiKey: true,
196
+ configured: true,
197
+ activeKeySource: "extension"
198
+ });
146
199
  return c.json({
147
200
  ok: true,
148
201
  payload: { providers: meta }
@@ -1 +1 @@
1
- {"version":3,"file":"models.js","names":["getModelsJsonPath"],"sources":["../../../../../src/gateway/hono/routes/models.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport {\n getModelsJsonPath,\n loadModelsJson,\n saveModelsJson,\n validateModelsConfig,\n} from '../../../config/models-json.js';\nimport { testApiKeyResolution } from '../../../config/resolve-config-value.js';\nimport { listImageGenerationProvidersSummary } from '../../../agent/image/generation/runtime.js';\nimport {\n getAllModels,\n getAvailableModels,\n getModelRegistry,\n getAllProviders,\n getProviderActiveKeySource,\n isProviderConfigured,\n PROVIDER_META,\n} from '../../../providers/index.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nexport function registerModelsRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service } = deps;\n\n // GET /api/models-json - Get models.json configuration\n authenticated.get('/api/models-json', async (c) => {\n const path = getModelsJsonPath();\n const { config, error } = loadModelsJson(path);\n const registry = getModelRegistry();\n \n return c.json({\n ok: true,\n payload: {\n config,\n path,\n exists: error === undefined,\n loadError: error || registry.getError(),\n },\n });\n });\n\n // POST /api/models-json/validate - Validate models.json configuration\n authenticated.post('/api/models-json/validate', async (c) => {\n const body = await c.req.json();\n const { config } = body;\n \n const result = validateModelsConfig(config);\n \n return c.json({\n ok: true,\n payload: result,\n });\n });\n\n // PATCH /api/models-json - Save models.json configuration\n authenticated.patch('/api/models-json', async (c) => {\n const body = await c.req.json();\n const { config } = body;\n \n const path = getModelsJsonPath();\n const result = saveModelsJson(path, config);\n \n if (!result.success) {\n return c.json({ ok: false, error: result.error }, 400);\n }\n \n // Refresh registry\n const registry = getModelRegistry();\n registry.refresh();\n \n // Emit event\n service.emit('models-json.updated', { \n modelCount: registry.getAll().length,\n });\n \n return c.json({ \n ok: true, \n payload: { \n saved: true,\n modelCount: registry.getAll().length,\n },\n });\n });\n\n // POST /api/models-json/reload - Hot reload models.json\n authenticated.post('/api/models-json/reload', async (c) => {\n const registry = getModelRegistry();\n registry.refresh();\n \n const error = registry.getError();\n const models = registry.getAll();\n \n service.emit('models-json.reloaded', { \n modelCount: models.length,\n error: error || undefined,\n });\n \n return c.json({\n ok: true,\n payload: {\n modelCount: models.length,\n error,\n },\n });\n });\n\n // POST /api/models-json/test-api-key - Test API key resolution\n authenticated.post('/api/models-json/test-api-key', async (c) => {\n const body = await c.req.json();\n const { value } = body;\n \n const result = testApiKeyResolution(value);\n \n return c.json({\n ok: true,\n payload: result,\n });\n });\n\n // GET /api/models - Get available models (only configured providers)\n authenticated.get('/api/models', async (c) => {\n const models = (await getAvailableModels()).map(m => ({\n id: `${m.provider}/${m.id}`,\n name: m.name,\n provider: m.provider,\n contextWindow: m.contextWindow ?? 128000,\n maxTokens: m.maxTokens ?? 4096,\n reasoning: m.reasoning ?? false,\n vision: m.input?.includes('image') ?? false,\n cost: {\n input: m.cost?.input ?? 0,\n output: m.cost?.output ?? 0,\n },\n }));\n\n // Sort by provider then name\n models.sort((a, b) => {\n if (a.provider !== b.provider) return a.provider.localeCompare(b.provider);\n return a.name.localeCompare(b.name);\n });\n\n return c.json({ ok: true, payload: { models } });\n });\n\n // GET /api/image/providers — registered image generation providers and models (not in LLM model registry)\n authenticated.get('/api/image/providers', (c) => {\n const providers = listImageGenerationProvidersSummary();\n return c.json({ ok: true, payload: { providers } });\n });\n\n // GET /api/providers - Get ALL available providers and models\n authenticated.get('/api/providers', async (c) => {\n const allModels = getAllModels();\n const availableModels = await getAvailableModels();\n const configured = new Set(availableModels.map(m => `${m.provider}/${m.id}`));\n \n const models = allModels.map(m => ({\n id: `${m.provider}/${m.id}`,\n name: m.name,\n provider: m.provider,\n contextWindow: m.contextWindow ?? 128000,\n maxTokens: m.maxTokens ?? 4096,\n reasoning: m.reasoning ?? false,\n vision: m.input?.includes('image') ?? false,\n cost: {\n input: m.cost?.input ?? 0,\n output: m.cost?.output ?? 0,\n },\n available: configured.has(`${m.provider}/${m.id}`),\n }));\n\n // Sort by provider then name\n models.sort((a, b) => {\n if (a.provider !== b.provider) return a.provider.localeCompare(b.provider);\n return a.name.localeCompare(b.name);\n });\n\n return c.json({ ok: true, payload: { models } });\n });\n\n // GET /api/providers/meta - Get provider metadata (categories, display names)\n authenticated.get('/api/providers/meta', async (c) => {\n const providers = getAllProviders();\n \n const meta = await Promise.all(\n providers.map(async (provider) => ({\n id: provider,\n name: PROVIDER_META[provider]?.name || provider,\n category: PROVIDER_META[provider]?.category || 'specialty',\n supportsOAuth: PROVIDER_META[provider]?.supportsOAuth ?? false,\n supportsApiKey: PROVIDER_META[provider]?.supportsApiKey ?? true,\n configured: await isProviderConfigured(provider),\n activeKeySource: await getProviderActiveKeySource(provider),\n })),\n );\n\n return c.json({ ok: true, payload: { providers: meta } });\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"models.js","names":["getModelsJsonPath"],"sources":["../../../../../src/gateway/hono/routes/models.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport {\n getModelsJsonPath,\n loadModelsJson,\n saveModelsJson,\n validateModelsConfig,\n} from '../../../config/models-json.js';\nimport { testApiKeyResolution } from '../../../config/resolve-config-value.js';\nimport { listImageGenerationProvidersSummary } from '../../../agent/image/generation/runtime.js';\nimport {\n getAllModels,\n getAvailableModels,\n getModelRegistry,\n getAllProviders,\n getProviderActiveKeySource,\n isProviderConfigured,\n PROVIDER_META,\n} from '../../../providers/index.js';\nimport { getProviderRegistry } from '../../../providers/plugin-registry.js';\nimport type { ProviderModelDefinition } from '../../../extensions/types/providers.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nfunction mapPluginModel(providerId: string, model: ProviderModelDefinition, available: boolean) {\n return {\n id: `${providerId}/${model.id}`,\n name: model.name,\n provider: providerId,\n contextWindow: model.contextWindow ?? 128000,\n maxTokens: model.maxOutputTokens ?? 4096,\n reasoning: false,\n vision: model.supportsImages ?? false,\n cost: { input: model.pricing?.input ?? 0, output: model.pricing?.output ?? 0 },\n available,\n source: 'extension' as const,\n };\n}\n\nexport function registerModelsRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service } = deps;\n\n // GET /api/models-json - Get models.json configuration\n authenticated.get('/api/models-json', async (c) => {\n const path = getModelsJsonPath();\n const { config, error } = loadModelsJson(path);\n const registry = getModelRegistry();\n \n return c.json({\n ok: true,\n payload: {\n config,\n path,\n exists: error === undefined,\n loadError: error || registry.getError(),\n },\n });\n });\n\n // POST /api/models-json/validate - Validate models.json configuration\n authenticated.post('/api/models-json/validate', async (c) => {\n const body = await c.req.json();\n const { config } = body;\n \n const result = validateModelsConfig(config);\n \n return c.json({\n ok: true,\n payload: result,\n });\n });\n\n // PATCH /api/models-json - Save models.json configuration\n authenticated.patch('/api/models-json', async (c) => {\n const body = await c.req.json();\n const { config } = body;\n \n const path = getModelsJsonPath();\n const result = saveModelsJson(path, config);\n \n if (!result.success) {\n return c.json({ ok: false, error: result.error }, 400);\n }\n \n // Refresh registry\n const registry = getModelRegistry();\n registry.refresh();\n \n // Emit event\n service.emit('models-json.updated', { \n modelCount: registry.getAll().length,\n });\n \n return c.json({ \n ok: true, \n payload: { \n saved: true,\n modelCount: registry.getAll().length,\n },\n });\n });\n\n // POST /api/models-json/reload - Hot reload models.json\n authenticated.post('/api/models-json/reload', async (c) => {\n const registry = getModelRegistry();\n registry.refresh();\n \n const error = registry.getError();\n const models = registry.getAll();\n \n service.emit('models-json.reloaded', { \n modelCount: models.length,\n error: error || undefined,\n });\n \n return c.json({\n ok: true,\n payload: {\n modelCount: models.length,\n error,\n },\n });\n });\n\n // POST /api/models-json/test-api-key - Test API key resolution\n authenticated.post('/api/models-json/test-api-key', async (c) => {\n const body = await c.req.json();\n const { value } = body;\n \n const result = testApiKeyResolution(value);\n \n return c.json({\n ok: true,\n payload: result,\n });\n });\n\n // GET /api/models - Get available models (only configured providers)\n authenticated.get('/api/models', async (c) => {\n const pluginRegistry = getProviderRegistry();\n const models = (await getAvailableModels()).map(m => ({\n id: `${m.provider}/${m.id}`,\n name: m.name,\n provider: m.provider,\n contextWindow: m.contextWindow ?? 128000,\n maxTokens: m.maxTokens ?? 4096,\n reasoning: m.reasoning ?? false,\n vision: m.input?.includes('image') ?? false,\n cost: {\n input: m.cost?.input ?? 0,\n output: m.cost?.output ?? 0,\n },\n ...(pluginRegistry.has(m.provider) ? { source: 'extension' as const } : {}),\n }));\n\n const existingIds = new Set(models.map(m => m.id));\n for (const plugin of pluginRegistry.listAll()) {\n for (const model of plugin.models) {\n const compositeId = `${plugin.id}/${model.id}`;\n if (!existingIds.has(compositeId)) {\n models.push(mapPluginModel(plugin.id, model, true));\n existingIds.add(compositeId);\n }\n }\n }\n\n // Sort by provider then name\n models.sort((a, b) => {\n if (a.provider !== b.provider) return a.provider.localeCompare(b.provider);\n return a.name.localeCompare(b.name);\n });\n\n return c.json({ ok: true, payload: { models } });\n });\n\n // GET /api/image/providers — registered image generation providers and models (not in LLM model registry)\n authenticated.get('/api/image/providers', (c) => {\n const providers = listImageGenerationProvidersSummary();\n return c.json({ ok: true, payload: { providers } });\n });\n\n // GET /api/providers - Get ALL available providers and models\n authenticated.get('/api/providers', async (c) => {\n const pluginRegistry = getProviderRegistry();\n const allModels = getAllModels();\n const availableModels = await getAvailableModels();\n const configured = new Set(availableModels.map(m => `${m.provider}/${m.id}`));\n\n const models = allModels.map(m => ({\n id: `${m.provider}/${m.id}`,\n name: m.name,\n provider: m.provider,\n contextWindow: m.contextWindow ?? 128000,\n maxTokens: m.maxTokens ?? 4096,\n reasoning: m.reasoning ?? false,\n vision: m.input?.includes('image') ?? false,\n cost: {\n input: m.cost?.input ?? 0,\n output: m.cost?.output ?? 0,\n },\n available: configured.has(`${m.provider}/${m.id}`),\n ...(pluginRegistry.has(m.provider) ? { source: 'extension' as const } : {}),\n }));\n\n const existingIds = new Set(models.map(m => m.id));\n for (const plugin of pluginRegistry.listAll()) {\n for (const model of plugin.models) {\n const compositeId = `${plugin.id}/${model.id}`;\n if (!existingIds.has(compositeId)) {\n models.push(mapPluginModel(plugin.id, model, configured.has(compositeId)));\n existingIds.add(compositeId);\n }\n }\n }\n\n // Sort by provider then name\n models.sort((a, b) => {\n if (a.provider !== b.provider) return a.provider.localeCompare(b.provider);\n return a.name.localeCompare(b.name);\n });\n\n return c.json({ ok: true, payload: { models } });\n });\n\n // GET /api/providers/meta - Get provider metadata (categories, display names)\n authenticated.get('/api/providers/meta', async (c) => {\n const providers = getAllProviders();\n const pluginRegistry = getProviderRegistry();\n\n const meta = await Promise.all(\n providers.map(async (provider) => {\n const plugin = pluginRegistry.get(provider);\n return {\n id: provider,\n name: plugin?.name ?? PROVIDER_META[provider]?.name ?? provider,\n category: plugin ? ('extension' as const) : PROVIDER_META[provider]?.category || 'specialty',\n supportsOAuth: plugin ? false : (PROVIDER_META[provider]?.supportsOAuth ?? false),\n supportsApiKey: plugin ? true : (PROVIDER_META[provider]?.supportsApiKey ?? true),\n configured: await isProviderConfigured(provider),\n activeKeySource: await getProviderActiveKeySource(provider),\n };\n }),\n );\n\n const knownProviderIds = new Set(providers);\n for (const plugin of pluginRegistry.listAll()) {\n if (!knownProviderIds.has(plugin.id)) {\n meta.push({\n id: plugin.id,\n name: plugin.name,\n category: 'extension',\n supportsOAuth: false,\n supportsApiKey: true,\n configured: true,\n activeKeySource: 'extension',\n });\n }\n }\n\n return c.json({ ok: true, payload: { providers: meta } });\n });\n}\n"],"mappings":"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"}
@@ -49,6 +49,16 @@ function registerPublicGatewayRoutes(app, service) {
49
49
  if (response) return response;
50
50
  return c.text("Not found", 404);
51
51
  });
52
+ app.get("/logo.svg", (c) => {
53
+ const response = serveStaticFile("logo.svg");
54
+ if (response) return response;
55
+ return c.text("Not found", 404);
56
+ });
57
+ app.get("/logo-dark.svg", (c) => {
58
+ const response = serveStaticFile("logo-dark.svg");
59
+ if (response) return response;
60
+ return c.text("Not found", 404);
61
+ });
52
62
  app.get("/", (c) => {
53
63
  const response = serveStaticFile("index.html");
54
64
  if (response) return response;
@@ -1 +1 @@
1
- {"version":3,"file":"public-gateway.js","names":[],"sources":["../../../../../src/gateway/hono/routes/public-gateway.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport { PACKAGE_VERSION } from '../../../package-version.js';\nimport type { GatewayService } from '../../service.js';\nimport { serveStaticFile } from '../lib/static-ui.js';\n\nexport function registerPublicGatewayRoutes(app: Hono, service: GatewayService): void {\n app.get('/health', (c) => {\n return c.json(service.getHealth());\n });\n\n app.get('/api', (c) => {\n return c.json({\n service: 'xopc-gateway',\n version: PACKAGE_VERSION,\n transport: 'streamable-http',\n endpoints: [\n 'GET /health',\n 'GET /status',\n 'POST /api/agent (SSE stream / JSON)',\n 'POST /api/agent/abort',\n 'POST /api/agent/steer',\n 'POST /api/send',\n 'GET /api/events (SSE stream)',\n 'GET /api/channels/status',\n 'POST /api/channels/weixin/login/start',\n 'GET /api/channels/weixin/login/:sessionKey',\n 'GET /api/config',\n 'GET /api/agents',\n 'POST /api/agents',\n 'PATCH /api/agents/:id',\n 'DELETE /api/agents/:id',\n 'GET/PUT /api/agents/:id/files/...',\n 'PATCH /api/config',\n 'POST /api/config/reload',\n 'POST /api/heartbeat/trigger',\n '... /api/cron/*',\n 'GET/PATCH /api/sessions/:key/agent-config',\n '... /api/sessions/*',\n 'GET /api/host/fs/meta',\n 'GET /api/host/fs/list',\n ],\n });\n });\n\n app.get('/assets/*', (c) => {\n const path = c.req.path.replace('/assets/', '');\n const response = serveStaticFile(`assets/${path}`);\n if (response) return response;\n return c.text('Not found', 404);\n });\n\n app.get('/favicon.ico', (c) => {\n const response = serveStaticFile('favicon.ico');\n if (response) return response;\n return c.text('Not found', 404);\n });\n\n app.get('/', (c) => {\n const response = serveStaticFile('index.html');\n if (response) return response;\n return c.text('UI not found', 404);\n });\n}\n"],"mappings":";;;sBAE8D;AAI9D,SAAgB,4BAA4B,KAAW,SAA+B;AACpF,KAAI,IAAI,YAAY,MAAM;AACxB,SAAO,EAAE,KAAK,QAAQ,WAAW,CAAC;GAClC;AAEF,KAAI,IAAI,SAAS,MAAM;AACrB,SAAO,EAAE,KAAK;GACZ,SAAS;GACT,SAAS;GACT,WAAW;GACX,WAAW;IACT;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD;GACF,CAAC;GACF;AAEF,KAAI,IAAI,cAAc,MAAM;EAE1B,MAAM,WAAW,gBAAgB,UADpB,EAAE,IAAI,KAAK,QAAQ,YAAY,GAAG,GACG;AAClD,MAAI,SAAU,QAAO;AACrB,SAAO,EAAE,KAAK,aAAa,IAAI;GAC/B;AAEF,KAAI,IAAI,iBAAiB,MAAM;EAC7B,MAAM,WAAW,gBAAgB,cAAc;AAC/C,MAAI,SAAU,QAAO;AACrB,SAAO,EAAE,KAAK,aAAa,IAAI;GAC/B;AAEF,KAAI,IAAI,MAAM,MAAM;EAClB,MAAM,WAAW,gBAAgB,aAAa;AAC9C,MAAI,SAAU,QAAO;AACrB,SAAO,EAAE,KAAK,gBAAgB,IAAI;GAClC"}
1
+ {"version":3,"file":"public-gateway.js","names":[],"sources":["../../../../../src/gateway/hono/routes/public-gateway.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport { PACKAGE_VERSION } from '../../../package-version.js';\nimport type { GatewayService } from '../../service.js';\nimport { serveStaticFile } from '../lib/static-ui.js';\n\nexport function registerPublicGatewayRoutes(app: Hono, service: GatewayService): void {\n app.get('/health', (c) => {\n return c.json(service.getHealth());\n });\n\n app.get('/api', (c) => {\n return c.json({\n service: 'xopc-gateway',\n version: PACKAGE_VERSION,\n transport: 'streamable-http',\n endpoints: [\n 'GET /health',\n 'GET /status',\n 'POST /api/agent (SSE stream / JSON)',\n 'POST /api/agent/abort',\n 'POST /api/agent/steer',\n 'POST /api/send',\n 'GET /api/events (SSE stream)',\n 'GET /api/channels/status',\n 'POST /api/channels/weixin/login/start',\n 'GET /api/channels/weixin/login/:sessionKey',\n 'GET /api/config',\n 'GET /api/agents',\n 'POST /api/agents',\n 'PATCH /api/agents/:id',\n 'DELETE /api/agents/:id',\n 'GET/PUT /api/agents/:id/files/...',\n 'PATCH /api/config',\n 'POST /api/config/reload',\n 'POST /api/heartbeat/trigger',\n '... /api/cron/*',\n 'GET/PATCH /api/sessions/:key/agent-config',\n '... /api/sessions/*',\n 'GET /api/host/fs/meta',\n 'GET /api/host/fs/list',\n ],\n });\n });\n\n app.get('/assets/*', (c) => {\n const path = c.req.path.replace('/assets/', '');\n const response = serveStaticFile(`assets/${path}`);\n if (response) return response;\n return c.text('Not found', 404);\n });\n\n app.get('/favicon.ico', (c) => {\n const response = serveStaticFile('favicon.ico');\n if (response) return response;\n return c.text('Not found', 404);\n });\n\n app.get('/logo.svg', (c) => {\n const response = serveStaticFile('logo.svg');\n if (response) return response;\n return c.text('Not found', 404);\n });\n\n app.get('/logo-dark.svg', (c) => {\n const response = serveStaticFile('logo-dark.svg');\n if (response) return response;\n return c.text('Not found', 404);\n });\n\n app.get('/', (c) => {\n const response = serveStaticFile('index.html');\n if (response) return response;\n return c.text('UI not found', 404);\n });\n}\n"],"mappings":";;;sBAE8D;AAI9D,SAAgB,4BAA4B,KAAW,SAA+B;AACpF,KAAI,IAAI,YAAY,MAAM;AACxB,SAAO,EAAE,KAAK,QAAQ,WAAW,CAAC;GAClC;AAEF,KAAI,IAAI,SAAS,MAAM;AACrB,SAAO,EAAE,KAAK;GACZ,SAAS;GACT,SAAS;GACT,WAAW;GACX,WAAW;IACT;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD;GACF,CAAC;GACF;AAEF,KAAI,IAAI,cAAc,MAAM;EAE1B,MAAM,WAAW,gBAAgB,UADpB,EAAE,IAAI,KAAK,QAAQ,YAAY,GAAG,GACG;AAClD,MAAI,SAAU,QAAO;AACrB,SAAO,EAAE,KAAK,aAAa,IAAI;GAC/B;AAEF,KAAI,IAAI,iBAAiB,MAAM;EAC7B,MAAM,WAAW,gBAAgB,cAAc;AAC/C,MAAI,SAAU,QAAO;AACrB,SAAO,EAAE,KAAK,aAAa,IAAI;GAC/B;AAEF,KAAI,IAAI,cAAc,MAAM;EAC1B,MAAM,WAAW,gBAAgB,WAAW;AAC5C,MAAI,SAAU,QAAO;AACrB,SAAO,EAAE,KAAK,aAAa,IAAI;GAC/B;AAEF,KAAI,IAAI,mBAAmB,MAAM;EAC/B,MAAM,WAAW,gBAAgB,gBAAgB;AACjD,MAAI,SAAU,QAAO;AACrB,SAAO,EAAE,KAAK,aAAa,IAAI;GAC/B;AAEF,KAAI,IAAI,MAAM,MAAM;EAClB,MAAM,WAAW,gBAAgB,aAAa;AAC9C,MAAI,SAAU,QAAO;AACrB,SAAO,EAAE,KAAK,gBAAgB,IAAI;GAClC"}
@@ -0,0 +1,3 @@
1
+ import type { Hono } from 'hono';
2
+ import type { AuthenticatedRouteDeps } from './deps.js';
3
+ export declare function registerUpdateRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void;
@@ -0,0 +1,141 @@
1
+ import { PACKAGE_VERSION, init_package_version } from "../../../package-version.js";
2
+ import { createLogger } from "../../../utils/logger/index.js";
3
+ import { init_logger } from "../../../utils/logger.js";
4
+ import { loadConfig } from "../../../config/loader.js";
5
+ import "../../../config/index.js";
6
+ import { normalizeUpdateChannel } from "../../../infra/update-channels.js";
7
+ import { detectInstallKind, resolvePackageRoot } from "../../../infra/update-check.js";
8
+ import { getUpdateAvailable, runGatewayUpdateCheck } from "../../../infra/update-startup.js";
9
+ import { runAutoUpdateCommand } from "../../../infra/update-runner.js";
10
+ //#region src/gateway/hono/routes/update.ts
11
+ init_package_version();
12
+ init_logger();
13
+ const log = createLogger("GatewayUpdate");
14
+ let updateRunInFlight = false;
15
+ function parseUpdateCliJson(stdout) {
16
+ const t = stdout.trim();
17
+ if (!t) return null;
18
+ try {
19
+ const parsed = JSON.parse(t);
20
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
21
+ } catch {
22
+ const lines = t.split("\n").filter(Boolean);
23
+ for (let i = lines.length - 1; i >= 0; i--) {
24
+ const line = lines[i].trim();
25
+ if (!line.startsWith("{")) continue;
26
+ try {
27
+ const parsed = JSON.parse(line);
28
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
29
+ } catch {}
30
+ }
31
+ }
32
+ return null;
33
+ }
34
+ function registerUpdateRoutes(authenticated, deps) {
35
+ const { strictRateLimitMiddleware, service } = deps;
36
+ /**
37
+ * GET /api/update/status
38
+ */
39
+ authenticated.get("/api/update/status", (c) => {
40
+ const update = getUpdateAvailable();
41
+ return c.json({
42
+ ok: true,
43
+ payload: {
44
+ currentVersion: PACKAGE_VERSION,
45
+ updateAvailable: update !== null,
46
+ latestVersion: update?.latestVersion ?? null,
47
+ channel: update?.channel ?? null
48
+ }
49
+ });
50
+ });
51
+ /**
52
+ * POST /api/update/check
53
+ */
54
+ authenticated.post("/api/update/check", strictRateLimitMiddleware, async (c) => {
55
+ await runGatewayUpdateCheck({
56
+ config: loadConfig(service.getHealth().configPath),
57
+ force: true,
58
+ onUpdateAvailableChange: (update) => {
59
+ service.emit("update.available", update);
60
+ }
61
+ });
62
+ const result = getUpdateAvailable();
63
+ return c.json({
64
+ ok: true,
65
+ payload: {
66
+ currentVersion: PACKAGE_VERSION,
67
+ updateAvailable: result !== null,
68
+ latestVersion: result?.latestVersion ?? null,
69
+ channel: result?.channel ?? null
70
+ }
71
+ });
72
+ });
73
+ /**
74
+ * POST /api/update/run — one-click npm install (OpenClaw-style). Rejects git checkouts.
75
+ */
76
+ authenticated.post("/api/update/run", strictRateLimitMiddleware, async (c) => {
77
+ if (updateRunInFlight) return c.json({
78
+ ok: false,
79
+ error: "busy",
80
+ message: "Another update is already in progress."
81
+ }, 409);
82
+ const configPath = service.getHealth().configPath;
83
+ const channel = normalizeUpdateChannel(loadConfig(configPath).update?.channel) ?? "stable";
84
+ const root = await resolvePackageRoot();
85
+ if (root) {
86
+ if (await detectInstallKind(root) === "git") return c.json({
87
+ ok: false,
88
+ error: "git-checkout",
89
+ message: "Running from a git checkout. Use `git pull` in the repo, or install from npm to use one-click update."
90
+ }, 400);
91
+ }
92
+ updateRunInFlight = true;
93
+ try {
94
+ log.info({ channel }, "Gateway: starting one-click npm update");
95
+ const result = await runAutoUpdateCommand({
96
+ channel,
97
+ root
98
+ });
99
+ const parsed = parseUpdateCliJson(result.stdout ?? "");
100
+ if (result.ok && parsed?.status === "skipped" && parsed?.reason === "git-checkout") return c.json({
101
+ ok: false,
102
+ error: "git-checkout",
103
+ message: String(parsed.message ?? "Git checkout — use git pull instead.")
104
+ }, 400);
105
+ if (!result.ok) {
106
+ log.warn({
107
+ channel,
108
+ exitCode: result.exitCode,
109
+ reason: result.reason
110
+ }, "Gateway: one-click npm update failed");
111
+ return c.json({
112
+ ok: false,
113
+ error: "update-failed",
114
+ message: result.stderr?.trim() || result.reason || `Update exited with code ${result.exitCode ?? "unknown"}`,
115
+ result: parsed
116
+ });
117
+ }
118
+ log.info({ channel }, "Gateway: one-click npm update finished");
119
+ return c.json({
120
+ ok: true,
121
+ result: parsed
122
+ });
123
+ } catch (err) {
124
+ log.error({
125
+ err,
126
+ channel
127
+ }, "Gateway: one-click npm update threw");
128
+ return c.json({
129
+ ok: false,
130
+ error: "internal",
131
+ message: err instanceof Error ? err.message : String(err)
132
+ }, 500);
133
+ } finally {
134
+ updateRunInFlight = false;
135
+ }
136
+ });
137
+ }
138
+ //#endregion
139
+ export { registerUpdateRoutes };
140
+
141
+ //# sourceMappingURL=update.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.js","names":[],"sources":["../../../../../src/gateway/hono/routes/update.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport { loadConfig } from '../../../config/index.js';\nimport { detectInstallKind, resolvePackageRoot } from '../../../infra/update-check.js';\nimport { DEFAULT_PACKAGE_CHANNEL, normalizeUpdateChannel } from '../../../infra/update-channels.js';\nimport { runAutoUpdateCommand } from '../../../infra/update-runner.js';\nimport { getUpdateAvailable, runGatewayUpdateCheck } from '../../../infra/update-startup.js';\nimport { PACKAGE_VERSION } from '../../../package-version.js';\nimport { createLogger } from '../../../utils/logger.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nconst log = createLogger('GatewayUpdate');\n\nlet updateRunInFlight = false;\n\nfunction parseUpdateCliJson(stdout: string): Record<string, unknown> | null {\n const t = stdout.trim();\n if (!t) return null;\n try {\n const parsed = JSON.parse(t) as unknown;\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : null;\n } catch {\n const lines = t.split('\\n').filter(Boolean);\n for (let i = lines.length - 1; i >= 0; i--) {\n const line = lines[i]!.trim();\n if (!line.startsWith('{')) continue;\n try {\n const parsed = JSON.parse(line) as unknown;\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n // try previous line\n }\n }\n }\n return null;\n}\n\nexport function registerUpdateRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { strictRateLimitMiddleware, service } = deps;\n\n /**\n * GET /api/update/status\n */\n authenticated.get('/api/update/status', (c) => {\n const update = getUpdateAvailable();\n return c.json({\n ok: true,\n payload: {\n currentVersion: PACKAGE_VERSION,\n updateAvailable: update !== null,\n latestVersion: update?.latestVersion ?? null,\n channel: update?.channel ?? null,\n },\n });\n });\n\n /**\n * POST /api/update/check\n */\n authenticated.post('/api/update/check', strictRateLimitMiddleware, async (c) => {\n const config = loadConfig(service.getHealth().configPath);\n await runGatewayUpdateCheck({\n config,\n force: true,\n onUpdateAvailableChange: (update) => {\n service.emit('update.available', update);\n },\n });\n const result = getUpdateAvailable();\n return c.json({\n ok: true,\n payload: {\n currentVersion: PACKAGE_VERSION,\n updateAvailable: result !== null,\n latestVersion: result?.latestVersion ?? null,\n channel: result?.channel ?? null,\n },\n });\n });\n\n /**\n * POST /api/update/run — one-click npm install (OpenClaw-style). Rejects git checkouts.\n */\n authenticated.post('/api/update/run', strictRateLimitMiddleware, async (c) => {\n if (updateRunInFlight) {\n return c.json(\n {\n ok: false,\n error: 'busy',\n message: 'Another update is already in progress.',\n },\n 409,\n );\n }\n\n const configPath = service.getHealth().configPath;\n const config = loadConfig(configPath);\n const channel =\n normalizeUpdateChannel(config.update?.channel) ?? DEFAULT_PACKAGE_CHANNEL;\n\n const root = await resolvePackageRoot();\n if (root) {\n const kind = await detectInstallKind(root);\n if (kind === 'git') {\n return c.json(\n {\n ok: false,\n error: 'git-checkout',\n message:\n 'Running from a git checkout. Use `git pull` in the repo, or install from npm to use one-click update.',\n },\n 400,\n );\n }\n }\n\n updateRunInFlight = true;\n try {\n log.info({ channel }, 'Gateway: starting one-click npm update');\n const result = await runAutoUpdateCommand({ channel, root });\n const parsed = parseUpdateCliJson(result.stdout ?? '');\n\n if (result.ok && parsed?.status === 'skipped' && parsed?.reason === 'git-checkout') {\n return c.json(\n {\n ok: false,\n error: 'git-checkout',\n message: String(parsed.message ?? 'Git checkout — use git pull instead.'),\n },\n 400,\n );\n }\n\n if (!result.ok) {\n log.warn(\n { channel, exitCode: result.exitCode, reason: result.reason },\n 'Gateway: one-click npm update failed',\n );\n return c.json({\n ok: false,\n error: 'update-failed',\n message: result.stderr?.trim() || result.reason || `Update exited with code ${result.exitCode ?? 'unknown'}`,\n result: parsed,\n });\n }\n\n log.info({ channel }, 'Gateway: one-click npm update finished');\n // Do not run registry check here: the process still reports the old `PACKAGE_VERSION`\n // until the gateway is restarted, which would keep `update available` set incorrectly.\n return c.json({ ok: true, result: parsed });\n } catch (err) {\n log.error({ err, channel }, 'Gateway: one-click npm update threw');\n return c.json(\n {\n ok: false,\n error: 'internal',\n message: err instanceof Error ? err.message : String(err),\n },\n 500,\n );\n } finally {\n updateRunInFlight = false;\n }\n });\n}\n"],"mappings":"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"}
@@ -1,13 +1,14 @@
1
+ import { createLogger } from "../../../utils/logger/index.js";
2
+ import { init_logger } from "../../../utils/logger.js";
1
3
  import { init_agent_scope, listAgentEntries, normalizeAgentId, resolveAgentHomeDir, resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../../agent/agent-scope.js";
2
4
  import { getWorkspacePath, init_schema } from "../../../config/schema.js";
3
5
  import { extractProfileAgentId } from "../../../config/agent-profile.js";
4
- import { createLogger } from "../../../utils/logger/index.js";
5
- import { init_logger } from "../../../utils/logger.js";
6
6
  import { resolveSafeInboundFilePath } from "../../../channels/attachments/inbound-persist.js";
7
+ import { isPathUnderWorkspace, resolveWorkspaceSafePath, toWorkspaceRelativePosix } from "../../workspace-editor-path.js";
7
8
  import { resolveSafeTtsFilePath } from "../../../channels/attachments/outbound-tts-persist.js";
8
9
  import { resolveHeartbeatMdPath } from "../../workspace-heartbeat-path.js";
9
- import { isPathUnderWorkspace, resolveWorkspaceSafePath, toWorkspaceRelativePosix } from "../../workspace-editor-path.js";
10
- import { runRipgrepInDirectory } from "../../workspace-ripgrep.js";
10
+ import { listWorkspaceRelativeFilesFsFallback } from "../../workspace-fs-file-list.js";
11
+ import { runRipgrepInDirectory, runRipgrepListFiles } from "../../workspace-ripgrep.js";
11
12
  import { join, resolve } from "node:path";
12
13
  import { readFile, readdir, stat, writeFile } from "node:fs/promises";
13
14
  //#region src/gateway/hono/routes/workspace.ts
@@ -20,6 +21,69 @@ function resolvePersistedAttachmentAgentHome(cfg, sessionKeyRaw) {
20
21
  const sk = typeof sessionKeyRaw === "string" ? sessionKeyRaw.trim() : "";
21
22
  return resolveAgentHomeDir(cfg, sk ? extractProfileAgentId(sk, cfg) : resolveDefaultAgentId(cfg));
22
23
  }
24
+ const FILE_SEARCH_MAX_LIMIT = 50;
25
+ /** Subsequence fuzzy match: all query chars appear in order in `candidate` (case-insensitive). */
26
+ function fuzzySubsequenceScore(query, candidate) {
27
+ const q = query.toLowerCase();
28
+ const c = candidate.toLowerCase();
29
+ if (q.length === 0) return 0;
30
+ let qi = 0;
31
+ for (let ci = 0; ci < c.length && qi < q.length; ci++) if (c[ci] === q[qi]) qi++;
32
+ if (qi < q.length) return null;
33
+ const base = c.split("/").pop() ?? c;
34
+ let score = 10;
35
+ if (c.startsWith(q)) score += 40;
36
+ if (base.startsWith(q)) score += 35;
37
+ else if (base.includes(q)) score += 20;
38
+ else if (c.includes(q)) score += 10;
39
+ score -= c.length * 1e-4;
40
+ return score;
41
+ }
42
+ async function fuzzySearchWorkspaceFiles(workspaceRoot, query, limit) {
43
+ let files = await runRipgrepListFiles(workspaceRoot);
44
+ if (files.length === 0) {
45
+ files = await listWorkspaceRelativeFilesFsFallback(workspaceRoot, 12e4);
46
+ if (files.length > 0) log.debug({
47
+ workspaceRoot,
48
+ fileCount: files.length
49
+ }, "workspace files/search: file list from fs walk (ripgrep unavailable or returned empty)");
50
+ }
51
+ const q = query.trim();
52
+ const capped = Math.min(Math.max(limit, 1), FILE_SEARCH_MAX_LIMIT);
53
+ const rows = [];
54
+ if (!q) {
55
+ const sorted = [...files].sort((a, b) => a.localeCompare(b));
56
+ for (const rel of sorted.slice(0, capped)) {
57
+ const name = rel.split("/").pop() ?? rel;
58
+ rows.push({
59
+ name,
60
+ path: rel,
61
+ isDirectory: false,
62
+ score: 0
63
+ });
64
+ }
65
+ return rows;
66
+ }
67
+ for (const rel of files) {
68
+ const name = rel.split("/").pop() ?? rel;
69
+ const scorePath = fuzzySubsequenceScore(q, rel);
70
+ const scoreName = fuzzySubsequenceScore(q, name);
71
+ const score = Math.max(scorePath ?? -Infinity, scoreName ?? -Infinity);
72
+ if (score === -Infinity) continue;
73
+ rows.push({
74
+ name,
75
+ path: rel,
76
+ isDirectory: false,
77
+ score
78
+ });
79
+ }
80
+ rows.sort((a, b) => b.score - a.score || a.path.localeCompare(b.path));
81
+ return rows.slice(0, capped).map(({ name, path, isDirectory }) => ({
82
+ name,
83
+ path,
84
+ isDirectory
85
+ }));
86
+ }
23
87
  function isKnownEditorAgentId(cfg, id) {
24
88
  const n = normalizeAgentId(id);
25
89
  if (n === resolveDefaultAgentId(cfg)) return true;
@@ -556,6 +620,22 @@ function registerWorkspaceRoutes(authenticated, deps) {
556
620
  payload: { results }
557
621
  });
558
622
  });
623
+ /** Fuzzy filename / path search over the session workspace (ripgrep `--files` + subsequence scoring). */
624
+ authenticated.get("/api/workspace/editor/files/search", async (c) => {
625
+ const q = typeof c.req.query("q") === "string" ? c.req.query("q").trim() : "";
626
+ const limitRaw = c.req.query("limit");
627
+ const limit = Math.min(Math.max(parseInt(typeof limitRaw === "string" ? limitRaw : "15", 10) || 15, 1), FILE_SEARCH_MAX_LIMIT);
628
+ const ws = await resolveEditorWorkspaceRootAsync(service, service.currentConfig, c.req.query("sessionKey"), c.req.query("agentId"));
629
+ if (ws.ok === false) return c.json({
630
+ ok: false,
631
+ error: { message: ws.message }
632
+ }, 400);
633
+ const entries = await fuzzySearchWorkspaceFiles(ws.root, q, limit);
634
+ return c.json({
635
+ ok: true,
636
+ payload: { entries }
637
+ });
638
+ });
559
639
  }
560
640
  //#endregion
561
641
  export { registerWorkspaceRoutes };