@xopcai/xopc 0.0.114 → 0.0.116

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 (328) hide show
  1. package/README.md +5 -4
  2. package/README.zh-CN.md +5 -4
  3. package/dist/browser-ext/manifest.json +1 -1
  4. package/dist/extensions/telegram/xopc.extension.json +1 -1
  5. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-Fp-p7bP1.js → agent-avatar-dicebear-CWM1UU5P.js} +1 -1
  6. package/dist/gateway/static/root/assets/agent-browser-settings-page-D1SyFYeh.js +6 -0
  7. package/dist/gateway/static/root/assets/agent-defaults-tabbed-page-C9YxgvuI.js +1 -0
  8. package/dist/gateway/static/root/assets/agents-CteVs3UO.js +71 -0
  9. package/dist/gateway/static/root/assets/app-management-settings-panel-yXSXY_bu.js +1 -0
  10. package/dist/gateway/static/root/assets/{appearance-settings-ycbdJRuZ.js → appearance-settings-DjY1P_ug.js} +1 -1
  11. package/dist/gateway/static/root/assets/apps-page-CVQ1M8Ax.js +1 -0
  12. package/dist/gateway/static/root/assets/{archive-plugin-CJzSADJo.js → archive-plugin-CytE5j-e.js} +1 -1
  13. package/dist/gateway/static/root/assets/{attachment-load-BTAB4zGS.js → attachment-load-BvvrN6rF.js} +1 -1
  14. package/dist/gateway/static/root/assets/{binary-plugins-DXR1-mct.js → binary-plugins-IBWSitnj.js} +1 -1
  15. package/dist/gateway/static/root/assets/{block-editor-CzQri9h2.js → block-editor-C-wDpsf-.js} +44 -44
  16. package/dist/gateway/static/root/assets/channels-settings-Cd3JbeQ1.js +1 -0
  17. package/dist/gateway/static/root/assets/{config-api-DRqjEvAx.js → config-api-C4RAtTKW.js} +1 -1
  18. package/dist/gateway/static/root/assets/connectors-page-MsMa5qBr.js +2 -0
  19. package/dist/gateway/static/root/assets/{cron-api-BXaZXv1o.js → cron-api-sJNMqB6P.js} +1 -1
  20. package/dist/gateway/static/root/assets/cron-page-D30CSwkn.js +3 -0
  21. package/dist/gateway/static/root/assets/dreaming-settings-6fCUWRQf.js +1 -0
  22. package/dist/gateway/static/root/assets/{extension-debug-page-DSkqMYo7.js → extension-debug-page-BAIyjDlq.js} +1 -1
  23. package/dist/gateway/static/root/assets/{extension-page-h2dGDbMN.js → extension-page-DEhv2GpC.js} +1 -1
  24. package/dist/gateway/static/root/assets/{extension-settings-page-hN99RhOi.js → extension-settings-page-DOuAxTDW.js} +1 -1
  25. package/dist/gateway/static/root/assets/{fetch-D9QqUtzZ.js → fetch-CTsREQIe.js} +1 -1
  26. package/dist/gateway/static/root/assets/{format-interval-label-D6E687rG.js → format-interval-label-D35Bfhtx.js} +1 -1
  27. package/dist/gateway/static/root/assets/{gateway-config-api-CLt_SKTC.js → gateway-config-api-C6989a-W.js} +1 -1
  28. package/dist/gateway/static/root/assets/gateway-settings-BpF26t91.js +1 -0
  29. package/dist/gateway/static/root/assets/{gateway-startup-retry-N7Z_sz7p.js → gateway-startup-retry-C1KAHrd2.js} +1 -1
  30. package/dist/gateway/static/root/assets/goal-detail-page-DOjhmRyf.js +1 -0
  31. package/dist/gateway/static/root/assets/goals-config-api-KnLGpu6h.js +1 -0
  32. package/dist/gateway/static/root/assets/goals-page-Xl9_wrFc.js +2 -0
  33. package/dist/gateway/static/root/assets/goals-settings-DtPtqZi8.js +1 -0
  34. package/dist/gateway/static/root/assets/{heartbeat-config-api-DGPadTw-.js → heartbeat-config-api-uKw1f0IG.js} +1 -1
  35. package/dist/gateway/static/root/assets/heartbeat-settings-DN9vEj9P.js +1 -0
  36. package/dist/gateway/static/root/assets/index-C8ytlAF2.js +122 -0
  37. package/dist/gateway/static/root/assets/index-CVpJW2NS.css +1 -0
  38. package/dist/gateway/static/root/assets/{interval-presets-CEklPQ_r.js → interval-presets-Dq0FXJ65.js} +1 -1
  39. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-Ce_aQ8Iv.js → keyboard-shortcuts-settings-C9_t4m7t.js} +1 -1
  40. package/dist/gateway/static/root/assets/logs-page-SNkWzidV.js +2 -0
  41. package/dist/gateway/static/root/assets/{markdown-split-BMz3xPXO.js → markdown-split-Ca87W8-h.js} +1 -1
  42. package/dist/gateway/static/root/assets/{media-plugins-BalOSoz9.js → media-plugins-BAcUjPRJ.js} +1 -1
  43. package/dist/gateway/static/root/assets/messages-BqzIDrlr.js +3 -0
  44. package/dist/gateway/static/root/assets/models-hub-panel-BO4o-GmO.js +2 -0
  45. package/dist/gateway/static/root/assets/note-detail-page-DLV_XDmT.js +2 -0
  46. package/dist/gateway/static/root/assets/{note-image-lightbox-C5D1Q5mY.js → note-image-lightbox-hnSX5SSn.js} +1 -1
  47. package/dist/gateway/static/root/assets/notes-page-ycsDlkFA.js +1 -0
  48. package/dist/gateway/static/root/assets/{preview-open-alternatives-CYFR5U9l.js → preview-open-alternatives-CNgM20H5.js} +1 -1
  49. package/dist/gateway/static/root/assets/remote-access-hub-DQpnE8K9.js +1 -0
  50. package/dist/gateway/static/root/assets/{save-bar-controls-BQ79Z5IP.js → save-bar-controls-CcdjGcfT.js} +1 -1
  51. package/dist/gateway/static/root/assets/{schedule-field-Bgk6Emue.js → schedule-field-CYr39z76.js} +1 -1
  52. package/dist/gateway/static/root/assets/{schema-form-DzS36TMa.js → schema-form-BRAJgZQ2.js} +1 -1
  53. package/dist/gateway/static/root/assets/sessions-page-BZui7iAY.js +1 -0
  54. package/dist/gateway/static/root/assets/settings-advanced-gate-DEfzZ1ax.js +1 -0
  55. package/dist/gateway/static/root/assets/{settings-collapsible-section-CdjFnSAy.js → settings-collapsible-section-BhzjeGWi.js} +1 -1
  56. package/dist/gateway/static/root/assets/{settings-form-section-DZLy_exu.js → settings-form-section-Cz3Pcr9b.js} +1 -1
  57. package/dist/gateway/static/root/assets/settings-page-CLuSsu1-.js +1 -0
  58. package/dist/gateway/static/root/assets/setup-status-panel-Cq4qpXLi.js +1 -0
  59. package/dist/gateway/static/root/assets/{share-preview-page-_qRDr4g_.js → share-preview-page-DL4y9K5e.js} +1 -1
  60. package/dist/gateway/static/root/assets/shares-settings-B9F3HyYQ.js +1 -0
  61. package/dist/gateway/static/root/assets/skill-reload-api-Bp9UPPwm.js +1 -0
  62. package/dist/gateway/static/root/assets/skills-page-BIoBsfSV.js +2 -0
  63. package/dist/gateway/static/root/assets/system-settings-panel-FD2jxRZ3.js +1 -0
  64. package/dist/gateway/static/root/assets/use-agent-defaults-form-Cz2_pPzu.js +1 -0
  65. package/dist/gateway/static/root/assets/{use-channel-catalog-CbXq97rT.js → use-channel-catalog-BInPrfR5.js} +1 -1
  66. package/dist/gateway/static/root/assets/{use-settings-tab-guard-DwS3drWK.js → use-settings-tab-guard-nuHMmZQv.js} +1 -1
  67. package/dist/gateway/static/root/assets/voice-api-key-field-BUIrKb8s.js +1 -0
  68. package/dist/gateway/static/root/assets/{voice-player-store-DSuOd9zy.js → voice-player-store-1U3vFppm.js} +1 -1
  69. package/dist/gateway/static/root/assets/workflows-page-qE3SexnO.js +18 -0
  70. package/dist/gateway/static/root/index.html +5 -5
  71. package/dist/gateway/static/root/logo.svg +4 -3
  72. package/dist/package.js +1 -1
  73. package/dist/src/agent/embedded/session-tool-result-guard.js +8 -1
  74. package/dist/src/agent/embedded/session-tool-result-guard.js.map +1 -1
  75. package/dist/src/agent/goals/checklist-judge.d.ts +2 -0
  76. package/dist/src/agent/goals/checklist-judge.js +4 -3
  77. package/dist/src/agent/goals/checklist-judge.js.map +1 -1
  78. package/dist/src/agent/goals/evaluate-turn.d.ts +2 -1
  79. package/dist/src/agent/goals/evaluate-turn.js +2 -0
  80. package/dist/src/agent/goals/evaluate-turn.js.map +1 -1
  81. package/dist/src/agent/goals/post-turn.d.ts +6 -1
  82. package/dist/src/agent/goals/post-turn.js +20 -4
  83. package/dist/src/agent/goals/post-turn.js.map +1 -1
  84. package/dist/src/agent/image/understanding/pi-ai-provider.js +5 -2
  85. package/dist/src/agent/image/understanding/pi-ai-provider.js.map +1 -1
  86. package/dist/src/agent/image/understanding/provider-registry.d.ts +1 -0
  87. package/dist/src/agent/image/understanding/provider-registry.js +16 -2
  88. package/dist/src/agent/image/understanding/provider-registry.js.map +1 -1
  89. package/dist/src/agent/tools/composio-tool.d.ts +8 -0
  90. package/dist/src/agent/tools/composio-tool.js +46 -0
  91. package/dist/src/agent/tools/composio-tool.js.map +1 -0
  92. package/dist/src/agent/tools/factory.js +13 -8
  93. package/dist/src/agent/tools/factory.js.map +1 -1
  94. package/dist/src/agent/tools/index.d.ts +1 -0
  95. package/dist/src/agent/tools/index.js +2 -1
  96. package/dist/src/agent/workflow/builtins/audit-repo.d.ts +1 -1
  97. package/dist/src/agent/workflow/builtins/audit-repo.js +15 -2
  98. package/dist/src/agent/workflow/builtins/audit-repo.js.map +1 -1
  99. package/dist/src/agent/workflow/builtins/client-proposal.d.ts +1 -1
  100. package/dist/src/agent/workflow/builtins/client-proposal.js +10 -1
  101. package/dist/src/agent/workflow/builtins/client-proposal.js.map +1 -1
  102. package/dist/src/agent/workflow/builtins/competitor-scan.d.ts +1 -1
  103. package/dist/src/agent/workflow/builtins/competitor-scan.js +16 -2
  104. package/dist/src/agent/workflow/builtins/competitor-scan.js.map +1 -1
  105. package/dist/src/agent/workflow/builtins/content-draft.d.ts +1 -1
  106. package/dist/src/agent/workflow/builtins/content-draft.js +15 -2
  107. package/dist/src/agent/workflow/builtins/content-draft.js.map +1 -1
  108. package/dist/src/agent/workflow/builtins/content-repurpose.d.ts +1 -1
  109. package/dist/src/agent/workflow/builtins/content-repurpose.js +10 -1
  110. package/dist/src/agent/workflow/builtins/content-repurpose.js.map +1 -1
  111. package/dist/src/agent/workflow/builtins/debug-incident.d.ts +1 -1
  112. package/dist/src/agent/workflow/builtins/debug-incident.js +9 -1
  113. package/dist/src/agent/workflow/builtins/debug-incident.js.map +1 -1
  114. package/dist/src/agent/workflow/builtins/decision-compare.d.ts +1 -1
  115. package/dist/src/agent/workflow/builtins/decision-compare.js +16 -2
  116. package/dist/src/agent/workflow/builtins/decision-compare.js.map +1 -1
  117. package/dist/src/agent/workflow/builtins/implementation-plan.d.ts +1 -1
  118. package/dist/src/agent/workflow/builtins/implementation-plan.js +11 -1
  119. package/dist/src/agent/workflow/builtins/implementation-plan.js.map +1 -1
  120. package/dist/src/agent/workflow/builtins/inbox-triage.d.ts +1 -1
  121. package/dist/src/agent/workflow/builtins/inbox-triage.js +11 -1
  122. package/dist/src/agent/workflow/builtins/inbox-triage.js.map +1 -1
  123. package/dist/src/agent/workflow/builtins/meeting-prep.d.ts +1 -1
  124. package/dist/src/agent/workflow/builtins/meeting-prep.js +9 -1
  125. package/dist/src/agent/workflow/builtins/meeting-prep.js.map +1 -1
  126. package/dist/src/agent/workflow/builtins/multi-perspective-review.d.ts +1 -1
  127. package/dist/src/agent/workflow/builtins/multi-perspective-review.js +9 -1
  128. package/dist/src/agent/workflow/builtins/multi-perspective-review.js.map +1 -1
  129. package/dist/src/agent/workflow/builtins/offer-design.d.ts +1 -1
  130. package/dist/src/agent/workflow/builtins/offer-design.js +10 -1
  131. package/dist/src/agent/workflow/builtins/offer-design.js.map +1 -1
  132. package/dist/src/agent/workflow/builtins/pr-review.d.ts +1 -1
  133. package/dist/src/agent/workflow/builtins/pr-review.js +9 -1
  134. package/dist/src/agent/workflow/builtins/pr-review.js.map +1 -1
  135. package/dist/src/agent/workflow/builtins/release-check.d.ts +1 -1
  136. package/dist/src/agent/workflow/builtins/release-check.js +10 -1
  137. package/dist/src/agent/workflow/builtins/release-check.js.map +1 -1
  138. package/dist/src/agent/workflow/builtins/research.d.ts +1 -1
  139. package/dist/src/agent/workflow/builtins/research.js +16 -2
  140. package/dist/src/agent/workflow/builtins/research.js.map +1 -1
  141. package/dist/src/agent/workflow/builtins/weekly-review.d.ts +1 -1
  142. package/dist/src/agent/workflow/builtins/weekly-review.js +9 -1
  143. package/dist/src/agent/workflow/builtins/weekly-review.js.map +1 -1
  144. package/dist/src/agent/workflow/snapshot.d.ts +10 -0
  145. package/dist/src/agent/workflow/snapshot.js +136 -1
  146. package/dist/src/agent/workflow/snapshot.js.map +1 -1
  147. package/dist/src/channels/catalog/channel-catalog-service.d.ts +2 -1
  148. package/dist/src/channels/catalog/channel-catalog-service.js +133 -16
  149. package/dist/src/channels/catalog/channel-catalog-service.js.map +1 -1
  150. package/dist/src/channels/catalog/channel-catalog-types.d.ts +13 -0
  151. package/dist/src/cli/commands/doctor/checks/channel-config.js +14 -1
  152. package/dist/src/cli/commands/doctor/checks/channel-config.js.map +1 -1
  153. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  154. package/dist/src/config/index.js +2 -2
  155. package/dist/src/config/schema.d.ts +15 -0
  156. package/dist/src/config/schema.js +12 -3
  157. package/dist/src/config/schema.js.map +1 -1
  158. package/dist/src/config/web-patch.d.ts +1 -0
  159. package/dist/src/config/web-patch.js +1 -0
  160. package/dist/src/config/web-patch.js.map +1 -1
  161. package/dist/src/connectors/composio-triggers.d.ts +10 -0
  162. package/dist/src/connectors/composio-triggers.js +32 -0
  163. package/dist/src/connectors/composio-triggers.js.map +1 -0
  164. package/dist/src/connectors/composio.d.ts +54 -0
  165. package/dist/src/connectors/composio.js +334 -0
  166. package/dist/src/connectors/composio.js.map +1 -0
  167. package/dist/src/connectors/health.js +16 -1
  168. package/dist/src/connectors/health.js.map +1 -1
  169. package/dist/src/connectors/install.d.ts +2 -1
  170. package/dist/src/connectors/install.js +50 -13
  171. package/dist/src/connectors/install.js.map +1 -1
  172. package/dist/src/connectors/instances.js +57 -6
  173. package/dist/src/connectors/instances.js.map +1 -1
  174. package/dist/src/connectors/lifecycle.d.ts +3 -0
  175. package/dist/src/connectors/lifecycle.js +23 -0
  176. package/dist/src/connectors/lifecycle.js.map +1 -0
  177. package/dist/src/connectors/materialize.js +2 -1
  178. package/dist/src/connectors/materialize.js.map +1 -1
  179. package/dist/src/connectors/providers.d.ts +1 -0
  180. package/dist/src/connectors/providers.js +10 -2
  181. package/dist/src/connectors/providers.js.map +1 -1
  182. package/dist/src/connectors/registries/modelscope.d.ts +2 -0
  183. package/dist/src/connectors/registries/modelscope.js +205 -0
  184. package/dist/src/connectors/registries/modelscope.js.map +1 -0
  185. package/dist/src/connectors/registries/official-mcp.d.ts +2 -0
  186. package/dist/src/connectors/registries/official-mcp.js +142 -0
  187. package/dist/src/connectors/registries/official-mcp.js.map +1 -0
  188. package/dist/src/connectors/registries/search.d.ts +12 -0
  189. package/dist/src/connectors/registries/search.js +89 -0
  190. package/dist/src/connectors/registries/search.js.map +1 -0
  191. package/dist/src/connectors/registries/smithery.d.ts +2 -0
  192. package/dist/src/connectors/registries/smithery.js +98 -0
  193. package/dist/src/connectors/registries/smithery.js.map +1 -0
  194. package/dist/src/connectors/registries/types.d.ts +20 -0
  195. package/dist/src/connectors/registries/types.js +1 -0
  196. package/dist/src/connectors/secret-store.js +4 -2
  197. package/dist/src/connectors/secret-store.js.map +1 -1
  198. package/dist/src/connectors/setup-secrets.d.ts +15 -0
  199. package/dist/src/connectors/setup-secrets.js +64 -0
  200. package/dist/src/connectors/setup-secrets.js.map +1 -0
  201. package/dist/src/connectors/supervisor.d.ts +15 -0
  202. package/dist/src/connectors/supervisor.js +73 -0
  203. package/dist/src/connectors/supervisor.js.map +1 -0
  204. package/dist/src/connectors/types.d.ts +27 -3
  205. package/dist/src/connectors/usage.js +6 -1
  206. package/dist/src/connectors/usage.js.map +1 -1
  207. package/dist/src/gateway/hono/app.js +5 -1
  208. package/dist/src/gateway/hono/app.js.map +1 -1
  209. package/dist/src/gateway/hono/lib/config-payload.d.ts +3 -1
  210. package/dist/src/gateway/hono/lib/config-payload.js +7 -4
  211. package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
  212. package/dist/src/gateway/hono/middleware/strict-rate-limit.d.ts +4 -0
  213. package/dist/src/gateway/hono/middleware/strict-rate-limit.js +19 -5
  214. package/dist/src/gateway/hono/middleware/strict-rate-limit.js.map +1 -1
  215. package/dist/src/gateway/hono/routes/channels.js +18 -14
  216. package/dist/src/gateway/hono/routes/channels.js.map +1 -1
  217. package/dist/src/gateway/hono/routes/connectors.js +230 -3
  218. package/dist/src/gateway/hono/routes/connectors.js.map +1 -1
  219. package/dist/src/gateway/hono/routes/deps.d.ts +1 -0
  220. package/dist/src/gateway/hono/routes/goals.js +52 -5
  221. package/dist/src/gateway/hono/routes/goals.js.map +1 -1
  222. package/dist/src/gateway/hono/routes/voice.js +14 -1
  223. package/dist/src/gateway/hono/routes/voice.js.map +1 -1
  224. package/dist/src/gateway/hono/sse.js.map +1 -1
  225. package/dist/src/gateway/rate-limit/buckets.d.ts +3 -0
  226. package/dist/src/gateway/rate-limit/buckets.js +12 -0
  227. package/dist/src/gateway/rate-limit/buckets.js.map +1 -1
  228. package/dist/src/gateway/service/agent-runner.d.ts +5 -12
  229. package/dist/src/gateway/service/agent-runner.js +8 -8
  230. package/dist/src/gateway/service/agent-runner.js.map +1 -1
  231. package/dist/src/gateway/service/run-gateway-agent.d.ts +2 -10
  232. package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
  233. package/dist/src/gateway/service.d.ts +1 -1
  234. package/dist/src/gateway/service.js +9 -4
  235. package/dist/src/gateway/service.js.map +1 -1
  236. package/dist/src/gateway/user-turn-input.d.ts +18 -0
  237. package/dist/src/gateway/user-turn-input.js +16 -0
  238. package/dist/src/gateway/user-turn-input.js.map +1 -0
  239. package/dist/src/goals/goal-queue-store.d.ts +2 -1
  240. package/dist/src/goals/goal-queue-store.js +2 -2
  241. package/dist/src/goals/goal-queue-store.js.map +1 -1
  242. package/dist/src/goals/goal-queue-types.d.ts +4 -3
  243. package/dist/src/goals/goal-runner.js +25 -3
  244. package/dist/src/goals/goal-runner.js.map +1 -1
  245. package/dist/src/goals/goal-service.d.ts +6 -1
  246. package/dist/src/goals/goal-service.js +9 -2
  247. package/dist/src/goals/goal-service.js.map +1 -1
  248. package/dist/src/goals/goal-store.d.ts +7 -1
  249. package/dist/src/goals/goal-store.js +42 -0
  250. package/dist/src/goals/goal-store.js.map +1 -1
  251. package/dist/src/goals/types.d.ts +10 -0
  252. package/dist/src/storage/sqlite/migrations/011_goal_context_messages.sql +8 -0
  253. package/dist/src/storage/sqlite/migrations/runner.d.ts +2 -2
  254. package/dist/src/storage/sqlite/migrations/runner.js +4 -4
  255. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  256. package/dist/src/storage/sqlite/migrations/runner.ts +2 -2
  257. package/dist/src/storage/sqlite/schema.js +3 -3
  258. package/dist/src/storage/sqlite/schema.sql +9 -0
  259. package/dist/src/tui/backends/embedded-backend.d.ts +8 -1
  260. package/dist/src/tui/backends/embedded-backend.js +73 -0
  261. package/dist/src/tui/backends/embedded-backend.js.map +1 -1
  262. package/dist/src/tui/backends/gateway-sse-backend.d.ts +2 -1
  263. package/dist/src/tui/backends/gateway-sse-backend.js +24 -0
  264. package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
  265. package/dist/src/tui/components/chat-log.d.ts +2 -0
  266. package/dist/src/tui/components/chat-log.js +19 -0
  267. package/dist/src/tui/components/chat-log.js.map +1 -1
  268. package/dist/src/tui/extension-host/autocomplete.d.ts +3 -2
  269. package/dist/src/tui/extension-host/autocomplete.js +17 -3
  270. package/dist/src/tui/extension-host/autocomplete.js.map +1 -1
  271. package/dist/src/tui/extension-host/runtime.d.ts +5 -0
  272. package/dist/src/tui/extension-host/runtime.js +1 -1
  273. package/dist/src/tui/extension-host/runtime.js.map +1 -1
  274. package/dist/src/tui/tui-backend.d.ts +14 -0
  275. package/dist/src/tui/tui-commands.d.ts +16 -3
  276. package/dist/src/tui/tui-commands.js +47 -19
  277. package/dist/src/tui/tui-commands.js.map +1 -1
  278. package/dist/src/tui/tui-startup-text.js +1 -1
  279. package/dist/src/tui/tui-startup-text.js.map +1 -1
  280. package/dist/src/tui/tui.js +72 -1
  281. package/dist/src/tui/tui.js.map +1 -1
  282. package/dist/src/voice/tts/openai-compatible-speech.d.ts +1 -1
  283. package/dist/src/voice/tts/openai-compatible-speech.js.map +1 -1
  284. package/dist/src/voice/tts/providers/edge-speech.js +14 -2
  285. package/dist/src/voice/tts/providers/edge-speech.js.map +1 -1
  286. package/dist/src/voice/tts/speak-core.js +3 -3
  287. package/dist/src/voice/tts/speak-core.js.map +1 -1
  288. package/dist/src/voice/tts/speech-provider-types.d.ts +2 -2
  289. package/dist/src/voice/tts/types.js +1 -1
  290. package/dist/src/voice/tts/types.js.map +1 -1
  291. package/dist/src/workflows/domain/result.d.ts +0 -1
  292. package/dist/src/workflows/engine/workflow-engine.js +7 -23
  293. package/dist/src/workflows/engine/workflow-engine.js.map +1 -1
  294. package/package.json +2 -2
  295. package/dist/gateway/static/root/assets/agent-browser-settings-page-CWh7trjd.js +0 -6
  296. package/dist/gateway/static/root/assets/agent-defaults-tabbed-page-kC8OuLoj.js +0 -1
  297. package/dist/gateway/static/root/assets/agents-DJWG26Si.js +0 -71
  298. package/dist/gateway/static/root/assets/app-management-settings-panel-v24i25Oj.js +0 -1
  299. package/dist/gateway/static/root/assets/apps-page-0V3DXZ7J.js +0 -1
  300. package/dist/gateway/static/root/assets/channels-settings-CIKOkYdO.js +0 -1
  301. package/dist/gateway/static/root/assets/connectors-settings-ClrDS5jH.js +0 -2
  302. package/dist/gateway/static/root/assets/cron-page-Chi_4u4P.js +0 -3
  303. package/dist/gateway/static/root/assets/dreaming-settings-B0_rs2tj.js +0 -1
  304. package/dist/gateway/static/root/assets/gateway-settings-1PDi4lI0.js +0 -1
  305. package/dist/gateway/static/root/assets/goal-detail-page-D_wwXL-t.js +0 -1
  306. package/dist/gateway/static/root/assets/goals-page-DZ2toZ8y.js +0 -2
  307. package/dist/gateway/static/root/assets/goals-settings-LFlwtIKE.js +0 -1
  308. package/dist/gateway/static/root/assets/heartbeat-settings-BHHAs8ZX.js +0 -1
  309. package/dist/gateway/static/root/assets/index-DefIdW9q.js +0 -122
  310. package/dist/gateway/static/root/assets/index-DzmWBkq-.css +0 -1
  311. package/dist/gateway/static/root/assets/logs-page-j_D2QTuQ.js +0 -2
  312. package/dist/gateway/static/root/assets/messages-BN_WiUg0.js +0 -3
  313. package/dist/gateway/static/root/assets/models-hub-panel-CMH_TmZz.js +0 -2
  314. package/dist/gateway/static/root/assets/note-detail-page-O18BrQPq.js +0 -2
  315. package/dist/gateway/static/root/assets/notes-page-B_ZYCmY9.js +0 -1
  316. package/dist/gateway/static/root/assets/remote-access-hub-CwKgWixY.js +0 -1
  317. package/dist/gateway/static/root/assets/sessions-page-C4fcD-7B.js +0 -1
  318. package/dist/gateway/static/root/assets/settings-advanced-gate-Ad2OJ86w.js +0 -1
  319. package/dist/gateway/static/root/assets/settings-page-D2BMY5wo.js +0 -1
  320. package/dist/gateway/static/root/assets/setup-status-panel-aWwxl_E1.js +0 -1
  321. package/dist/gateway/static/root/assets/shares-settings-DUE83uIf.js +0 -1
  322. package/dist/gateway/static/root/assets/skill-reload-api-mWKJcrog.js +0 -1
  323. package/dist/gateway/static/root/assets/skills-page-0Sx1Q58n.js +0 -2
  324. package/dist/gateway/static/root/assets/system-settings-panel-hqaBF5aG.js +0 -1
  325. package/dist/gateway/static/root/assets/use-agent-defaults-form-zbVIQLOA.js +0 -1
  326. package/dist/gateway/static/root/assets/voice-api-key-field-D6HR0hR2.js +0 -1
  327. package/dist/gateway/static/root/assets/workflow-page.utils-C0Uq3xyq.js +0 -1
  328. package/dist/gateway/static/root/assets/workflows-page-DW0fmWhQ.js +0 -18
@@ -10,7 +10,7 @@ import { buildGatewayConsoleCspHeader } from "../security/csp.js";
10
10
  import { checkBrowserOrigin } from "../security/origin-check.js";
11
11
  import { auth } from "./middleware/auth.js";
12
12
  import { operatorScopes } from "./middleware/scopes.js";
13
- import { createStrictRateLimitMiddleware } from "./middleware/strict-rate-limit.js";
13
+ import { createChannelRateLimitMiddleware, createStrictRateLimitMiddleware } from "./middleware/strict-rate-limit.js";
14
14
  import { logContextMiddleware } from "./middleware/log-context.js";
15
15
  import { logger } from "./middleware/logger.js";
16
16
  import { routeErrorMiddleware } from "./middleware/route-errors.js";
@@ -209,6 +209,10 @@ function createHonoApp(config) {
209
209
  trustedProxies: service.currentConfig.gateway?.trustedProxies,
210
210
  allowRealIpFallback: service.currentConfig.gateway?.allowRealIpFallback === true
211
211
  }) }),
212
+ channelRateLimitMiddleware: createChannelRateLimitMiddleware({ getTrustedProxyContext: () => ({
213
+ trustedProxies: service.currentConfig.gateway?.trustedProxies,
214
+ allowRealIpFallback: service.currentConfig.gateway?.allowRealIpFallback === true
215
+ }) }),
212
216
  sseConfig: {
213
217
  service,
214
218
  maxSseConnections: service.currentConfig.gateway.maxSseConnections
@@ -1 +1 @@
1
- {"version":3,"file":"app.js","names":[],"sources":["../../../../src/gateway/hono/app.ts"],"sourcesContent":["import { Hono } from 'hono';\nimport type { Context } from 'hono';\nimport { cors } from 'hono/cors';\nimport { createMiddleware } from 'hono/factory';\nimport { bodyLimit } from 'hono/body-limit';\nimport { getConnInfo } from '@hono/node-server/conninfo';\n\nimport { resolveGatewayEffectiveHost } from '../../config/gateway-bind.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type { GatewayService } from '../service.js';\nimport { resolveAllowedBrowserOrigins, resolveGatewayServiceListenPort } from '../host.js';\nimport { loadTunnelState } from '../../tunnel/tunnel-state.js';\nimport { maxWebchatAgentRequestBodyBytes } from '../chat-limits.js';\nimport { buildGatewayConsoleCspHeader } from '../security/csp.js';\nimport { checkBrowserOrigin } from '../security/origin-check.js';\nimport { isLoopbackIpAddress, isTrustedProxyAddress } from '../client-ip.js';\nimport { resolveReverseProxyPublicUrl } from '../public-url.js';\nimport { auth } from './middleware/auth.js';\nimport { operatorScopes } from './middleware/scopes.js';\nimport { createStrictRateLimitMiddleware } from './middleware/strict-rate-limit.js';\nimport { logContextMiddleware } from './middleware/log-context.js';\nimport { logger } from './middleware/logger.js';\nimport { routeErrorMiddleware } from './middleware/route-errors.js';\nimport { registerPublicExtensionAssetRoutes } from './routes/auth-registry-extensions.js';\nimport { registerAuthenticatedRoutes } from './routes/index.js';\nimport { registerPublicGatewayRoutes } from './routes/public-gateway.js';\nimport { resetLazyRouteBundlesForTests } from './routes/lazy-fallback.js';\nimport { prewarmStaticUiCache } from './lib/static-ui.js';\nimport { registerSiteShareMiddleware } from '../../share/site-share-router.js';\nconst log = createLogger('Gateway:App');\n\nexport interface HonoAppConfig {\n service: GatewayService;\n token?: string;\n}\n\n/**\n * Extension sandbox HTML under `/api/extensions/:id/assets/*` ships its own CSP\n * (`frame-ancestors 'self'`). The global gateway middleware must not overwrite it\n * with `frame-ancestors 'none'` / `X-Frame-Options: DENY`, or the console cannot embed iframes.\n */\nexport function isExtensionGatewayUiAssetPath(path: string): boolean {\n return /^\\/api\\/extensions\\/[^/]+\\/assets\\//.test(path);\n}\n\nexport function createHonoApp(config: HonoAppConfig): Hono {\n if (process.env.VITEST) {\n resetLazyRouteBundlesForTests();\n }\n const { service, token } = config;\n const app = new Hono();\n\n const gatewayPort = resolveGatewayServiceListenPort(service);\n\n const resolveBrowserOrigins = (): string[] =>\n resolveAllowedBrowserOrigins({\n configuredOrigins: service.currentConfig.gateway.corsOrigins,\n port: gatewayPort,\n bindHost: resolveGatewayEffectiveHost(service.currentConfig),\n tunnelPublicUrl: loadTunnelState()?.publicUrl,\n reverseProxyPublicUrl: resolveReverseProxyPublicUrl(service.currentConfig),\n });\n\n /**\n * TCP source for the in-flight request, normalized for trusted-proxy checks.\n * Returns undefined when the runtime doesn't expose conninfo (tests, mocks).\n */\n const resolveRequestRemoteAddress = (c: Context): string | undefined => {\n try {\n return getConnInfo(c).remote.address;\n } catch {\n return undefined;\n }\n };\n\n /**\n * A request's TCP source qualifies as a \"trusted proxy hop\" when it's\n * loopback (the user's own machine, where any reverse proxy lives) or\n * listed in `gateway.trustedProxies`. We use this signal to safely\n * auto-allow same-host Origins through CSRF without requiring a manual\n * `corsOrigins` entry for every reverse-proxy hostname.\n */\n const isRequestFromTrustedProxy = (c: Context): boolean => {\n const remote = resolveRequestRemoteAddress(c);\n if (!remote) return false;\n if (isLoopbackIpAddress(remote)) return true;\n return isTrustedProxyAddress(remote, service.currentConfig.gateway?.trustedProxies);\n };\n\n app.use(logContextMiddleware());\n app.use(logger({\n trustedProxies: service.currentConfig.gateway?.trustedProxies,\n allowRealIpFallback: service.currentConfig.gateway?.allowRealIpFallback === true,\n }));\n\n // Site-share middleware runs BEFORE CORS/CSP — it owns the request when the\n // Host header matches `*.<publicHostSuffix>` (default `*.share.xopc.ai`) or\n // the path starts with `/site/:token/`. Otherwise it falls through.\n registerSiteShareMiddleware(app, service);\n app.use(\n cors({\n origin: (origin) => {\n const allowed = resolveBrowserOrigins();\n if (!origin) {\n return allowed[0] ?? `http://127.0.0.1:${gatewayPort}`;\n }\n const normalized = origin.toLowerCase();\n const hit = allowed.find((entry) => entry.toLowerCase() === normalized);\n if (hit) return origin;\n return allowed.includes('*') ? '*' : '';\n },\n allowMethods: ['GET', 'POST', 'PATCH', 'DELETE', 'OPTIONS'],\n allowHeaders: ['Content-Type', 'Authorization', 'Accept', 'X-Session-Id', 'Last-Event-ID'],\n credentials: true,\n maxAge: 86400,\n }),\n );\n\n // Build CSP header once at startup (no inline script hashes needed for SPA)\n const gatewayConsoleCsp = buildGatewayConsoleCspHeader();\n\n // Security headers middleware\n app.use(createMiddleware(async (c, next) => {\n await next();\n if (isExtensionGatewayUiAssetPath(c.req.path)) {\n return;\n }\n c.header('X-Frame-Options', 'DENY');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('Referrer-Policy', 'strict-origin-when-cross-origin');\n c.header('X-XSS-Protection', '1; mode=block');\n // microphone=(self): allow same-origin chat voice (composer). microphone=() breaks packaged Electron loading the gateway SPA.\n c.header('Permissions-Policy', 'camera=(), microphone=(self), geolocation=()');\n c.header('Content-Security-Policy', gatewayConsoleCsp);\n }));\n\n // Browser Origin check middleware for API routes (CSRF protection).\n // Non-browser requests (no Origin header) pass through — they are\n // authenticated by the token middleware instead.\n const allowHostHeaderOriginFallback =\n service.currentConfig.gateway?.dangerouslyAllowHostHeaderOriginFallback === true;\n app.use('/api/*', createMiddleware(async (c, next) => {\n // Sandboxed extension iframes (no allow-same-origin) send `Origin: null`.\n // `checkBrowserOrigin` rejects that; these routes rely on CSP instead\n // (`registerPublicExtensionAssetRoutes`).\n if (isExtensionGatewayUiAssetPath(c.req.path)) {\n return next();\n }\n\n const origin = c.req.header('origin');\n if (!origin || origin.trim().toLowerCase() === 'null') {\n // Native apps / opaque origins — authenticated via Bearer token\n return next();\n }\n\n const result = checkBrowserOrigin({\n requestHost: c.req.header('host'),\n origin,\n allowedOrigins: resolveBrowserOrigins(),\n allowHostHeaderOriginFallback,\n autoAllowSameHostFromTrustedProxy: isRequestFromTrustedProxy(c),\n isLocalClient: false,\n });\n\n if (!result.ok) {\n log.warn(\n {\n origin,\n requestHost: c.req.header('host'),\n reason: 'reason' in result ? result.reason : 'unknown',\n path: c.req.path,\n method: c.req.method,\n },\n `Browser origin check failed: ${origin} not in allowed list`,\n );\n return c.json({ error: 'Forbidden', message: 'Origin not allowed' }, 403);\n }\n\n return next();\n }));\n\n const DEFAULT_API_BODY_MAX = 1 * 1024 * 1024;\n const SKILL_UPLOAD_BODY_MAX = 10 * 1024 * 1024;\n const NOTE_MEDIA_BODY_MAX = 25 * 1024 * 1024;\n const VOICE_TRANSCRIBE_BODY_MAX = 35 * 1024 * 1024;\n const WEBCHAT_AGENT_BODY_MAX = maxWebchatAgentRequestBodyBytes();\n\n const isNoteMediaUploadRequest = (path: string, method: string, contentType: string | undefined): boolean => {\n if (method !== 'POST') return false;\n if (/^\\/api\\/notes\\/[^/]+\\/media$/.test(path)) return true;\n return path === '/api/notes' && contentType?.includes('multipart/form-data') === true;\n };\n\n app.use('/api/skills/upload', bodyLimit({\n maxSize: SKILL_UPLOAD_BODY_MAX,\n onError: (c) => {\n log.warn({ path: c.req.path, maxSizeMb: 10 }, 'Request body too large: skills upload exceeds 10MB limit');\n return c.json({ error: 'Skill package too large', maxSize: '10MB' }, 413);\n },\n }));\n\n app.use('/api/*', async (c, next) => {\n const contentType = c.req.header('content-type');\n const maxSize = c.req.path === '/api/agent'\n ? WEBCHAT_AGENT_BODY_MAX\n : c.req.path === '/api/skills/upload'\n ? SKILL_UPLOAD_BODY_MAX\n : c.req.path === '/api/voice/transcribe'\n ? VOICE_TRANSCRIBE_BODY_MAX\n : isNoteMediaUploadRequest(c.req.path, c.req.method, contentType)\n ? NOTE_MEDIA_BODY_MAX\n : DEFAULT_API_BODY_MAX;\n const maxSizeMb = Math.ceil(maxSize / (1024 * 1024));\n return bodyLimit({\n maxSize,\n onError: (ctx) => {\n log.warn({ path: ctx.req.path, maxSizeMb }, `Request body too large: exceeds ${maxSizeMb}MB limit`);\n return ctx.json({ error: 'Request body too large', maxSize: `${maxSizeMb}MB` }, 413);\n },\n })(c, next);\n });\n\n registerPublicGatewayRoutes(app, service);\n\n // Extension UI assets are served without auth: sandboxed iframes (no allow-same-origin)\n // have an opaque origin of `null` and cannot forward the ?token= from the parent HTML URL.\n // Security is enforced by the strict CSP (frame-ancestors 'self') on every response.\n registerPublicExtensionAssetRoutes(app, service);\n\n const authenticated = new Hono();\n authenticated.use(routeErrorMiddleware());\n authenticated.use(\n auth({\n token,\n getGatewayAuth: () => service.currentConfig.gateway?.auth,\n getResolvedAuth: () => {\n if (typeof service.getResolvedAuth === 'function') {\n return service.getResolvedAuth();\n }\n return token ? { mode: 'token', token } : { mode: 'none' };\n },\n getTrustedProxyContext: () => ({\n trustedProxies: service.currentConfig.gateway?.trustedProxies,\n allowRealIpFallback: service.currentConfig.gateway?.allowRealIpFallback === true,\n }),\n }),\n );\n authenticated.use(operatorScopes());\n\n const strictRateLimitMiddleware = createStrictRateLimitMiddleware({\n getTrustedProxyContext: () => ({\n trustedProxies: service.currentConfig.gateway?.trustedProxies,\n allowRealIpFallback: service.currentConfig.gateway?.allowRealIpFallback === true,\n }),\n });\n\n const sseConfig = {\n service,\n maxSseConnections: service.currentConfig.gateway.maxSseConnections,\n };\n\n registerAuthenticatedRoutes(app, authenticated, {\n service,\n strictRateLimitMiddleware,\n sseConfig,\n });\n\n const prewarm = prewarmStaticUiCache();\n if (prewarm.loaded > 0) {\n log.debug({ loaded: prewarm.loaded, missing: prewarm.missing }, 'Static UI cache prewarmed');\n }\n\n app.route('/', authenticated);\n\n app.notFound((c) => {\n const isApiRoute = c.req.path.startsWith('/api/');\n const fields = { path: c.req.path, method: c.req.method };\n if (isApiRoute) {\n log.warn(fields, 'Route not found');\n } else {\n log.debug(fields, 'Route not found');\n }\n return c.json({ error: 'Not found' }, 404);\n });\n\n app.onError((err, c) => {\n log.error(\n {\n err,\n phase: 'gateway.http.unhandled',\n path: c.req.path,\n method: c.req.method,\n userAgent: c.req.header('user-agent'),\n },\n `Hono error on ${c.req.method} ${c.req.path}: ${err instanceof Error ? err.message : String(err)}`,\n );\n return c.json({ error: 'Internal server error' }, 500);\n });\n\n return app;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;aAQqD;AAqBrD,MAAM,MAAM,aAAa,cAAc;;;;;;AAYvC,SAAgB,8BAA8B,MAAuB;AACnE,QAAO,sCAAsC,KAAK,KAAK;;AAGzD,SAAgB,cAAc,QAA6B;AACzD,KAAI,QAAQ,IAAI,OACd,gCAA+B;CAEjC,MAAM,EAAE,SAAS,UAAU;CAC3B,MAAM,MAAM,IAAI,MAAM;CAEtB,MAAM,cAAc,gCAAgC,QAAQ;CAE5D,MAAM,8BACJ,6BAA6B;EAC3B,mBAAmB,QAAQ,cAAc,QAAQ;EACjD,MAAM;EACN,UAAU,4BAA4B,QAAQ,cAAc;EAC5D,iBAAiB,iBAAiB,EAAE;EACpC,uBAAuB,6BAA6B,QAAQ,cAAc;EAC3E,CAAC;;;;;CAMJ,MAAM,+BAA+B,MAAmC;AACtE,MAAI;AACF,UAAO,YAAY,EAAE,CAAC,OAAO;UACvB;AACN;;;;;;;;;;CAWJ,MAAM,6BAA6B,MAAwB;EACzD,MAAM,SAAS,4BAA4B,EAAE;AAC7C,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,oBAAoB,OAAO,CAAE,QAAO;AACxC,SAAO,sBAAsB,QAAQ,QAAQ,cAAc,SAAS,eAAe;;AAGrF,KAAI,IAAI,sBAAsB,CAAC;AAC/B,KAAI,IAAI,OAAO;EACb,gBAAgB,QAAQ,cAAc,SAAS;EAC/C,qBAAqB,QAAQ,cAAc,SAAS,wBAAwB;EAC7E,CAAC,CAAC;AAKH,6BAA4B,KAAK,QAAQ;AACzC,KAAI,IACF,KAAK;EACH,SAAS,WAAW;GAClB,MAAM,UAAU,uBAAuB;AACvC,OAAI,CAAC,OACH,QAAO,QAAQ,MAAM,oBAAoB;GAE3C,MAAM,aAAa,OAAO,aAAa;AAEvC,OADY,QAAQ,MAAM,UAAU,MAAM,aAAa,KAAK,WACrD,CAAE,QAAO;AAChB,UAAO,QAAQ,SAAS,IAAI,GAAG,MAAM;;EAEvC,cAAc;GAAC;GAAO;GAAQ;GAAS;GAAU;GAAU;EAC3D,cAAc;GAAC;GAAgB;GAAiB;GAAU;GAAgB;GAAgB;EAC1F,aAAa;EACb,QAAQ;EACT,CAAC,CACH;CAGD,MAAM,oBAAoB,8BAA8B;AAGxD,KAAI,IAAI,iBAAiB,OAAO,GAAG,SAAS;AAC1C,QAAM,MAAM;AACZ,MAAI,8BAA8B,EAAE,IAAI,KAAK,CAC3C;AAEF,IAAE,OAAO,mBAAmB,OAAO;AACnC,IAAE,OAAO,0BAA0B,UAAU;AAC7C,IAAE,OAAO,mBAAmB,kCAAkC;AAC9D,IAAE,OAAO,oBAAoB,gBAAgB;AAE7C,IAAE,OAAO,sBAAsB,+CAA+C;AAC9E,IAAE,OAAO,2BAA2B,kBAAkB;GACtD,CAAC;CAKH,MAAM,gCACJ,QAAQ,cAAc,SAAS,6CAA6C;AAC9E,KAAI,IAAI,UAAU,iBAAiB,OAAO,GAAG,SAAS;AAIpD,MAAI,8BAA8B,EAAE,IAAI,KAAK,CAC3C,QAAO,MAAM;EAGf,MAAM,SAAS,EAAE,IAAI,OAAO,SAAS;AACrC,MAAI,CAAC,UAAU,OAAO,MAAM,CAAC,aAAa,KAAK,OAE7C,QAAO,MAAM;EAGf,MAAM,SAAS,mBAAmB;GAChC,aAAa,EAAE,IAAI,OAAO,OAAO;GACjC;GACA,gBAAgB,uBAAuB;GACvC;GACA,mCAAmC,0BAA0B,EAAE;GAC/D,eAAe;GAChB,CAAC;AAEF,MAAI,CAAC,OAAO,IAAI;AACd,OAAI,KACF;IACE;IACA,aAAa,EAAE,IAAI,OAAO,OAAO;IACjC,QAAQ,YAAY,SAAS,OAAO,SAAS;IAC7C,MAAM,EAAE,IAAI;IACZ,QAAQ,EAAE,IAAI;IACf,EACD,gCAAgC,OAAO,sBACxC;AACD,UAAO,EAAE,KAAK;IAAE,OAAO;IAAa,SAAS;IAAsB,EAAE,IAAI;;AAG3E,SAAO,MAAM;GACb,CAAC;CAEH,MAAM,uBAAuB,IAAI,OAAO;CACxC,MAAM,wBAAwB,KAAK,OAAO;CAC1C,MAAM,sBAAsB,KAAK,OAAO;CACxC,MAAM,4BAA4B,KAAK,OAAO;CAC9C,MAAM,yBAAyB,iCAAiC;CAEhE,MAAM,4BAA4B,MAAc,QAAgB,gBAA6C;AAC3G,MAAI,WAAW,OAAQ,QAAO;AAC9B,MAAI,+BAA+B,KAAK,KAAK,CAAE,QAAO;AACtD,SAAO,SAAS,gBAAgB,aAAa,SAAS,sBAAsB,KAAK;;AAGnF,KAAI,IAAI,sBAAsB,UAAU;EACtC,SAAS;EACT,UAAU,MAAM;AACd,OAAI,KAAK;IAAE,MAAM,EAAE,IAAI;IAAM,WAAW;IAAI,EAAE,2DAA2D;AACzG,UAAO,EAAE,KAAK;IAAE,OAAO;IAA2B,SAAS;IAAQ,EAAE,IAAI;;EAE5E,CAAC,CAAC;AAEH,KAAI,IAAI,UAAU,OAAO,GAAG,SAAS;EACnC,MAAM,cAAc,EAAE,IAAI,OAAO,eAAe;EAChD,MAAM,UAAU,EAAE,IAAI,SAAS,eAC3B,yBACA,EAAE,IAAI,SAAS,uBACb,wBACA,EAAE,IAAI,SAAS,0BACb,4BACA,yBAAyB,EAAE,IAAI,MAAM,EAAE,IAAI,QAAQ,YAAY,GAC7D,sBACA;EACV,MAAM,YAAY,KAAK,KAAK,WAAW,OAAO,MAAM;AACpD,SAAO,UAAU;GACf;GACA,UAAU,QAAQ;AAChB,QAAI,KAAK;KAAE,MAAM,IAAI,IAAI;KAAM;KAAW,EAAE,mCAAmC,UAAU,UAAU;AACnG,WAAO,IAAI,KAAK;KAAE,OAAO;KAA0B,SAAS,GAAG,UAAU;KAAK,EAAE,IAAI;;GAEvF,CAAC,CAAC,GAAG,KAAK;GACX;AAEF,6BAA4B,KAAK,QAAQ;AAKzC,oCAAmC,KAAK,QAAQ;CAEhD,MAAM,gBAAgB,IAAI,MAAM;AAChC,eAAc,IAAI,sBAAsB,CAAC;AACzC,eAAc,IACZ,KAAK;EACH;EACA,sBAAsB,QAAQ,cAAc,SAAS;EACrD,uBAAuB;AACrB,OAAI,OAAO,QAAQ,oBAAoB,WACrC,QAAO,QAAQ,iBAAiB;AAElC,UAAO,QAAQ;IAAE,MAAM;IAAS;IAAO,GAAG,EAAE,MAAM,QAAQ;;EAE5D,+BAA+B;GAC7B,gBAAgB,QAAQ,cAAc,SAAS;GAC/C,qBAAqB,QAAQ,cAAc,SAAS,wBAAwB;GAC7E;EACF,CAAC,CACH;AACD,eAAc,IAAI,gBAAgB,CAAC;AAcnC,6BAA4B,KAAK,eAAe;EAC9C;EACA,2BAdgC,gCAAgC,EAChE,+BAA+B;GAC7B,gBAAgB,QAAQ,cAAc,SAAS;GAC/C,qBAAqB,QAAQ,cAAc,SAAS,wBAAwB;GAC7E,GACF,CAS0B;EACzB,WAAA;GAPA;GACA,mBAAmB,QAAQ,cAAc,QAAQ;GAMxC;EACV,CAAC;CAEF,MAAM,UAAU,sBAAsB;AACtC,KAAI,QAAQ,SAAS,EACnB,KAAI,MAAM;EAAE,QAAQ,QAAQ;EAAQ,SAAS,QAAQ;EAAS,EAAE,4BAA4B;AAG9F,KAAI,MAAM,KAAK,cAAc;AAE7B,KAAI,UAAU,MAAM;EAClB,MAAM,aAAa,EAAE,IAAI,KAAK,WAAW,QAAQ;EACjD,MAAM,SAAS;GAAE,MAAM,EAAE,IAAI;GAAM,QAAQ,EAAE,IAAI;GAAQ;AACzD,MAAI,WACF,KAAI,KAAK,QAAQ,kBAAkB;MAEnC,KAAI,MAAM,QAAQ,kBAAkB;AAEtC,SAAO,EAAE,KAAK,EAAE,OAAO,aAAa,EAAE,IAAI;GAC1C;AAEF,KAAI,SAAS,KAAK,MAAM;AACtB,MAAI,MACF;GACE;GACA,OAAO;GACP,MAAM,EAAE,IAAI;GACZ,QAAQ,EAAE,IAAI;GACd,WAAW,EAAE,IAAI,OAAO,aAAa;GACtC,EACD,iBAAiB,EAAE,IAAI,OAAO,GAAG,EAAE,IAAI,KAAK,IAAI,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GACjG;AACD,SAAO,EAAE,KAAK,EAAE,OAAO,yBAAyB,EAAE,IAAI;GACtD;AAEF,QAAO"}
1
+ {"version":3,"file":"app.js","names":[],"sources":["../../../../src/gateway/hono/app.ts"],"sourcesContent":["import { Hono } from 'hono';\nimport type { Context } from 'hono';\nimport { cors } from 'hono/cors';\nimport { createMiddleware } from 'hono/factory';\nimport { bodyLimit } from 'hono/body-limit';\nimport { getConnInfo } from '@hono/node-server/conninfo';\n\nimport { resolveGatewayEffectiveHost } from '../../config/gateway-bind.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type { GatewayService } from '../service.js';\nimport { resolveAllowedBrowserOrigins, resolveGatewayServiceListenPort } from '../host.js';\nimport { loadTunnelState } from '../../tunnel/tunnel-state.js';\nimport { maxWebchatAgentRequestBodyBytes } from '../chat-limits.js';\nimport { buildGatewayConsoleCspHeader } from '../security/csp.js';\nimport { checkBrowserOrigin } from '../security/origin-check.js';\nimport { isLoopbackIpAddress, isTrustedProxyAddress } from '../client-ip.js';\nimport { resolveReverseProxyPublicUrl } from '../public-url.js';\nimport { auth } from './middleware/auth.js';\nimport { operatorScopes } from './middleware/scopes.js';\nimport {\n createChannelRateLimitMiddleware,\n createStrictRateLimitMiddleware,\n} from './middleware/strict-rate-limit.js';\nimport { logContextMiddleware } from './middleware/log-context.js';\nimport { logger } from './middleware/logger.js';\nimport { routeErrorMiddleware } from './middleware/route-errors.js';\nimport { registerPublicExtensionAssetRoutes } from './routes/auth-registry-extensions.js';\nimport { registerAuthenticatedRoutes } from './routes/index.js';\nimport { registerPublicGatewayRoutes } from './routes/public-gateway.js';\nimport { resetLazyRouteBundlesForTests } from './routes/lazy-fallback.js';\nimport { prewarmStaticUiCache } from './lib/static-ui.js';\nimport { registerSiteShareMiddleware } from '../../share/site-share-router.js';\nconst log = createLogger('Gateway:App');\n\nexport interface HonoAppConfig {\n service: GatewayService;\n token?: string;\n}\n\n/**\n * Extension sandbox HTML under `/api/extensions/:id/assets/*` ships its own CSP\n * (`frame-ancestors 'self'`). The global gateway middleware must not overwrite it\n * with `frame-ancestors 'none'` / `X-Frame-Options: DENY`, or the console cannot embed iframes.\n */\nexport function isExtensionGatewayUiAssetPath(path: string): boolean {\n return /^\\/api\\/extensions\\/[^/]+\\/assets\\//.test(path);\n}\n\nexport function createHonoApp(config: HonoAppConfig): Hono {\n if (process.env.VITEST) {\n resetLazyRouteBundlesForTests();\n }\n const { service, token } = config;\n const app = new Hono();\n\n const gatewayPort = resolveGatewayServiceListenPort(service);\n\n const resolveBrowserOrigins = (): string[] =>\n resolveAllowedBrowserOrigins({\n configuredOrigins: service.currentConfig.gateway.corsOrigins,\n port: gatewayPort,\n bindHost: resolveGatewayEffectiveHost(service.currentConfig),\n tunnelPublicUrl: loadTunnelState()?.publicUrl,\n reverseProxyPublicUrl: resolveReverseProxyPublicUrl(service.currentConfig),\n });\n\n /**\n * TCP source for the in-flight request, normalized for trusted-proxy checks.\n * Returns undefined when the runtime doesn't expose conninfo (tests, mocks).\n */\n const resolveRequestRemoteAddress = (c: Context): string | undefined => {\n try {\n return getConnInfo(c).remote.address;\n } catch {\n return undefined;\n }\n };\n\n /**\n * A request's TCP source qualifies as a \"trusted proxy hop\" when it's\n * loopback (the user's own machine, where any reverse proxy lives) or\n * listed in `gateway.trustedProxies`. We use this signal to safely\n * auto-allow same-host Origins through CSRF without requiring a manual\n * `corsOrigins` entry for every reverse-proxy hostname.\n */\n const isRequestFromTrustedProxy = (c: Context): boolean => {\n const remote = resolveRequestRemoteAddress(c);\n if (!remote) return false;\n if (isLoopbackIpAddress(remote)) return true;\n return isTrustedProxyAddress(remote, service.currentConfig.gateway?.trustedProxies);\n };\n\n app.use(logContextMiddleware());\n app.use(logger({\n trustedProxies: service.currentConfig.gateway?.trustedProxies,\n allowRealIpFallback: service.currentConfig.gateway?.allowRealIpFallback === true,\n }));\n\n // Site-share middleware runs BEFORE CORS/CSP — it owns the request when the\n // Host header matches `*.<publicHostSuffix>` (default `*.share.xopc.ai`) or\n // the path starts with `/site/:token/`. Otherwise it falls through.\n registerSiteShareMiddleware(app, service);\n app.use(\n cors({\n origin: (origin) => {\n const allowed = resolveBrowserOrigins();\n if (!origin) {\n return allowed[0] ?? `http://127.0.0.1:${gatewayPort}`;\n }\n const normalized = origin.toLowerCase();\n const hit = allowed.find((entry) => entry.toLowerCase() === normalized);\n if (hit) return origin;\n return allowed.includes('*') ? '*' : '';\n },\n allowMethods: ['GET', 'POST', 'PATCH', 'DELETE', 'OPTIONS'],\n allowHeaders: ['Content-Type', 'Authorization', 'Accept', 'X-Session-Id', 'Last-Event-ID'],\n credentials: true,\n maxAge: 86400,\n }),\n );\n\n // Build CSP header once at startup (no inline script hashes needed for SPA)\n const gatewayConsoleCsp = buildGatewayConsoleCspHeader();\n\n // Security headers middleware\n app.use(createMiddleware(async (c, next) => {\n await next();\n if (isExtensionGatewayUiAssetPath(c.req.path)) {\n return;\n }\n c.header('X-Frame-Options', 'DENY');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('Referrer-Policy', 'strict-origin-when-cross-origin');\n c.header('X-XSS-Protection', '1; mode=block');\n // microphone=(self): allow same-origin chat voice (composer). microphone=() breaks packaged Electron loading the gateway SPA.\n c.header('Permissions-Policy', 'camera=(), microphone=(self), geolocation=()');\n c.header('Content-Security-Policy', gatewayConsoleCsp);\n }));\n\n // Browser Origin check middleware for API routes (CSRF protection).\n // Non-browser requests (no Origin header) pass through — they are\n // authenticated by the token middleware instead.\n const allowHostHeaderOriginFallback =\n service.currentConfig.gateway?.dangerouslyAllowHostHeaderOriginFallback === true;\n app.use('/api/*', createMiddleware(async (c, next) => {\n // Sandboxed extension iframes (no allow-same-origin) send `Origin: null`.\n // `checkBrowserOrigin` rejects that; these routes rely on CSP instead\n // (`registerPublicExtensionAssetRoutes`).\n if (isExtensionGatewayUiAssetPath(c.req.path)) {\n return next();\n }\n\n const origin = c.req.header('origin');\n if (!origin || origin.trim().toLowerCase() === 'null') {\n // Native apps / opaque origins — authenticated via Bearer token\n return next();\n }\n\n const result = checkBrowserOrigin({\n requestHost: c.req.header('host'),\n origin,\n allowedOrigins: resolveBrowserOrigins(),\n allowHostHeaderOriginFallback,\n autoAllowSameHostFromTrustedProxy: isRequestFromTrustedProxy(c),\n isLocalClient: false,\n });\n\n if (!result.ok) {\n log.warn(\n {\n origin,\n requestHost: c.req.header('host'),\n reason: 'reason' in result ? result.reason : 'unknown',\n path: c.req.path,\n method: c.req.method,\n },\n `Browser origin check failed: ${origin} not in allowed list`,\n );\n return c.json({ error: 'Forbidden', message: 'Origin not allowed' }, 403);\n }\n\n return next();\n }));\n\n const DEFAULT_API_BODY_MAX = 1 * 1024 * 1024;\n const SKILL_UPLOAD_BODY_MAX = 10 * 1024 * 1024;\n const NOTE_MEDIA_BODY_MAX = 25 * 1024 * 1024;\n const VOICE_TRANSCRIBE_BODY_MAX = 35 * 1024 * 1024;\n const WEBCHAT_AGENT_BODY_MAX = maxWebchatAgentRequestBodyBytes();\n\n const isNoteMediaUploadRequest = (path: string, method: string, contentType: string | undefined): boolean => {\n if (method !== 'POST') return false;\n if (/^\\/api\\/notes\\/[^/]+\\/media$/.test(path)) return true;\n return path === '/api/notes' && contentType?.includes('multipart/form-data') === true;\n };\n\n app.use('/api/skills/upload', bodyLimit({\n maxSize: SKILL_UPLOAD_BODY_MAX,\n onError: (c) => {\n log.warn({ path: c.req.path, maxSizeMb: 10 }, 'Request body too large: skills upload exceeds 10MB limit');\n return c.json({ error: 'Skill package too large', maxSize: '10MB' }, 413);\n },\n }));\n\n app.use('/api/*', async (c, next) => {\n const contentType = c.req.header('content-type');\n const maxSize = c.req.path === '/api/agent'\n ? WEBCHAT_AGENT_BODY_MAX\n : c.req.path === '/api/skills/upload'\n ? SKILL_UPLOAD_BODY_MAX\n : c.req.path === '/api/voice/transcribe'\n ? VOICE_TRANSCRIBE_BODY_MAX\n : isNoteMediaUploadRequest(c.req.path, c.req.method, contentType)\n ? NOTE_MEDIA_BODY_MAX\n : DEFAULT_API_BODY_MAX;\n const maxSizeMb = Math.ceil(maxSize / (1024 * 1024));\n return bodyLimit({\n maxSize,\n onError: (ctx) => {\n log.warn({ path: ctx.req.path, maxSizeMb }, `Request body too large: exceeds ${maxSizeMb}MB limit`);\n return ctx.json({ error: 'Request body too large', maxSize: `${maxSizeMb}MB` }, 413);\n },\n })(c, next);\n });\n\n registerPublicGatewayRoutes(app, service);\n\n // Extension UI assets are served without auth: sandboxed iframes (no allow-same-origin)\n // have an opaque origin of `null` and cannot forward the ?token= from the parent HTML URL.\n // Security is enforced by the strict CSP (frame-ancestors 'self') on every response.\n registerPublicExtensionAssetRoutes(app, service);\n\n const authenticated = new Hono();\n authenticated.use(routeErrorMiddleware());\n authenticated.use(\n auth({\n token,\n getGatewayAuth: () => service.currentConfig.gateway?.auth,\n getResolvedAuth: () => {\n if (typeof service.getResolvedAuth === 'function') {\n return service.getResolvedAuth();\n }\n return token ? { mode: 'token', token } : { mode: 'none' };\n },\n getTrustedProxyContext: () => ({\n trustedProxies: service.currentConfig.gateway?.trustedProxies,\n allowRealIpFallback: service.currentConfig.gateway?.allowRealIpFallback === true,\n }),\n }),\n );\n authenticated.use(operatorScopes());\n\n const strictRateLimitMiddleware = createStrictRateLimitMiddleware({\n getTrustedProxyContext: () => ({\n trustedProxies: service.currentConfig.gateway?.trustedProxies,\n allowRealIpFallback: service.currentConfig.gateway?.allowRealIpFallback === true,\n }),\n });\n const channelRateLimitMiddleware = createChannelRateLimitMiddleware({\n getTrustedProxyContext: () => ({\n trustedProxies: service.currentConfig.gateway?.trustedProxies,\n allowRealIpFallback: service.currentConfig.gateway?.allowRealIpFallback === true,\n }),\n });\n\n const sseConfig = {\n service,\n maxSseConnections: service.currentConfig.gateway.maxSseConnections,\n };\n\n registerAuthenticatedRoutes(app, authenticated, {\n service,\n strictRateLimitMiddleware,\n channelRateLimitMiddleware,\n sseConfig,\n });\n\n const prewarm = prewarmStaticUiCache();\n if (prewarm.loaded > 0) {\n log.debug({ loaded: prewarm.loaded, missing: prewarm.missing }, 'Static UI cache prewarmed');\n }\n\n app.route('/', authenticated);\n\n app.notFound((c) => {\n const isApiRoute = c.req.path.startsWith('/api/');\n const fields = { path: c.req.path, method: c.req.method };\n if (isApiRoute) {\n log.warn(fields, 'Route not found');\n } else {\n log.debug(fields, 'Route not found');\n }\n return c.json({ error: 'Not found' }, 404);\n });\n\n app.onError((err, c) => {\n log.error(\n {\n err,\n phase: 'gateway.http.unhandled',\n path: c.req.path,\n method: c.req.method,\n userAgent: c.req.header('user-agent'),\n },\n `Hono error on ${c.req.method} ${c.req.path}: ${err instanceof Error ? err.message : String(err)}`,\n );\n return c.json({ error: 'Internal server error' }, 500);\n });\n\n return app;\n}\n"],"mappings":"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"}
@@ -194,8 +194,9 @@ export declare function buildSafeWebConfigPayload(service: GatewayService, optio
194
194
  };
195
195
  channels: {
196
196
  [k: string]: {
197
- enabled: unknown;
197
+ enabled: boolean;
198
198
  configured: boolean;
199
+ setupStatus: import("../../../channels/catalog/channel-catalog-types.js").ChannelSetupStatus;
199
200
  config: Record<string, unknown>;
200
201
  schema: Record<string, unknown>;
201
202
  uiHints: Record<string, import("../../../extensions/index.js").ChannelConfigUiHint>;
@@ -266,6 +267,7 @@ export declare function buildSafeWebConfigPayload(service: GatewayService, optio
266
267
  goals: {
267
268
  maxTurns: number;
268
269
  checklistMode: boolean;
270
+ checklistDecomposePolicy: "empty_only" | "supplement_existing";
269
271
  maxConsecutiveParseFailures: number;
270
272
  judgeTimeoutMs: number;
271
273
  checklistHistoryChars: number;
@@ -2,7 +2,7 @@ import { init_agent_scope, listAgentEntries, normalizeAgentId, resolveDefaultAge
2
2
  import { resolveModelsJsonPath } from "../../../config/paths.js";
3
3
  import { init_models_json, loadModelsJson } from "../../../config/models-json.js";
4
4
  import { CredentialResolver, init_credentials } from "../../../auth/credentials.js";
5
- import { buildChannelCatalogForConfig, buildChannelCatalogFromSnapshot, init_channel_catalog_service, isChannelConfigured } from "../../../channels/catalog/channel-catalog-service.js";
5
+ import { buildChannelCatalogForConfig, buildChannelCatalogFromSnapshot, getChannelSetupStatus, init_channel_catalog_service } from "../../../channels/catalog/channel-catalog-service.js";
6
6
  import { getProviderRegistry, init_plugin_registry } from "../../../providers/plugin-registry.js";
7
7
  import { getAllProviders, init_providers, isProviderConfigured } from "../../../providers/index.js";
8
8
  import { resolveShareConfig } from "../../../share/share-config.js";
@@ -95,10 +95,13 @@ async function buildSafeWebConfigPayload(service, options = {}) {
95
95
  const catalog = snapshot ? buildChannelCatalogFromSnapshot(snapshot, { locale: options.locale }) : buildChannelCatalogForConfig(config, { locale: options.locale });
96
96
  const channelsPayload = Object.fromEntries(catalog.entries.map((entry) => {
97
97
  const plugin = typeof service.getChannelRuntimePlugin === "function" ? service.getChannelRuntimePlugin(entry.id) : void 0;
98
- const channelCfg = config.channels?.[entry.id];
98
+ const channelCfgKey = entry.configPath.startsWith("channels.") ? entry.configPath.slice(9).split(".")[0] || entry.id : entry.id;
99
+ const channelCfg = config.channels?.[channelCfgKey];
100
+ const setupStatus = getChannelSetupStatus(config, entry.id, entry);
99
101
  return [entry.id, {
100
- enabled: channelCfg?.enabled ?? false,
101
- configured: isChannelConfigured(config, entry.id),
102
+ enabled: setupStatus.enabled,
103
+ configured: setupStatus.ready,
104
+ setupStatus,
102
105
  config: plugin?.configSurface?.buildConfigSurface(config) ?? channelCfg ?? {},
103
106
  schema: entry.configSchema,
104
107
  uiHints: entry.uiHints
@@ -1 +1 @@
1
- {"version":3,"file":"config-payload.js","names":["getModelsJsonPath"],"sources":["../../../../../src/gateway/hono/lib/config-payload.ts"],"sourcesContent":["import {\n listAgentEntries,\n normalizeAgentId,\n resolveDefaultAgentId,\n} from '../../../agent/agent-scope.js';\nimport {\n buildChannelCatalogForConfig,\n buildChannelCatalogFromSnapshot,\n isChannelConfigured,\n} from '../../../channels/catalog/channel-catalog-service.js';\nimport { normalizeConfiguredMcpServers } from '../../../config/mcp-config-normalize.js';\nimport type { Config } from '../../../config/schema.js';\nimport {\n GENERIC_MASKED_SECRET,\n maskSecretLength,\n} from './mask-secret-length.js';\nimport { maskTunnelSecretForWeb } from '../../../tunnel/env.js';\nimport { resolveShareConfig } from '../../../share/share-config.js';\nimport {\n resolveCronConfigForWeb,\n resolveGoalsConfigForWeb,\n resolveSessionConfigForWeb,\n resolveUpdateConfigForWeb,\n} from '../../../config/web-patch.js';\nimport { CredentialResolver } from '../../../auth/credentials.js';\nimport { loadModelsJson, getModelsJsonPath } from '../../../config/models-json.js';\nimport { getAllProviders, isProviderConfigured } from '../../../providers/index.js';\nimport { getProviderRegistry } from '../../../providers/plugin-registry.js';\nimport type { GatewayService } from '../../service.js';\nimport { safeToolsWebForGet } from '../../config-tools-web.js';\nimport {\n agentImageGenerationModelAutoProviderFallback,\n agentImageGenerationModelTimeoutMs,\n agentModelFallbacksToArray,\n agentModelRefToString,\n} from './agent-model.js';\nimport { buildSafeProvidersConfigForWeb } from './safe-providers-config.js';\nimport { maskSttConfigForWeb, maskTtsConfigForWeb } from './safe-voice-config.js';\n\nfunction readModelsJsonProviderApiKey(providerId: string): string | undefined {\n const { config } = loadModelsJson(getModelsJsonPath());\n const entry = config.providers?.[providerId];\n const key = entry?.apiKey;\n return typeof key === 'string' && key.trim() ? key.trim() : undefined;\n}\n\n/** Length-preserving mask for LLM provider keys in GET `/api/config`. */\nasync function maskLlmProviderApiKeyForWeb(provider: string): Promise<string> {\n const resolver = new CredentialResolver();\n const stored = await resolver.revealGatewayStoredApiKey(provider);\n if (stored) return maskSecretLength(stored);\n\n const fromModelsJson = readModelsJsonProviderApiKey(provider);\n if (fromModelsJson) return maskSecretLength(fromModelsJson);\n\n // Extension plugins manage their own auth; don't show a fake gateway key mask.\n if (getProviderRegistry().has(provider)) return '';\n\n if (await isProviderConfigured(provider)) return GENERIC_MASKED_SECRET;\n return '';\n}\n\n/** MCP block for GET/PATCH `/api/config` (authenticated console editing). */\nexport function buildSafeMcpConfigForWeb(config: Config) {\n const mcp = config.mcp;\n if (!mcp) {\n return { servers: {} as Record<string, Record<string, unknown>> };\n }\n return {\n ...(mcp.sessionIdleTtlMs !== undefined ? { sessionIdleTtlMs: mcp.sessionIdleTtlMs } : {}),\n servers: normalizeConfiguredMcpServers(mcp.servers),\n };\n}\n\nfunction maskBrowserCloudConfigForWeb(cloud: unknown): Record<string, unknown> | null {\n if (!cloud || typeof cloud !== 'object' || Array.isArray(cloud)) {\n return null;\n }\n const raw = cloud as Record<string, unknown>;\n const safe: Record<string, unknown> = {};\n if (typeof raw.apiKey === 'string' && raw.apiKey.trim()) {\n safe.apiKey = maskSecretLength(raw.apiKey);\n }\n if (typeof raw.projectId === 'string' && raw.projectId.trim()) {\n safe.projectId = raw.projectId.trim();\n }\n if (typeof raw.region === 'string' && raw.region.trim()) {\n safe.region = raw.region.trim();\n }\n return Object.keys(safe).length > 0 ? safe : null;\n}\n\nexport function buildSafeBrowserConfigForWeb(browser: Config['agents']['defaults']['browser'] | undefined) {\n if (!browser || typeof browser !== 'object') {\n return {\n enabled: false,\n headless: false,\n allowPrivateUrls: false,\n commandTimeout: null,\n backend: null,\n cloudProvider: null,\n cloud: null,\n cdpUrl: null,\n extension: null,\n cloakbrowser: null,\n humanize: null,\n humanPreset: null,\n dialogPolicy: null,\n dialogTimeoutSeconds: null,\n };\n }\n\n return {\n enabled: browser.enabled !== false,\n headless: browser.headless === true,\n allowPrivateUrls: browser.allowPrivateUrls === true,\n commandTimeout:\n typeof browser.commandTimeout === 'number' && Number.isFinite(browser.commandTimeout)\n ? Math.floor(browser.commandTimeout)\n : null,\n backend:\n browser.backend === 'local' ||\n browser.backend === 'cdp' ||\n browser.backend === 'cloud' ||\n browser.backend === 'extension' ||\n browser.backend === 'cloakbrowser'\n ? browser.backend\n : null,\n cloudProvider:\n browser.cloudProvider === 'browserbase' || browser.cloudProvider === 'browser-use'\n ? browser.cloudProvider\n : null,\n cloud: maskBrowserCloudConfigForWeb(browser.cloud),\n cdpUrl: typeof browser.cdpUrl === 'string' && browser.cdpUrl.trim() ? browser.cdpUrl.trim() : null,\n extension: browser.extension && typeof browser.extension === 'object' && !Array.isArray(browser.extension)\n ? browser.extension\n : null,\n cloakbrowser:\n browser.cloakbrowser && typeof browser.cloakbrowser === 'object' && !Array.isArray(browser.cloakbrowser)\n ? browser.cloakbrowser\n : null,\n humanize: typeof browser.humanize === 'boolean' ? browser.humanize : null,\n humanPreset: browser.humanPreset === 'default' || browser.humanPreset === 'careful' ? browser.humanPreset : null,\n dialogPolicy:\n browser.dialogPolicy === 'must_respond' ||\n browser.dialogPolicy === 'auto_accept' ||\n browser.dialogPolicy === 'auto_dismiss'\n ? browser.dialogPolicy\n : null,\n dialogTimeoutSeconds:\n typeof browser.dialogTimeoutSeconds === 'number' && Number.isFinite(browser.dialogTimeoutSeconds)\n ? Math.floor(browser.dialogTimeoutSeconds)\n : null,\n };\n}\n\n/** Sanitized config snapshot for GET/PATCH `/api/config` (matches persisted `service.currentConfig`). */\nexport async function buildSafeWebConfigPayload(service: GatewayService, options: { locale?: string } = {}) {\n const config = service.currentConfig;\n const extensionLoader =\n typeof service.getExtensionLoader === 'function' ? service.getExtensionLoader() : undefined;\n const snapshot = extensionLoader?.getManifestSnapshot();\n const catalog = snapshot\n ? buildChannelCatalogFromSnapshot(snapshot, { locale: options.locale })\n : buildChannelCatalogForConfig(config, { locale: options.locale });\n const channelsPayload = Object.fromEntries(\n catalog.entries.map((entry) => {\n const plugin =\n typeof service.getChannelRuntimePlugin === 'function'\n ? service.getChannelRuntimePlugin(entry.id)\n : undefined;\n const channelCfg = config.channels?.[entry.id] as Record<string, unknown> | undefined;\n return [\n entry.id,\n {\n enabled: channelCfg?.enabled ?? false,\n configured: isChannelConfigured(config, entry.id),\n config: plugin?.configSurface?.buildConfigSurface(config) ?? channelCfg ?? {},\n schema: entry.configSchema,\n uiHints: entry.uiHints,\n },\n ];\n }),\n );\n return {\n agents: {\n defaultId: resolveDefaultAgentId(config),\n list: listAgentEntries(config)\n .filter((e) => e.enabled !== false)\n .map((e) => ({\n id: normalizeAgentId(e.id),\n })),\n defaults: {\n model: agentModelRefToString(config.agents?.defaults?.models?.chat) ?? '',\n modelFallbacks: agentModelFallbacksToArray(config.agents?.defaults?.models?.chat),\n imageModel: agentModelRefToString(config.agents?.defaults?.imageModel) ?? null,\n imageModelFallbacks: agentModelFallbacksToArray(config.agents?.defaults?.imageModel),\n imageGenerationModel: agentModelRefToString(config.agents?.defaults?.imageGenerationModel) ?? null,\n imageGenerationModelFallbacks: agentModelFallbacksToArray(\n config.agents?.defaults?.imageGenerationModel,\n ),\n imageGenerationModelTimeoutMs: agentImageGenerationModelTimeoutMs(\n config.agents?.defaults?.imageGenerationModel,\n ),\n imageGenerationModelAutoProviderFallback: agentImageGenerationModelAutoProviderFallback(\n config.agents?.defaults?.imageGenerationModel,\n ),\n mediaMaxMb: config.agents?.defaults?.mediaMaxMb,\n maxTokens: config.agents?.defaults?.maxTokens,\n temperature: config.agents?.defaults?.temperature,\n maxToolIterations: config.agents?.defaults?.maxToolIterations,\n workspace: config.agents?.defaults?.workspace,\n thinkingDefault: config.agents?.defaults?.thinkingDefault,\n reasoningDefault: config.agents?.defaults?.reasoningDefault,\n verboseDefault: config.agents?.defaults?.verboseDefault,\n browser: buildSafeBrowserConfigForWeb(config.agents?.defaults?.browser),\n maxTaskDurationMs: config.agents?.defaults?.maxTaskDurationMs,\n maxRequestsPerTurn: config.agents?.defaults?.maxRequestsPerTurn,\n maxToolFailuresPerTurn: config.agents?.defaults?.maxToolFailuresPerTurn,\n compaction: config.agents?.defaults?.compaction,\n pruning: config.agents?.defaults?.pruning,\n memory: config.agents?.defaults?.memory,\n sessionSearch: config.agents?.defaults?.sessionSearch,\n backgroundReview: config.agents?.defaults?.backgroundReview,\n webExtract: config.agents?.defaults?.webExtract,\n delegate: config.agents?.defaults?.delegate,\n executeCode: config.agents?.defaults?.executeCode,\n systemPromptOverride: config.agents?.defaults?.systemPromptOverride,\n skills: config.agents?.defaults?.skills,\n tools: config.agents?.defaults?.tools,\n models: config.agents?.defaults?.models,\n params: config.agents?.defaults?.params,\n },\n },\n channels: channelsPayload,\n providers: Object.fromEntries(\n await Promise.all(\n getAllProviders().map(async (provider) => [\n provider,\n await maskLlmProviderApiKeyForWeb(provider),\n ]),\n ),\n ),\n /** Masked `cfg.providers` for capability keys (image / STT / etc.). */\n providersConfig: buildSafeProvidersConfigForWeb(config.providers),\n gateway: {\n bind: config.gateway?.bind ?? 'loopback',\n customBindHost: config.gateway?.customBindHost,\n port: config.gateway?.port,\n corsOrigins: Array.isArray(config.gateway?.corsOrigins) ? config.gateway.corsOrigins : [],\n trustedProxies: Array.isArray(config.gateway?.trustedProxies)\n ? config.gateway.trustedProxies\n : [],\n allowRealIpFallback: config.gateway?.allowRealIpFallback === true,\n dangerouslyAllowHostHeaderOriginFallback:\n config.gateway?.dangerouslyAllowHostHeaderOriginFallback === true,\n security: {\n strict: config.gateway?.security?.strict === true,\n },\n auth: {\n mode: config.gateway?.auth?.mode || 'token',\n token: config.gateway?.auth?.token ? maskSecretLength(config.gateway.auth.token) : '',\n password: config.gateway?.auth?.password\n ? maskSecretLength(config.gateway.auth.password)\n : '',\n trustedProxy: config.gateway?.auth?.trustedProxy\n ? {\n userHeader: config.gateway.auth.trustedProxy.userHeader,\n requiredHeaders: config.gateway.auth.trustedProxy.requiredHeaders ?? [],\n allowUsers: config.gateway.auth.trustedProxy.allowUsers ?? [],\n allowLoopback: config.gateway.auth.trustedProxy.allowLoopback === true,\n }\n : undefined,\n rateLimit: {\n enabled: config.gateway?.auth?.rateLimit?.enabled !== false,\n maxAttempts:\n typeof config.gateway?.auth?.rateLimit?.maxAttempts === 'number'\n ? config.gateway.auth.rateLimit.maxAttempts\n : 5,\n windowMs:\n typeof config.gateway?.auth?.rateLimit?.windowMs === 'number'\n ? config.gateway.auth.rateLimit.windowMs\n : 900_000,\n blockDurationMs:\n typeof config.gateway?.auth?.rateLimit?.blockDurationMs === 'number'\n ? config.gateway.auth.rateLimit.blockDurationMs\n : typeof config.gateway?.auth?.rateLimit?.lockoutMs === 'number'\n ? config.gateway.auth.rateLimit.lockoutMs\n : 300_000,\n exemptLoopback: config.gateway?.auth?.rateLimit?.exemptLoopback !== false,\n },\n },\n heartbeat: {\n enabled: config.gateway?.heartbeat?.enabled,\n intervalMs: config.gateway?.heartbeat?.intervalMs,\n includeSystemPromptSection: config.gateway?.heartbeat?.includeSystemPromptSection === true,\n target: config.gateway?.heartbeat?.target,\n targetChatId: config.gateway?.heartbeat?.targetChatId,\n prompt: config.gateway?.heartbeat?.prompt,\n ackMaxChars: config.gateway?.heartbeat?.ackMaxChars,\n isolatedSession: config.gateway?.heartbeat?.isolatedSession,\n activeHours: config.gateway?.heartbeat?.activeHours,\n },\n maxSseConnections:\n typeof config.gateway?.maxSseConnections === 'number'\n ? config.gateway.maxSseConnections\n : 100,\n channelConnectDeferMode: config.gateway?.channelConnectDeferMode ?? 'auto',\n channelConnectDeferIds: Array.isArray(config.gateway?.channelConnectDeferIds)\n ? config.gateway.channelConnectDeferIds\n : [],\n channelConnectDeferSkipIds: Array.isArray(config.gateway?.channelConnectDeferSkipIds)\n ? config.gateway.channelConnectDeferSkipIds\n : [],\n share: resolveShareConfig(config.gateway?.share),\n publicUrl: config.gateway?.publicUrl ?? null,\n skillsMarketplaceProvider: config.gateway?.skillsMarketplaceProvider ?? 'skillhub',\n skillsStoreBaseUrl: config.gateway?.skillsStoreBaseUrl ?? 'https://store.xopc.ai',\n },\n cron: resolveCronConfigForWeb(config),\n goals: resolveGoalsConfigForWeb(config),\n session: resolveSessionConfigForWeb(config),\n tunnel: {\n enabled: config.tunnel?.enabled === true,\n autoStart: config.tunnel?.autoStart === true,\n brokerUrl: config.tunnel?.brokerUrl ?? 'https://frp.xopc.ai/api',\n registrationSecret: config.tunnel?.registrationSecret\n ? maskTunnelSecretForWeb(config.tunnel.registrationSecret)\n : '',\n consent: config.tunnel?.consent\n ? {\n version: config.tunnel.consent.version,\n acceptedAt: config.tunnel.consent.acceptedAt,\n }\n : undefined,\n transport: { tls: 'broker_terminated' as const },\n },\n update: {\n ...resolveUpdateConfigForWeb(config),\n },\n stt: maskSttConfigForWeb(config.tools?.media?.audio),\n tts: maskTtsConfigForWeb(config.messages?.tts),\n tools: safeToolsWebForGet(config),\n bindings: Array.isArray(config.bindings) ? config.bindings : [],\n mcp: buildSafeMcpConfigForWeb(config),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"config-payload.js","names":["getModelsJsonPath"],"sources":["../../../../../src/gateway/hono/lib/config-payload.ts"],"sourcesContent":["import {\n listAgentEntries,\n normalizeAgentId,\n resolveDefaultAgentId,\n} from '../../../agent/agent-scope.js';\nimport {\n buildChannelCatalogForConfig,\n buildChannelCatalogFromSnapshot,\n getChannelSetupStatus,\n} from '../../../channels/catalog/channel-catalog-service.js';\nimport { normalizeConfiguredMcpServers } from '../../../config/mcp-config-normalize.js';\nimport type { Config } from '../../../config/schema.js';\nimport {\n GENERIC_MASKED_SECRET,\n maskSecretLength,\n} from './mask-secret-length.js';\nimport { maskTunnelSecretForWeb } from '../../../tunnel/env.js';\nimport { resolveShareConfig } from '../../../share/share-config.js';\nimport {\n resolveCronConfigForWeb,\n resolveGoalsConfigForWeb,\n resolveSessionConfigForWeb,\n resolveUpdateConfigForWeb,\n} from '../../../config/web-patch.js';\nimport { CredentialResolver } from '../../../auth/credentials.js';\nimport { loadModelsJson, getModelsJsonPath } from '../../../config/models-json.js';\nimport { getAllProviders, isProviderConfigured } from '../../../providers/index.js';\nimport { getProviderRegistry } from '../../../providers/plugin-registry.js';\nimport type { GatewayService } from '../../service.js';\nimport { safeToolsWebForGet } from '../../config-tools-web.js';\nimport {\n agentImageGenerationModelAutoProviderFallback,\n agentImageGenerationModelTimeoutMs,\n agentModelFallbacksToArray,\n agentModelRefToString,\n} from './agent-model.js';\nimport { buildSafeProvidersConfigForWeb } from './safe-providers-config.js';\nimport { maskSttConfigForWeb, maskTtsConfigForWeb } from './safe-voice-config.js';\n\nfunction readModelsJsonProviderApiKey(providerId: string): string | undefined {\n const { config } = loadModelsJson(getModelsJsonPath());\n const entry = config.providers?.[providerId];\n const key = entry?.apiKey;\n return typeof key === 'string' && key.trim() ? key.trim() : undefined;\n}\n\n/** Length-preserving mask for LLM provider keys in GET `/api/config`. */\nasync function maskLlmProviderApiKeyForWeb(provider: string): Promise<string> {\n const resolver = new CredentialResolver();\n const stored = await resolver.revealGatewayStoredApiKey(provider);\n if (stored) return maskSecretLength(stored);\n\n const fromModelsJson = readModelsJsonProviderApiKey(provider);\n if (fromModelsJson) return maskSecretLength(fromModelsJson);\n\n // Extension plugins manage their own auth; don't show a fake gateway key mask.\n if (getProviderRegistry().has(provider)) return '';\n\n if (await isProviderConfigured(provider)) return GENERIC_MASKED_SECRET;\n return '';\n}\n\n/** MCP block for GET/PATCH `/api/config` (authenticated console editing). */\nexport function buildSafeMcpConfigForWeb(config: Config) {\n const mcp = config.mcp;\n if (!mcp) {\n return { servers: {} as Record<string, Record<string, unknown>> };\n }\n return {\n ...(mcp.sessionIdleTtlMs !== undefined ? { sessionIdleTtlMs: mcp.sessionIdleTtlMs } : {}),\n servers: normalizeConfiguredMcpServers(mcp.servers),\n };\n}\n\nfunction maskBrowserCloudConfigForWeb(cloud: unknown): Record<string, unknown> | null {\n if (!cloud || typeof cloud !== 'object' || Array.isArray(cloud)) {\n return null;\n }\n const raw = cloud as Record<string, unknown>;\n const safe: Record<string, unknown> = {};\n if (typeof raw.apiKey === 'string' && raw.apiKey.trim()) {\n safe.apiKey = maskSecretLength(raw.apiKey);\n }\n if (typeof raw.projectId === 'string' && raw.projectId.trim()) {\n safe.projectId = raw.projectId.trim();\n }\n if (typeof raw.region === 'string' && raw.region.trim()) {\n safe.region = raw.region.trim();\n }\n return Object.keys(safe).length > 0 ? safe : null;\n}\n\nexport function buildSafeBrowserConfigForWeb(browser: Config['agents']['defaults']['browser'] | undefined) {\n if (!browser || typeof browser !== 'object') {\n return {\n enabled: false,\n headless: false,\n allowPrivateUrls: false,\n commandTimeout: null,\n backend: null,\n cloudProvider: null,\n cloud: null,\n cdpUrl: null,\n extension: null,\n cloakbrowser: null,\n humanize: null,\n humanPreset: null,\n dialogPolicy: null,\n dialogTimeoutSeconds: null,\n };\n }\n\n return {\n enabled: browser.enabled !== false,\n headless: browser.headless === true,\n allowPrivateUrls: browser.allowPrivateUrls === true,\n commandTimeout:\n typeof browser.commandTimeout === 'number' && Number.isFinite(browser.commandTimeout)\n ? Math.floor(browser.commandTimeout)\n : null,\n backend:\n browser.backend === 'local' ||\n browser.backend === 'cdp' ||\n browser.backend === 'cloud' ||\n browser.backend === 'extension' ||\n browser.backend === 'cloakbrowser'\n ? browser.backend\n : null,\n cloudProvider:\n browser.cloudProvider === 'browserbase' || browser.cloudProvider === 'browser-use'\n ? browser.cloudProvider\n : null,\n cloud: maskBrowserCloudConfigForWeb(browser.cloud),\n cdpUrl: typeof browser.cdpUrl === 'string' && browser.cdpUrl.trim() ? browser.cdpUrl.trim() : null,\n extension: browser.extension && typeof browser.extension === 'object' && !Array.isArray(browser.extension)\n ? browser.extension\n : null,\n cloakbrowser:\n browser.cloakbrowser && typeof browser.cloakbrowser === 'object' && !Array.isArray(browser.cloakbrowser)\n ? browser.cloakbrowser\n : null,\n humanize: typeof browser.humanize === 'boolean' ? browser.humanize : null,\n humanPreset: browser.humanPreset === 'default' || browser.humanPreset === 'careful' ? browser.humanPreset : null,\n dialogPolicy:\n browser.dialogPolicy === 'must_respond' ||\n browser.dialogPolicy === 'auto_accept' ||\n browser.dialogPolicy === 'auto_dismiss'\n ? browser.dialogPolicy\n : null,\n dialogTimeoutSeconds:\n typeof browser.dialogTimeoutSeconds === 'number' && Number.isFinite(browser.dialogTimeoutSeconds)\n ? Math.floor(browser.dialogTimeoutSeconds)\n : null,\n };\n}\n\n/** Sanitized config snapshot for GET/PATCH `/api/config` (matches persisted `service.currentConfig`). */\nexport async function buildSafeWebConfigPayload(service: GatewayService, options: { locale?: string } = {}) {\n const config = service.currentConfig;\n const extensionLoader =\n typeof service.getExtensionLoader === 'function' ? service.getExtensionLoader() : undefined;\n const snapshot = extensionLoader?.getManifestSnapshot();\n const catalog = snapshot\n ? buildChannelCatalogFromSnapshot(snapshot, { locale: options.locale })\n : buildChannelCatalogForConfig(config, { locale: options.locale });\n const channelsPayload = Object.fromEntries(\n catalog.entries.map((entry) => {\n const plugin =\n typeof service.getChannelRuntimePlugin === 'function'\n ? service.getChannelRuntimePlugin(entry.id)\n : undefined;\n const channelCfgKey = entry.configPath.startsWith('channels.')\n ? entry.configPath.slice('channels.'.length).split('.')[0] || entry.id\n : entry.id;\n const channelCfg = config.channels?.[channelCfgKey] as Record<string, unknown> | undefined;\n const setupStatus = getChannelSetupStatus(config, entry.id, entry);\n return [\n entry.id,\n {\n enabled: setupStatus.enabled,\n configured: setupStatus.ready,\n setupStatus,\n config: plugin?.configSurface?.buildConfigSurface(config) ?? channelCfg ?? {},\n schema: entry.configSchema,\n uiHints: entry.uiHints,\n },\n ];\n }),\n );\n return {\n agents: {\n defaultId: resolveDefaultAgentId(config),\n list: listAgentEntries(config)\n .filter((e) => e.enabled !== false)\n .map((e) => ({\n id: normalizeAgentId(e.id),\n })),\n defaults: {\n model: agentModelRefToString(config.agents?.defaults?.models?.chat) ?? '',\n modelFallbacks: agentModelFallbacksToArray(config.agents?.defaults?.models?.chat),\n imageModel: agentModelRefToString(config.agents?.defaults?.imageModel) ?? null,\n imageModelFallbacks: agentModelFallbacksToArray(config.agents?.defaults?.imageModel),\n imageGenerationModel: agentModelRefToString(config.agents?.defaults?.imageGenerationModel) ?? null,\n imageGenerationModelFallbacks: agentModelFallbacksToArray(\n config.agents?.defaults?.imageGenerationModel,\n ),\n imageGenerationModelTimeoutMs: agentImageGenerationModelTimeoutMs(\n config.agents?.defaults?.imageGenerationModel,\n ),\n imageGenerationModelAutoProviderFallback: agentImageGenerationModelAutoProviderFallback(\n config.agents?.defaults?.imageGenerationModel,\n ),\n mediaMaxMb: config.agents?.defaults?.mediaMaxMb,\n maxTokens: config.agents?.defaults?.maxTokens,\n temperature: config.agents?.defaults?.temperature,\n maxToolIterations: config.agents?.defaults?.maxToolIterations,\n workspace: config.agents?.defaults?.workspace,\n thinkingDefault: config.agents?.defaults?.thinkingDefault,\n reasoningDefault: config.agents?.defaults?.reasoningDefault,\n verboseDefault: config.agents?.defaults?.verboseDefault,\n browser: buildSafeBrowserConfigForWeb(config.agents?.defaults?.browser),\n maxTaskDurationMs: config.agents?.defaults?.maxTaskDurationMs,\n maxRequestsPerTurn: config.agents?.defaults?.maxRequestsPerTurn,\n maxToolFailuresPerTurn: config.agents?.defaults?.maxToolFailuresPerTurn,\n compaction: config.agents?.defaults?.compaction,\n pruning: config.agents?.defaults?.pruning,\n memory: config.agents?.defaults?.memory,\n sessionSearch: config.agents?.defaults?.sessionSearch,\n backgroundReview: config.agents?.defaults?.backgroundReview,\n webExtract: config.agents?.defaults?.webExtract,\n delegate: config.agents?.defaults?.delegate,\n executeCode: config.agents?.defaults?.executeCode,\n systemPromptOverride: config.agents?.defaults?.systemPromptOverride,\n skills: config.agents?.defaults?.skills,\n tools: config.agents?.defaults?.tools,\n models: config.agents?.defaults?.models,\n params: config.agents?.defaults?.params,\n },\n },\n channels: channelsPayload,\n providers: Object.fromEntries(\n await Promise.all(\n getAllProviders().map(async (provider) => [\n provider,\n await maskLlmProviderApiKeyForWeb(provider),\n ]),\n ),\n ),\n /** Masked `cfg.providers` for capability keys (image / STT / etc.). */\n providersConfig: buildSafeProvidersConfigForWeb(config.providers),\n gateway: {\n bind: config.gateway?.bind ?? 'loopback',\n customBindHost: config.gateway?.customBindHost,\n port: config.gateway?.port,\n corsOrigins: Array.isArray(config.gateway?.corsOrigins) ? config.gateway.corsOrigins : [],\n trustedProxies: Array.isArray(config.gateway?.trustedProxies)\n ? config.gateway.trustedProxies\n : [],\n allowRealIpFallback: config.gateway?.allowRealIpFallback === true,\n dangerouslyAllowHostHeaderOriginFallback:\n config.gateway?.dangerouslyAllowHostHeaderOriginFallback === true,\n security: {\n strict: config.gateway?.security?.strict === true,\n },\n auth: {\n mode: config.gateway?.auth?.mode || 'token',\n token: config.gateway?.auth?.token ? maskSecretLength(config.gateway.auth.token) : '',\n password: config.gateway?.auth?.password\n ? maskSecretLength(config.gateway.auth.password)\n : '',\n trustedProxy: config.gateway?.auth?.trustedProxy\n ? {\n userHeader: config.gateway.auth.trustedProxy.userHeader,\n requiredHeaders: config.gateway.auth.trustedProxy.requiredHeaders ?? [],\n allowUsers: config.gateway.auth.trustedProxy.allowUsers ?? [],\n allowLoopback: config.gateway.auth.trustedProxy.allowLoopback === true,\n }\n : undefined,\n rateLimit: {\n enabled: config.gateway?.auth?.rateLimit?.enabled !== false,\n maxAttempts:\n typeof config.gateway?.auth?.rateLimit?.maxAttempts === 'number'\n ? config.gateway.auth.rateLimit.maxAttempts\n : 5,\n windowMs:\n typeof config.gateway?.auth?.rateLimit?.windowMs === 'number'\n ? config.gateway.auth.rateLimit.windowMs\n : 900_000,\n blockDurationMs:\n typeof config.gateway?.auth?.rateLimit?.blockDurationMs === 'number'\n ? config.gateway.auth.rateLimit.blockDurationMs\n : typeof config.gateway?.auth?.rateLimit?.lockoutMs === 'number'\n ? config.gateway.auth.rateLimit.lockoutMs\n : 300_000,\n exemptLoopback: config.gateway?.auth?.rateLimit?.exemptLoopback !== false,\n },\n },\n heartbeat: {\n enabled: config.gateway?.heartbeat?.enabled,\n intervalMs: config.gateway?.heartbeat?.intervalMs,\n includeSystemPromptSection: config.gateway?.heartbeat?.includeSystemPromptSection === true,\n target: config.gateway?.heartbeat?.target,\n targetChatId: config.gateway?.heartbeat?.targetChatId,\n prompt: config.gateway?.heartbeat?.prompt,\n ackMaxChars: config.gateway?.heartbeat?.ackMaxChars,\n isolatedSession: config.gateway?.heartbeat?.isolatedSession,\n activeHours: config.gateway?.heartbeat?.activeHours,\n },\n maxSseConnections:\n typeof config.gateway?.maxSseConnections === 'number'\n ? config.gateway.maxSseConnections\n : 100,\n channelConnectDeferMode: config.gateway?.channelConnectDeferMode ?? 'auto',\n channelConnectDeferIds: Array.isArray(config.gateway?.channelConnectDeferIds)\n ? config.gateway.channelConnectDeferIds\n : [],\n channelConnectDeferSkipIds: Array.isArray(config.gateway?.channelConnectDeferSkipIds)\n ? config.gateway.channelConnectDeferSkipIds\n : [],\n share: resolveShareConfig(config.gateway?.share),\n publicUrl: config.gateway?.publicUrl ?? null,\n skillsMarketplaceProvider: config.gateway?.skillsMarketplaceProvider ?? 'skillhub',\n skillsStoreBaseUrl: config.gateway?.skillsStoreBaseUrl ?? 'https://store.xopc.ai',\n },\n cron: resolveCronConfigForWeb(config),\n goals: resolveGoalsConfigForWeb(config),\n session: resolveSessionConfigForWeb(config),\n tunnel: {\n enabled: config.tunnel?.enabled === true,\n autoStart: config.tunnel?.autoStart === true,\n brokerUrl: config.tunnel?.brokerUrl ?? 'https://frp.xopc.ai/api',\n registrationSecret: config.tunnel?.registrationSecret\n ? maskTunnelSecretForWeb(config.tunnel.registrationSecret)\n : '',\n consent: config.tunnel?.consent\n ? {\n version: config.tunnel.consent.version,\n acceptedAt: config.tunnel.consent.acceptedAt,\n }\n : undefined,\n transport: { tls: 'broker_terminated' as const },\n },\n update: {\n ...resolveUpdateConfigForWeb(config),\n },\n stt: maskSttConfigForWeb(config.tools?.media?.audio),\n tts: maskTtsConfigForWeb(config.messages?.tts),\n tools: safeToolsWebForGet(config),\n bindings: Array.isArray(config.bindings) ? config.bindings : [],\n mcp: buildSafeMcpConfigForWeb(config),\n };\n}\n"],"mappings":"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"}
@@ -12,3 +12,7 @@ export declare function createStrictRateLimitMiddleware(deps: StrictRateLimitDep
12
12
  error: string;
13
13
  code: string;
14
14
  }, 429, "json">>;
15
+ export declare function createChannelRateLimitMiddleware(deps: StrictRateLimitDeps): import("hono/dist/types/types.js").MiddlewareHandler<any, string, {}, Response & import("hono/dist/types/types.js").TypedResponse<{
16
+ error: string;
17
+ code: string;
18
+ }, 429, "json">>;
@@ -31,9 +31,9 @@ function resolveClientIp(c, deps) {
31
31
  }
32
32
  return getClientIpFromHeaders({ get: (name) => c.req.header(name) ?? void 0 });
33
33
  }
34
- function createStrictRateLimitMiddleware(deps) {
34
+ function createClientRateLimitMiddleware(deps, options) {
35
35
  return createMiddleware(async (c, next) => {
36
- const limiter = buckets.strictApi();
36
+ const limiter = options.limiter();
37
37
  const clientIp = resolveClientIp(c, deps);
38
38
  const result = limiter.consume(clientIp);
39
39
  if (!result.allowed) {
@@ -43,8 +43,8 @@ function createStrictRateLimitMiddleware(deps) {
43
43
  path: c.req.path,
44
44
  method: c.req.method,
45
45
  retryAfterSec,
46
- reason: "strict_rate_limit_exceeded"
47
- }, "Strict API rate limit exceeded");
46
+ reason: options.reason
47
+ }, options.exceededMessage);
48
48
  c.header("Retry-After", String(retryAfterSec));
49
49
  c.header("X-RateLimit-Remaining", "0");
50
50
  return c.json({
@@ -56,7 +56,21 @@ function createStrictRateLimitMiddleware(deps) {
56
56
  await next();
57
57
  });
58
58
  }
59
+ function createStrictRateLimitMiddleware(deps) {
60
+ return createClientRateLimitMiddleware(deps, {
61
+ limiter: () => buckets.strictApi(),
62
+ exceededMessage: "Strict API rate limit exceeded",
63
+ reason: "strict_rate_limit_exceeded"
64
+ });
65
+ }
66
+ function createChannelRateLimitMiddleware(deps) {
67
+ return createClientRateLimitMiddleware(deps, {
68
+ limiter: () => buckets.channelApi(),
69
+ exceededMessage: "Channel API rate limit exceeded",
70
+ reason: "channel_rate_limit_exceeded"
71
+ });
72
+ }
59
73
  //#endregion
60
- export { createStrictRateLimitMiddleware };
74
+ export { createChannelRateLimitMiddleware, createStrictRateLimitMiddleware };
61
75
 
62
76
  //# sourceMappingURL=strict-rate-limit.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"strict-rate-limit.js","names":[],"sources":["../../../../../src/gateway/hono/middleware/strict-rate-limit.ts"],"sourcesContent":["/**\n * Per-client request-rate gate for sensitive admin/mutation endpoints.\n * Backed by `buckets.strictApi()` — see {@link ../../rate-limit/buckets.ts}.\n */\n\nimport { createMiddleware } from 'hono/factory';\nimport type { Context } from 'hono';\nimport { getConnInfo } from '@hono/node-server/conninfo';\n\nimport { buckets } from '../../rate-limit/index.js';\nimport { getClientIpFromHeaders } from '../../security/loopback.js';\nimport { resolveClientIpFromRequest } from '../../client-ip.js';\nimport { createLogger } from '../../../utils/logger.js';\n\nconst log = createLogger('Hono:StrictRateLimit');\n\nexport type StrictRateLimitDeps = {\n getTrustedProxyContext: () => {\n trustedProxies?: string[];\n allowRealIpFallback?: boolean;\n };\n};\n\nfunction resolveClientIp(c: Context, deps: StrictRateLimitDeps): string {\n const { trustedProxies, allowRealIpFallback } = deps.getTrustedProxyContext();\n if (trustedProxies?.length) {\n let remoteAddress: string | undefined;\n try {\n remoteAddress = getConnInfo(c).remote.address;\n } catch {\n remoteAddress = undefined;\n }\n return resolveClientIpFromRequest({\n remoteAddress,\n getHeader: (name) => c.req.header(name),\n trustedProxies,\n allowRealIpFallback,\n });\n }\n return getClientIpFromHeaders({ get: (name) => c.req.header(name) ?? undefined });\n}\n\nexport function createStrictRateLimitMiddleware(deps: StrictRateLimitDeps) {\n return createMiddleware(async (c, next) => {\n const limiter = buckets.strictApi();\n const clientIp = resolveClientIp(c, deps);\n const result = limiter.consume(clientIp);\n\n if (!result.allowed) {\n const retryAfterSec = Math.ceil(result.retryAfterMs / 1000);\n log.warn(\n {\n clientIp,\n path: c.req.path,\n method: c.req.method,\n retryAfterSec,\n reason: 'strict_rate_limit_exceeded',\n },\n 'Strict API rate limit exceeded',\n );\n c.header('Retry-After', String(retryAfterSec));\n c.header('X-RateLimit-Remaining', '0');\n return c.json({ error: 'Too many requests', code: 'rate_limited' }, 429);\n }\n\n c.header('X-RateLimit-Remaining', String(result.remaining));\n await next();\n });\n}\n"],"mappings":";;;;;;;;;;;;;aAYwD;AAExD,MAAM,MAAM,aAAa,uBAAuB;AAShD,SAAS,gBAAgB,GAAY,MAAmC;CACtE,MAAM,EAAE,gBAAgB,wBAAwB,KAAK,wBAAwB;AAC7E,KAAI,gBAAgB,QAAQ;EAC1B,IAAI;AACJ,MAAI;AACF,mBAAgB,YAAY,EAAE,CAAC,OAAO;UAChC;AACN,mBAAgB,KAAA;;AAElB,SAAO,2BAA2B;GAChC;GACA,YAAY,SAAS,EAAE,IAAI,OAAO,KAAK;GACvC;GACA;GACD,CAAC;;AAEJ,QAAO,uBAAuB,EAAE,MAAM,SAAS,EAAE,IAAI,OAAO,KAAK,IAAI,KAAA,GAAW,CAAC;;AAGnF,SAAgB,gCAAgC,MAA2B;AACzE,QAAO,iBAAiB,OAAO,GAAG,SAAS;EACzC,MAAM,UAAU,QAAQ,WAAW;EACnC,MAAM,WAAW,gBAAgB,GAAG,KAAK;EACzC,MAAM,SAAS,QAAQ,QAAQ,SAAS;AAExC,MAAI,CAAC,OAAO,SAAS;GACnB,MAAM,gBAAgB,KAAK,KAAK,OAAO,eAAe,IAAK;AAC3D,OAAI,KACF;IACE;IACA,MAAM,EAAE,IAAI;IACZ,QAAQ,EAAE,IAAI;IACd;IACA,QAAQ;IACT,EACD,iCACD;AACD,KAAE,OAAO,eAAe,OAAO,cAAc,CAAC;AAC9C,KAAE,OAAO,yBAAyB,IAAI;AACtC,UAAO,EAAE,KAAK;IAAE,OAAO;IAAqB,MAAM;IAAgB,EAAE,IAAI;;AAG1E,IAAE,OAAO,yBAAyB,OAAO,OAAO,UAAU,CAAC;AAC3D,QAAM,MAAM;GACZ"}
1
+ {"version":3,"file":"strict-rate-limit.js","names":[],"sources":["../../../../../src/gateway/hono/middleware/strict-rate-limit.ts"],"sourcesContent":["/**\n * Per-client request-rate gate for sensitive admin/mutation endpoints.\n * Backed by `buckets.strictApi()` — see {@link ../../rate-limit/buckets.ts}.\n */\n\nimport { createMiddleware } from 'hono/factory';\nimport type { Context } from 'hono';\nimport { getConnInfo } from '@hono/node-server/conninfo';\n\nimport { buckets } from '../../rate-limit/index.js';\nimport { getClientIpFromHeaders } from '../../security/loopback.js';\nimport { resolveClientIpFromRequest } from '../../client-ip.js';\nimport { createLogger } from '../../../utils/logger.js';\n\nconst log = createLogger('Hono:StrictRateLimit');\n\nexport type StrictRateLimitDeps = {\n getTrustedProxyContext: () => {\n trustedProxies?: string[];\n allowRealIpFallback?: boolean;\n };\n};\n\nfunction resolveClientIp(c: Context, deps: StrictRateLimitDeps): string {\n const { trustedProxies, allowRealIpFallback } = deps.getTrustedProxyContext();\n if (trustedProxies?.length) {\n let remoteAddress: string | undefined;\n try {\n remoteAddress = getConnInfo(c).remote.address;\n } catch {\n remoteAddress = undefined;\n }\n return resolveClientIpFromRequest({\n remoteAddress,\n getHeader: (name) => c.req.header(name),\n trustedProxies,\n allowRealIpFallback,\n });\n }\n return getClientIpFromHeaders({ get: (name) => c.req.header(name) ?? undefined });\n}\n\nfunction createClientRateLimitMiddleware(\n deps: StrictRateLimitDeps,\n options: {\n limiter: () => ReturnType<typeof buckets.strictApi>;\n exceededMessage: string;\n reason: string;\n },\n) {\n return createMiddleware(async (c, next) => {\n const limiter = options.limiter();\n const clientIp = resolveClientIp(c, deps);\n const result = limiter.consume(clientIp);\n\n if (!result.allowed) {\n const retryAfterSec = Math.ceil(result.retryAfterMs / 1000);\n log.warn(\n {\n clientIp,\n path: c.req.path,\n method: c.req.method,\n retryAfterSec,\n reason: options.reason,\n },\n options.exceededMessage,\n );\n c.header('Retry-After', String(retryAfterSec));\n c.header('X-RateLimit-Remaining', '0');\n return c.json({ error: 'Too many requests', code: 'rate_limited' }, 429);\n }\n\n c.header('X-RateLimit-Remaining', String(result.remaining));\n await next();\n });\n}\n\nexport function createStrictRateLimitMiddleware(deps: StrictRateLimitDeps) {\n return createClientRateLimitMiddleware(deps, {\n limiter: () => buckets.strictApi(),\n exceededMessage: 'Strict API rate limit exceeded',\n reason: 'strict_rate_limit_exceeded',\n });\n}\n\nexport function createChannelRateLimitMiddleware(deps: StrictRateLimitDeps) {\n return createClientRateLimitMiddleware(deps, {\n limiter: () => buckets.channelApi(),\n exceededMessage: 'Channel API rate limit exceeded',\n reason: 'channel_rate_limit_exceeded',\n });\n}\n"],"mappings":";;;;;;;;;;;;;aAYwD;AAExD,MAAM,MAAM,aAAa,uBAAuB;AAShD,SAAS,gBAAgB,GAAY,MAAmC;CACtE,MAAM,EAAE,gBAAgB,wBAAwB,KAAK,wBAAwB;AAC7E,KAAI,gBAAgB,QAAQ;EAC1B,IAAI;AACJ,MAAI;AACF,mBAAgB,YAAY,EAAE,CAAC,OAAO;UAChC;AACN,mBAAgB,KAAA;;AAElB,SAAO,2BAA2B;GAChC;GACA,YAAY,SAAS,EAAE,IAAI,OAAO,KAAK;GACvC;GACA;GACD,CAAC;;AAEJ,QAAO,uBAAuB,EAAE,MAAM,SAAS,EAAE,IAAI,OAAO,KAAK,IAAI,KAAA,GAAW,CAAC;;AAGnF,SAAS,gCACP,MACA,SAKA;AACA,QAAO,iBAAiB,OAAO,GAAG,SAAS;EACzC,MAAM,UAAU,QAAQ,SAAS;EACjC,MAAM,WAAW,gBAAgB,GAAG,KAAK;EACzC,MAAM,SAAS,QAAQ,QAAQ,SAAS;AAExC,MAAI,CAAC,OAAO,SAAS;GACnB,MAAM,gBAAgB,KAAK,KAAK,OAAO,eAAe,IAAK;AAC3D,OAAI,KACF;IACE;IACA,MAAM,EAAE,IAAI;IACZ,QAAQ,EAAE,IAAI;IACd;IACA,QAAQ,QAAQ;IACjB,EACD,QAAQ,gBACT;AACD,KAAE,OAAO,eAAe,OAAO,cAAc,CAAC;AAC9C,KAAE,OAAO,yBAAyB,IAAI;AACtC,UAAO,EAAE,KAAK;IAAE,OAAO;IAAqB,MAAM;IAAgB,EAAE,IAAI;;AAG1E,IAAE,OAAO,yBAAyB,OAAO,OAAO,UAAU,CAAC;AAC3D,QAAM,MAAM;GACZ;;AAGJ,SAAgB,gCAAgC,MAA2B;AACzE,QAAO,gCAAgC,MAAM;EAC3C,eAAe,QAAQ,WAAW;EAClC,iBAAiB;EACjB,QAAQ;EACT,CAAC;;AAGJ,SAAgB,iCAAiC,MAA2B;AAC1E,QAAO,gCAAgC,MAAM;EAC3C,eAAe,QAAQ,YAAY;EACnC,iBAAiB;EACjB,QAAQ;EACT,CAAC"}
@@ -1,4 +1,4 @@
1
- import { buildChannelCatalogForConfig, buildChannelCatalogFromSnapshot, init_channel_catalog_service, isChannelConfigured } from "../../../channels/catalog/channel-catalog-service.js";
1
+ import { buildChannelCatalogForConfig, buildChannelCatalogFromSnapshot, getChannelSetupStatus, init_channel_catalog_service } from "../../../channels/catalog/channel-catalog-service.js";
2
2
  import { approveChannelPairing, approveChannelPairingBySender, dismissChannelPairingPending, listChannelPairingState, listChannelPairingSummary, revokeChannelPairingPaired } from "../../../channels/pairing/pairing-service.js";
3
3
  //#region src/gateway/hono/routes/channels.ts
4
4
  init_channel_catalog_service();
@@ -16,20 +16,24 @@ function localeFromRequest(c) {
16
16
  }
17
17
  function registerChannelRoutes(authenticated, deps) {
18
18
  const { service, strictRateLimitMiddleware } = deps;
19
+ const channelRateLimitMiddleware = deps.channelRateLimitMiddleware ?? strictRateLimitMiddleware;
19
20
  authenticated.get("/api/channels/catalog", (c) => {
20
21
  const snapshot = service.getExtensionLoader()?.getManifestSnapshot();
21
22
  const locale = localeFromRequest(c);
22
23
  const catalog = snapshot ? buildChannelCatalogFromSnapshot(snapshot, { locale }) : buildChannelCatalogForConfig(service.currentConfig, { locale });
23
- const channelsCfg = service.currentConfig.channels;
24
24
  const running = new Set(service.getRunningChannelIds());
25
25
  return c.json({
26
26
  ok: true,
27
- payload: { channels: catalog.entries.map((entry) => ({
28
- ...entry,
29
- enabled: channelsCfg?.[entry.id]?.enabled === true,
30
- configured: isChannelConfigured(service.currentConfig, entry.id),
31
- runtime: running.has(entry.id) ? "loaded" : "missing"
32
- })) }
27
+ payload: { channels: catalog.entries.map((entry) => {
28
+ const setupStatus = getChannelSetupStatus(service.currentConfig, entry.id, entry);
29
+ return {
30
+ ...entry,
31
+ enabled: setupStatus.enabled,
32
+ configured: setupStatus.ready,
33
+ setupStatus,
34
+ runtime: running.has(entry.id) ? "loaded" : "missing"
35
+ };
36
+ }) }
33
37
  });
34
38
  });
35
39
  authenticated.get("/api/channels/status", (c) => {
@@ -61,7 +65,7 @@ function registerChannelRoutes(authenticated, deps) {
61
65
  payload: { config: value }
62
66
  });
63
67
  });
64
- authenticated.post("/api/channels/:channelId/actions/:actionId", strictRateLimitMiddleware, async (c) => {
68
+ authenticated.post("/api/channels/:channelId/actions/:actionId", channelRateLimitMiddleware, async (c) => {
65
69
  const channel = channelIdParam(c.req.param("channelId"));
66
70
  const action = c.req.param("actionId")?.trim() ?? "";
67
71
  if (!channel || !action) return c.json({
@@ -162,7 +166,7 @@ function registerChannelRoutes(authenticated, deps) {
162
166
  payload: { summary }
163
167
  });
164
168
  });
165
- authenticated.post("/api/channels/:channelId/pairing/approve", strictRateLimitMiddleware, async (c) => {
169
+ authenticated.post("/api/channels/:channelId/pairing/approve", channelRateLimitMiddleware, async (c) => {
166
170
  const channel = channelIdParam(c.req.param("channelId"));
167
171
  const body = await c.req.json().catch(() => ({}));
168
172
  const result = approveChannelPairing({
@@ -185,7 +189,7 @@ function registerChannelRoutes(authenticated, deps) {
185
189
  }
186
190
  });
187
191
  });
188
- authenticated.post("/api/channels/:channelId/pairing/approve-sender", strictRateLimitMiddleware, async (c) => {
192
+ authenticated.post("/api/channels/:channelId/pairing/approve-sender", channelRateLimitMiddleware, async (c) => {
189
193
  const channel = channelIdParam(c.req.param("channelId"));
190
194
  const body = await c.req.json().catch(() => ({}));
191
195
  const result = approveChannelPairingBySender({
@@ -208,7 +212,7 @@ function registerChannelRoutes(authenticated, deps) {
208
212
  }
209
213
  });
210
214
  });
211
- authenticated.delete("/api/channels/:channelId/pairing/paired", strictRateLimitMiddleware, async (c) => {
215
+ authenticated.delete("/api/channels/:channelId/pairing/paired", channelRateLimitMiddleware, async (c) => {
212
216
  const channel = channelIdParam(c.req.param("channelId"));
213
217
  const body = await c.req.json().catch(() => ({}));
214
218
  const result = revokeChannelPairingPaired({
@@ -228,7 +232,7 @@ function registerChannelRoutes(authenticated, deps) {
228
232
  payload: { changed: result.changed }
229
233
  });
230
234
  });
231
- authenticated.delete("/api/channels/:channelId/pairing/pending", strictRateLimitMiddleware, async (c) => {
235
+ authenticated.delete("/api/channels/:channelId/pairing/pending", channelRateLimitMiddleware, async (c) => {
232
236
  const channel = channelIdParam(c.req.param("channelId"));
233
237
  const body = await c.req.json().catch(() => ({}));
234
238
  const result = dismissChannelPairingPending({
@@ -261,7 +265,7 @@ function registerChannelRoutes(authenticated, deps) {
261
265
  payload: { checks }
262
266
  });
263
267
  });
264
- authenticated.post("/api/channels/:channelId/restart", strictRateLimitMiddleware, async (c) => {
268
+ authenticated.post("/api/channels/:channelId/restart", channelRateLimitMiddleware, async (c) => {
265
269
  const channel = channelIdParam(c.req.param("channelId"));
266
270
  await service.restartChannel(channel);
267
271
  return c.json({
@@ -1 +1 @@
1
- {"version":3,"file":"channels.js","names":[],"sources":["../../../../../src/gateway/hono/routes/channels.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport {\n approveChannelPairing,\n approveChannelPairingBySender,\n dismissChannelPairingPending,\n listChannelPairingState,\n listChannelPairingSummary,\n revokeChannelPairingPaired,\n} from '../../../channels/pairing/pairing-service.js';\nimport {\n buildChannelCatalogForConfig,\n buildChannelCatalogFromSnapshot,\n isChannelConfigured,\n} from '../../../channels/catalog/channel-catalog-service.js';\nimport type { Config } from '../../../config/index.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\nimport type { ChannelRuntimeActionPayload } from '../../../channels/plugins/types.adapters.js';\n\nfunction channelIdParam(raw: string | undefined): string {\n return (raw ?? '').trim().toLowerCase();\n}\n\nfunction accountIdFromQuery(raw: string | undefined): string {\n return raw?.trim() || 'default';\n}\n\nfunction actionPayloadChangedConfig(payload: ChannelRuntimeActionPayload | undefined): boolean {\n return Boolean(payload && 'configChanged' in payload && payload.configChanged === true);\n}\n\nfunction localeFromRequest(c: { req: { query(name: string): string | undefined; header(name: string): string | undefined } }): string | undefined {\n return c.req.query('locale') ?? c.req.header('X-XOPC-Locale') ?? c.req.header('Accept-Language')?.split(',')[0];\n}\n\nexport function registerChannelRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service, strictRateLimitMiddleware } = deps;\n\n authenticated.get('/api/channels/catalog', (c) => {\n const snapshot = service.getExtensionLoader()?.getManifestSnapshot();\n const locale = localeFromRequest(c);\n const catalog = snapshot\n ? buildChannelCatalogFromSnapshot(snapshot, { locale })\n : buildChannelCatalogForConfig(service.currentConfig, { locale });\n const channelsCfg = service.currentConfig.channels as Record<string, { enabled?: boolean } | undefined> | undefined;\n const running = new Set(service.getRunningChannelIds());\n return c.json({\n ok: true,\n payload: {\n channels: catalog.entries.map((entry) => ({\n ...entry,\n enabled: channelsCfg?.[entry.id]?.enabled === true,\n configured: isChannelConfigured(service.currentConfig, entry.id),\n runtime: running.has(entry.id) ? 'loaded' : 'missing',\n })),\n },\n });\n });\n\n authenticated.get('/api/channels/status', (c) => {\n const channels = service.getChannelsStatus();\n return c.json({ ok: true, payload: { channels } });\n });\n\n authenticated.get('/api/channels/meta', (c) => {\n const channels = service.getChannelsHubMeta();\n return c.json({ ok: true, payload: { channels } });\n });\n\n authenticated.get('/api/channels/:channelId/config', (c) => {\n const channel = channelIdParam(c.req.param('channelId'));\n if (!channel) {\n return c.json({ ok: false, error: { code: 'BAD_REQUEST', message: 'Missing channel id' } }, 400);\n }\n const plugin = service.getChannelRuntimePlugin(channel);\n const value =\n plugin?.configSurface?.buildConfigSurface(service.currentConfig) ??\n (service.currentConfig.channels as Record<string, unknown> | undefined)?.[channel] ??\n {};\n return c.json({ ok: true, payload: { config: value } });\n });\n\n authenticated.post('/api/channels/:channelId/actions/:actionId', strictRateLimitMiddleware, async (c) => {\n const channel = channelIdParam(c.req.param('channelId'));\n const action = c.req.param('actionId')?.trim() ?? '';\n if (!channel || !action) {\n return c.json({ ok: false, error: { code: 'BAD_REQUEST', message: 'Missing channel/action id' } }, 400);\n }\n const plugin = await service.ensureChannelRuntimePlugin(channel);\n if (!plugin) {\n return c.json({ ok: false, error: { code: 'CHANNEL_RUNTIME_MISSING', message: 'Channel runtime is not loaded' } }, 404);\n }\n const body = await c.req.json().catch(() => ({}));\n const accountId =\n body && typeof body === 'object' && typeof (body as { accountId?: unknown }).accountId === 'string'\n ? (body as { accountId: string }).accountId.trim()\n : undefined;\n const input = body && typeof body === 'object' ? (body as { input?: unknown }).input : undefined;\n\n if (action === 'doctor.run' && plugin.doctor) {\n const checks = await plugin.doctor.check({ cfg: service.currentConfig, locale: localeFromRequest(c) });\n return c.json({ ok: true, payload: { type: 'diagnostics', checks } });\n }\n\n if (plugin.runtimeActions) {\n const result = await plugin.runtimeActions.runAction({\n cfg: service.currentConfig,\n locale: localeFromRequest(c),\n actionId: action,\n accountId,\n input,\n });\n if (!result.ok) {\n return c.json({ ok: false, error: { code: 'CHANNEL_ACTION_FAILED', message: result.message ?? 'Channel action failed' } }, 400);\n }\n if (result.nextConfig) {\n const saved = await service.saveConfig(result.nextConfig);\n if (!saved.saved) {\n return c.json({ ok: false, error: { code: 'CHANNEL_ACTION_FAILED', message: saved.error ?? 'Failed to save channel config' } }, 500);\n }\n } else if (actionPayloadChangedConfig(result.payload)) {\n await service.reloadConfig();\n }\n return c.json({ ok: true, payload: result.payload ?? {} });\n }\n\n return c.json(\n {\n ok: false,\n error: {\n code: 'ACTION_NOT_IMPLEMENTED',\n message: `Channel action \"${channel}.${action}\" is not implemented by the runtime adapter.`,\n },\n },\n 501,\n );\n });\n\n authenticated.get('/api/channels/:channelId/pairing', (c) => {\n const channel = channelIdParam(c.req.param('channelId'));\n if (!channel) {\n return c.json({ ok: false, error: { code: 'BAD_REQUEST', message: 'Missing channel id' } }, 400);\n }\n const accountId = accountIdFromQuery(c.req.query('account'));\n const config = service.currentConfig as Config | undefined;\n const state = listChannelPairingState({ channel, accountId, config });\n return c.json({ ok: true, payload: state });\n });\n\n authenticated.get('/api/channels/pairing/summary', (c) => {\n const config = service.currentConfig as Config | undefined;\n const summary = listChannelPairingSummary(config);\n return c.json({ ok: true, payload: { summary } });\n });\n\n authenticated.post('/api/channels/:channelId/pairing/approve', strictRateLimitMiddleware, async (c) => {\n const channel = channelIdParam(c.req.param('channelId'));\n const body = await c.req.json().catch(() => ({}));\n const accountId =\n body && typeof body === 'object' && typeof (body as { accountId?: unknown }).accountId === 'string'\n ? (body as { accountId: string }).accountId.trim()\n : 'default';\n const code =\n body && typeof body === 'object' && typeof (body as { code?: unknown }).code === 'string'\n ? (body as { code: string }).code.trim()\n : '';\n const result = approveChannelPairing({ channel, accountId, code });\n if (result.ok === false) {\n return c.json({ ok: false, error: { code: 'PAIRING_INVALID', message: result.error } }, 400);\n }\n return c.json({ ok: true, payload: { senderId: result.senderId, alreadyPaired: result.alreadyPaired } });\n });\n\n authenticated.post('/api/channels/:channelId/pairing/approve-sender', strictRateLimitMiddleware, async (c) => {\n const channel = channelIdParam(c.req.param('channelId'));\n const body = await c.req.json().catch(() => ({}));\n const accountId =\n body && typeof body === 'object' && typeof (body as { accountId?: unknown }).accountId === 'string'\n ? (body as { accountId: string }).accountId.trim()\n : 'default';\n const senderId =\n body && typeof body === 'object' && typeof (body as { senderId?: unknown }).senderId === 'string'\n ? (body as { senderId: string }).senderId.trim()\n : '';\n const result = approveChannelPairingBySender({ channel, accountId, senderId });\n if (result.ok === false) {\n return c.json({ ok: false, error: { code: 'PAIRING_INVALID', message: result.error } }, 400);\n }\n return c.json({ ok: true, payload: { senderId: result.senderId, alreadyPaired: result.alreadyPaired } });\n });\n\n authenticated.delete('/api/channels/:channelId/pairing/paired', strictRateLimitMiddleware, async (c) => {\n const channel = channelIdParam(c.req.param('channelId'));\n const body = await c.req.json().catch(() => ({}));\n const accountId =\n body && typeof body === 'object' && typeof (body as { accountId?: unknown }).accountId === 'string'\n ? (body as { accountId: string }).accountId.trim()\n : 'default';\n const senderId =\n body && typeof body === 'object' && typeof (body as { senderId?: unknown }).senderId === 'string'\n ? (body as { senderId: string }).senderId.trim()\n : '';\n const result = revokeChannelPairingPaired({ channel, accountId, senderId });\n if (result.ok === false) {\n return c.json({ ok: false, error: { code: 'BAD_REQUEST', message: result.error } }, 400);\n }\n return c.json({ ok: true, payload: { changed: result.changed } });\n });\n\n authenticated.delete('/api/channels/:channelId/pairing/pending', strictRateLimitMiddleware, async (c) => {\n const channel = channelIdParam(c.req.param('channelId'));\n const body = await c.req.json().catch(() => ({}));\n const accountId =\n body && typeof body === 'object' && typeof (body as { accountId?: unknown }).accountId === 'string'\n ? (body as { accountId: string }).accountId.trim()\n : 'default';\n const senderId =\n body && typeof body === 'object' && typeof (body as { senderId?: unknown }).senderId === 'string'\n ? (body as { senderId: string }).senderId.trim()\n : '';\n const result = dismissChannelPairingPending({ channel, accountId, senderId });\n if (result.ok === false) {\n return c.json({ ok: false, error: { code: 'PAIRING_INVALID', message: result.error } }, 400);\n }\n return c.json({ ok: true, payload: { senderId: result.senderId } });\n });\n\n authenticated.get('/api/channels/:channelId/doctor', async (c) => {\n const channel = channelIdParam(c.req.param('channelId'));\n const plugin = service.getChannelRuntimePlugin(channel);\n if (!plugin?.doctor) {\n return c.json({ ok: true, payload: { checks: [] } });\n }\n const checks = await plugin.doctor.check({ cfg: service.currentConfig });\n return c.json({ ok: true, payload: { checks } });\n });\n\n authenticated.post('/api/channels/:channelId/restart', strictRateLimitMiddleware, async (c) => {\n const channel = channelIdParam(c.req.param('channelId'));\n await service.restartChannel(channel);\n return c.json({ ok: true, payload: { restarted: true } });\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"channels.js","names":[],"sources":["../../../../../src/gateway/hono/routes/channels.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport {\n approveChannelPairing,\n approveChannelPairingBySender,\n dismissChannelPairingPending,\n listChannelPairingState,\n listChannelPairingSummary,\n revokeChannelPairingPaired,\n} from '../../../channels/pairing/pairing-service.js';\nimport {\n buildChannelCatalogForConfig,\n buildChannelCatalogFromSnapshot,\n getChannelSetupStatus,\n} from '../../../channels/catalog/channel-catalog-service.js';\nimport type { Config } from '../../../config/index.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\nimport type { ChannelRuntimeActionPayload } from '../../../channels/plugins/types.adapters.js';\n\nfunction channelIdParam(raw: string | undefined): string {\n return (raw ?? '').trim().toLowerCase();\n}\n\nfunction accountIdFromQuery(raw: string | undefined): string {\n return raw?.trim() || 'default';\n}\n\nfunction actionPayloadChangedConfig(payload: ChannelRuntimeActionPayload | undefined): boolean {\n return Boolean(payload && 'configChanged' in payload && payload.configChanged === true);\n}\n\nfunction localeFromRequest(c: { req: { query(name: string): string | undefined; header(name: string): string | undefined } }): string | undefined {\n return c.req.query('locale') ?? c.req.header('X-XOPC-Locale') ?? c.req.header('Accept-Language')?.split(',')[0];\n}\n\nexport function registerChannelRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service, strictRateLimitMiddleware } = deps;\n const channelRateLimitMiddleware = deps.channelRateLimitMiddleware ?? strictRateLimitMiddleware;\n\n authenticated.get('/api/channels/catalog', (c) => {\n const snapshot = service.getExtensionLoader()?.getManifestSnapshot();\n const locale = localeFromRequest(c);\n const catalog = snapshot\n ? buildChannelCatalogFromSnapshot(snapshot, { locale })\n : buildChannelCatalogForConfig(service.currentConfig, { locale });\n const running = new Set(service.getRunningChannelIds());\n return c.json({\n ok: true,\n payload: {\n channels: catalog.entries.map((entry) => {\n const setupStatus = getChannelSetupStatus(service.currentConfig, entry.id, entry);\n return {\n ...entry,\n enabled: setupStatus.enabled,\n configured: setupStatus.ready,\n setupStatus,\n runtime: running.has(entry.id) ? 'loaded' : 'missing',\n };\n }),\n },\n });\n });\n\n authenticated.get('/api/channels/status', (c) => {\n const channels = service.getChannelsStatus();\n return c.json({ ok: true, payload: { channels } });\n });\n\n authenticated.get('/api/channels/meta', (c) => {\n const channels = service.getChannelsHubMeta();\n return c.json({ ok: true, payload: { channels } });\n });\n\n authenticated.get('/api/channels/:channelId/config', (c) => {\n const channel = channelIdParam(c.req.param('channelId'));\n if (!channel) {\n return c.json({ ok: false, error: { code: 'BAD_REQUEST', message: 'Missing channel id' } }, 400);\n }\n const plugin = service.getChannelRuntimePlugin(channel);\n const value =\n plugin?.configSurface?.buildConfigSurface(service.currentConfig) ??\n (service.currentConfig.channels as Record<string, unknown> | undefined)?.[channel] ??\n {};\n return c.json({ ok: true, payload: { config: value } });\n });\n\n authenticated.post('/api/channels/:channelId/actions/:actionId', channelRateLimitMiddleware, async (c) => {\n const channel = channelIdParam(c.req.param('channelId'));\n const action = c.req.param('actionId')?.trim() ?? '';\n if (!channel || !action) {\n return c.json({ ok: false, error: { code: 'BAD_REQUEST', message: 'Missing channel/action id' } }, 400);\n }\n const plugin = await service.ensureChannelRuntimePlugin(channel);\n if (!plugin) {\n return c.json({ ok: false, error: { code: 'CHANNEL_RUNTIME_MISSING', message: 'Channel runtime is not loaded' } }, 404);\n }\n const body = await c.req.json().catch(() => ({}));\n const accountId =\n body && typeof body === 'object' && typeof (body as { accountId?: unknown }).accountId === 'string'\n ? (body as { accountId: string }).accountId.trim()\n : undefined;\n const input = body && typeof body === 'object' ? (body as { input?: unknown }).input : undefined;\n\n if (action === 'doctor.run' && plugin.doctor) {\n const checks = await plugin.doctor.check({ cfg: service.currentConfig, locale: localeFromRequest(c) });\n return c.json({ ok: true, payload: { type: 'diagnostics', checks } });\n }\n\n if (plugin.runtimeActions) {\n const result = await plugin.runtimeActions.runAction({\n cfg: service.currentConfig,\n locale: localeFromRequest(c),\n actionId: action,\n accountId,\n input,\n });\n if (!result.ok) {\n return c.json({ ok: false, error: { code: 'CHANNEL_ACTION_FAILED', message: result.message ?? 'Channel action failed' } }, 400);\n }\n if (result.nextConfig) {\n const saved = await service.saveConfig(result.nextConfig);\n if (!saved.saved) {\n return c.json({ ok: false, error: { code: 'CHANNEL_ACTION_FAILED', message: saved.error ?? 'Failed to save channel config' } }, 500);\n }\n } else if (actionPayloadChangedConfig(result.payload)) {\n await service.reloadConfig();\n }\n return c.json({ ok: true, payload: result.payload ?? {} });\n }\n\n return c.json(\n {\n ok: false,\n error: {\n code: 'ACTION_NOT_IMPLEMENTED',\n message: `Channel action \"${channel}.${action}\" is not implemented by the runtime adapter.`,\n },\n },\n 501,\n );\n });\n\n authenticated.get('/api/channels/:channelId/pairing', (c) => {\n const channel = channelIdParam(c.req.param('channelId'));\n if (!channel) {\n return c.json({ ok: false, error: { code: 'BAD_REQUEST', message: 'Missing channel id' } }, 400);\n }\n const accountId = accountIdFromQuery(c.req.query('account'));\n const config = service.currentConfig as Config | undefined;\n const state = listChannelPairingState({ channel, accountId, config });\n return c.json({ ok: true, payload: state });\n });\n\n authenticated.get('/api/channels/pairing/summary', (c) => {\n const config = service.currentConfig as Config | undefined;\n const summary = listChannelPairingSummary(config);\n return c.json({ ok: true, payload: { summary } });\n });\n\n authenticated.post('/api/channels/:channelId/pairing/approve', channelRateLimitMiddleware, async (c) => {\n const channel = channelIdParam(c.req.param('channelId'));\n const body = await c.req.json().catch(() => ({}));\n const accountId =\n body && typeof body === 'object' && typeof (body as { accountId?: unknown }).accountId === 'string'\n ? (body as { accountId: string }).accountId.trim()\n : 'default';\n const code =\n body && typeof body === 'object' && typeof (body as { code?: unknown }).code === 'string'\n ? (body as { code: string }).code.trim()\n : '';\n const result = approveChannelPairing({ channel, accountId, code });\n if (result.ok === false) {\n return c.json({ ok: false, error: { code: 'PAIRING_INVALID', message: result.error } }, 400);\n }\n return c.json({ ok: true, payload: { senderId: result.senderId, alreadyPaired: result.alreadyPaired } });\n });\n\n authenticated.post('/api/channels/:channelId/pairing/approve-sender', channelRateLimitMiddleware, async (c) => {\n const channel = channelIdParam(c.req.param('channelId'));\n const body = await c.req.json().catch(() => ({}));\n const accountId =\n body && typeof body === 'object' && typeof (body as { accountId?: unknown }).accountId === 'string'\n ? (body as { accountId: string }).accountId.trim()\n : 'default';\n const senderId =\n body && typeof body === 'object' && typeof (body as { senderId?: unknown }).senderId === 'string'\n ? (body as { senderId: string }).senderId.trim()\n : '';\n const result = approveChannelPairingBySender({ channel, accountId, senderId });\n if (result.ok === false) {\n return c.json({ ok: false, error: { code: 'PAIRING_INVALID', message: result.error } }, 400);\n }\n return c.json({ ok: true, payload: { senderId: result.senderId, alreadyPaired: result.alreadyPaired } });\n });\n\n authenticated.delete('/api/channels/:channelId/pairing/paired', channelRateLimitMiddleware, async (c) => {\n const channel = channelIdParam(c.req.param('channelId'));\n const body = await c.req.json().catch(() => ({}));\n const accountId =\n body && typeof body === 'object' && typeof (body as { accountId?: unknown }).accountId === 'string'\n ? (body as { accountId: string }).accountId.trim()\n : 'default';\n const senderId =\n body && typeof body === 'object' && typeof (body as { senderId?: unknown }).senderId === 'string'\n ? (body as { senderId: string }).senderId.trim()\n : '';\n const result = revokeChannelPairingPaired({ channel, accountId, senderId });\n if (result.ok === false) {\n return c.json({ ok: false, error: { code: 'BAD_REQUEST', message: result.error } }, 400);\n }\n return c.json({ ok: true, payload: { changed: result.changed } });\n });\n\n authenticated.delete('/api/channels/:channelId/pairing/pending', channelRateLimitMiddleware, async (c) => {\n const channel = channelIdParam(c.req.param('channelId'));\n const body = await c.req.json().catch(() => ({}));\n const accountId =\n body && typeof body === 'object' && typeof (body as { accountId?: unknown }).accountId === 'string'\n ? (body as { accountId: string }).accountId.trim()\n : 'default';\n const senderId =\n body && typeof body === 'object' && typeof (body as { senderId?: unknown }).senderId === 'string'\n ? (body as { senderId: string }).senderId.trim()\n : '';\n const result = dismissChannelPairingPending({ channel, accountId, senderId });\n if (result.ok === false) {\n return c.json({ ok: false, error: { code: 'PAIRING_INVALID', message: result.error } }, 400);\n }\n return c.json({ ok: true, payload: { senderId: result.senderId } });\n });\n\n authenticated.get('/api/channels/:channelId/doctor', async (c) => {\n const channel = channelIdParam(c.req.param('channelId'));\n const plugin = service.getChannelRuntimePlugin(channel);\n if (!plugin?.doctor) {\n return c.json({ ok: true, payload: { checks: [] } });\n }\n const checks = await plugin.doctor.check({ cfg: service.currentConfig });\n return c.json({ ok: true, payload: { checks } });\n });\n\n authenticated.post('/api/channels/:channelId/restart', channelRateLimitMiddleware, async (c) => {\n const channel = channelIdParam(c.req.param('channelId'));\n await service.restartChannel(channel);\n return c.json({ ok: true, payload: { restarted: true } });\n });\n}\n"],"mappings":"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"}