@xopcai/xopc 0.0.93 → 0.0.94

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 (356) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
  3. package/dist/extensions/feishu/src/workflow-progress.js +1 -1
  4. package/dist/extensions/telegram/src/plugin.js +1 -1
  5. package/dist/extensions/telegram/src/routing-integration.js +2 -2
  6. package/dist/extensions/telegram/src/workflow-progress.js +1 -1
  7. package/dist/extensions/telegram/xopc.extension.json +1 -1
  8. package/dist/extensions/weixin/src/api/api.js +2 -2
  9. package/dist/extensions/weixin/src/auth/accounts.js +1 -1
  10. package/dist/extensions/weixin/src/cdn/upload.js +1 -1
  11. package/dist/extensions/weixin/src/media/data-url.js +1 -1
  12. package/dist/extensions/weixin/src/messaging/debug-mode.js +1 -1
  13. package/dist/extensions/weixin/src/messaging/inbound.js +1 -1
  14. package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
  15. package/dist/extensions/weixin/src/plugin.js +1 -1
  16. package/dist/extensions/weixin/src/storage/sync-buf.js +1 -1
  17. package/dist/extensions/weixin/src/workflow-progress.js +1 -1
  18. package/dist/gateway/static/root/assets/agents-OqhbJkMf.js +222 -0
  19. package/dist/gateway/static/root/assets/apps-page-OHXW9XP8.js +1 -0
  20. package/dist/gateway/static/root/assets/channels-settings-4N2R-jof.js +1 -0
  21. package/dist/gateway/static/root/assets/{channels-status-swr-CsGkK9h9.js → channels-status-swr-Bv6f9kDq.js} +1 -1
  22. package/dist/gateway/static/root/assets/{cron-api-CyAm0xJT.js → cron-api-BtaQaHJq.js} +1 -1
  23. package/dist/gateway/static/root/assets/cron-page-Dah32HJK.js +1 -0
  24. package/dist/gateway/static/root/assets/{dist-DHwVV8XK.js → dist-BJfD9Qvs.js} +1 -1
  25. package/dist/gateway/static/root/assets/{extension-debug-page-BK8kcc4F.js → extension-debug-page-DnYuMzmH.js} +1 -1
  26. package/dist/gateway/static/root/assets/{extension-page-Cf8X_QUc.js → extension-page-CJfc-6XV.js} +1 -1
  27. package/dist/gateway/static/root/assets/{extension-settings-page-C5-YLMmy.js → extension-settings-page-BxdfYQMG.js} +1 -1
  28. package/dist/gateway/static/root/assets/{fetch-BAPnkYbC.js → fetch-B0aeeY0q.js} +1 -1
  29. package/dist/gateway/static/root/assets/{field-primitives-8p7ucXa1.js → field-primitives-DOLHwowi.js} +1 -1
  30. package/dist/gateway/static/root/assets/{heartbeat-config-api-CpgW2sGp.js → heartbeat-config-api-Bj2INAf5.js} +1 -1
  31. package/dist/gateway/static/root/assets/index-Bj_l8QDp.css +1 -0
  32. package/dist/gateway/static/root/assets/{index-Do52EfZK.js → index-DuQ1XPoA.js} +99 -98
  33. package/dist/gateway/static/root/assets/logs-page-AsOgLNJE.js +2 -0
  34. package/dist/gateway/static/root/assets/{note-detail-page-WLM6FUIi.js → note-detail-page-24J4mVP-.js} +3 -3
  35. package/dist/gateway/static/root/assets/{note-time-EFyIVhec.js → note-time-JBszYV3s.js} +1 -1
  36. package/dist/gateway/static/root/assets/{notes-page-BYPVYcYn.js → notes-page-BApAirFB.js} +1 -1
  37. package/dist/gateway/static/root/assets/sessions-page-DX9huWsA.js +1 -0
  38. package/dist/gateway/static/root/assets/{settings-advanced-gate-CEs8pGh6.js → settings-advanced-gate-DWvhsTuz.js} +1 -1
  39. package/dist/gateway/static/root/assets/{settings-form-section-C6cGTVwK.js → settings-form-section-CxMjaMiy.js} +1 -1
  40. package/dist/gateway/static/root/assets/settings-page-4VmUTzQs.js +3 -0
  41. package/dist/gateway/static/root/assets/{share-preview-page-tnIfJ4K6.js → share-preview-page-IX0TJvRd.js} +1 -1
  42. package/dist/gateway/static/root/assets/skills-page-CGKGKfwe.js +2 -0
  43. package/dist/gateway/static/root/assets/{theme-store-D6EsNTPr.js → theme-store-Cg_SuBw0.js} +1 -1
  44. package/dist/gateway/static/root/assets/{url-CTjpm0Uz.js → url-BHHmdJYc.js} +2 -2
  45. package/dist/gateway/static/root/assets/{utils-C86AVfY-.js → utils-BmlcxR2j.js} +1 -1
  46. package/dist/gateway/static/root/assets/voice-api-key-field-DaGm2N4J.js +1 -0
  47. package/dist/gateway/static/root/assets/{workflow-page.utils-DsEriMFW.js → workflow-page.utils-D0vsIGHD.js} +1 -1
  48. package/dist/gateway/static/root/assets/workflows-page-BFCrD3nw.js +27 -0
  49. package/dist/gateway/static/root/index.html +5 -5
  50. package/dist/package.js +1 -1
  51. package/dist/src/agent/agent-manager.js +7 -7
  52. package/dist/src/agent/agent-scope.js +1 -1
  53. package/dist/src/agent/bootstrap/load-bootstrap-files.js +1 -1
  54. package/dist/src/agent/context/workspace-seed.js +2 -2
  55. package/dist/src/agent/goals/goal-run-store.js +4 -4
  56. package/dist/src/agent/goals/persistent-goal-service.js +1 -1
  57. package/dist/src/agent/goals/post-turn.js +2 -2
  58. package/dist/src/agent/image/load-image-media.js +2 -2
  59. package/dist/src/agent/inbound/turn-dispatcher.d.ts +1 -0
  60. package/dist/src/agent/inbound/turn-dispatcher.js +3 -0
  61. package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
  62. package/dist/src/agent/ipc/bus.js +1 -1
  63. package/dist/src/agent/ipc/inbox.js +2 -2
  64. package/dist/src/agent/ipc/socket.js +1 -1
  65. package/dist/src/agent/lifecycle/handlers/compaction.js +1 -1
  66. package/dist/src/agent/lifecycle/handlers/compaction.js.map +1 -1
  67. package/dist/src/agent/mcp/bundle-mcp-materialize.js +2 -2
  68. package/dist/src/agent/mcp/bundle-mcp-materialize.js.map +1 -1
  69. package/dist/src/agent/mcp/bundle-mcp-runtime.js +18 -5
  70. package/dist/src/agent/mcp/bundle-mcp-runtime.js.map +1 -1
  71. package/dist/src/agent/mcp/mcp-transport-config.js +11 -4
  72. package/dist/src/agent/mcp/mcp-transport-config.js.map +1 -1
  73. package/dist/src/agent/mcp/mcp-transport.js +2 -2
  74. package/dist/src/agent/mcp/mcp-transport.js.map +1 -1
  75. package/dist/src/agent/memory/builtin-memory-store.js +1 -1
  76. package/dist/src/agent/memory/dreaming/deep-promotion.js +1 -1
  77. package/dist/src/agent/memory/dreaming/events.js +1 -1
  78. package/dist/src/agent/memory/dreaming/last-run.js +1 -1
  79. package/dist/src/agent/memory/dreaming/light-sweep.js +1 -1
  80. package/dist/src/agent/memory/dreaming/preview.js +1 -1
  81. package/dist/src/agent/memory/dreaming/rem-patterns.js +1 -1
  82. package/dist/src/agent/memory/dreaming/short-term-store.js +1 -1
  83. package/dist/src/agent/memory/dreaming/utils.js +1 -1
  84. package/dist/src/agent/memory/plugin-discovery.js +1 -1
  85. package/dist/src/agent/models/manager.js +1 -1
  86. package/dist/src/agent/prompt/service-prompt-builder.js +2 -2
  87. package/dist/src/agent/reply/post-compaction-context.js +1 -1
  88. package/dist/src/agent/reply/workspace-boundary-read.js +1 -1
  89. package/dist/src/agent/sandbox/path-policy.js +2 -2
  90. package/dist/src/agent/service/build-direct-message-content.js +1 -1
  91. package/dist/src/agent/service/process-direct-streaming.d.ts +1 -0
  92. package/dist/src/agent/service/process-direct-streaming.js +15 -12
  93. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  94. package/dist/src/agent/service.d.ts +4 -2
  95. package/dist/src/agent/service.js +24 -8
  96. package/dist/src/agent/service.js.map +1 -1
  97. package/dist/src/agent/service.types.d.ts +3 -1
  98. package/dist/src/agent/session/session-inspector.js +1 -1
  99. package/dist/src/agent/skills/config.js +1 -1
  100. package/dist/src/agent/skills/hub-hash.js +2 -2
  101. package/dist/src/agent/skills/hub-lock.js +1 -1
  102. package/dist/src/agent/skills/hub-pull.js +2 -2
  103. package/dist/src/agent/skills/index.js +1 -1
  104. package/dist/src/agent/skills/managed-store.js +1 -1
  105. package/dist/src/agent/skills/scanner.js +1 -1
  106. package/dist/src/agent/skills/skill-manage-ops.js +1 -1
  107. package/dist/src/agent/skills/skill-manager.js +1 -1
  108. package/dist/src/agent/tools/browser/tool/browser-use-tool.js +1 -1
  109. package/dist/src/agent/tools/browser/tool/browser-use-tool.js.map +1 -1
  110. package/dist/src/agent/tools/dreaming-tool.js +1 -1
  111. package/dist/src/agent/tools/factory.js +1 -1
  112. package/dist/src/agent/tools/image-generate-tool.js +1 -1
  113. package/dist/src/agent/tools/search/registry.js +1 -1
  114. package/dist/src/agent/tools/search/registry.js.map +1 -1
  115. package/dist/src/agent/tools/send-media.js +1 -1
  116. package/dist/src/agent/tools/session-search-tool.js +1 -1
  117. package/dist/src/agent/tools/session-search-tool.js.map +1 -1
  118. package/dist/src/agent/tools/skill-manage-tool.js +1 -1
  119. package/dist/src/agent/tools/workflow-tool.js +2 -2
  120. package/dist/src/agent/tools/workflow-tool.js.map +1 -1
  121. package/dist/src/agent/tools/write.js +1 -1
  122. package/dist/src/agent/workflow/catalog.js +1 -1
  123. package/dist/src/agent/workflow/progress-broker.js +1 -1
  124. package/dist/src/agent/workflow/progress-broker.js.map +1 -1
  125. package/dist/src/agent/workflow/subagent-runner.js +1 -1
  126. package/dist/src/agent/workflow/subagent-runner.js.map +1 -1
  127. package/dist/src/auth/credentials.js +3 -3
  128. package/dist/src/auth/profiles/store.js +1 -1
  129. package/dist/src/auth/sync-provider-auth.js +1 -1
  130. package/dist/src/browser/cache-dir-policy.js +1 -1
  131. package/dist/src/browser/cdp-local-launcher.js +2 -2
  132. package/dist/src/browser/providers/browser-ext-install.js +3 -3
  133. package/dist/src/browser/providers/cloakbrowser.js +4 -4
  134. package/dist/src/browser/providers/playwright-doctor.js +1 -1
  135. package/dist/src/browser/stealth.js +1 -1
  136. package/dist/src/channels/attachments/inbound-persist.js +1 -1
  137. package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
  138. package/dist/src/channels/outbound/persist-store.js +1 -1
  139. package/dist/src/channels/pairing/allow-from-file.js +1 -1
  140. package/dist/src/channels/pairing/pairing-store.js +2 -2
  141. package/dist/src/channels/pipeline.js +3 -2
  142. package/dist/src/channels/pipeline.js.map +1 -1
  143. package/dist/src/chat-commands/agent-edit.js +2 -2
  144. package/dist/src/chat-commands/builtins/config.js +2 -2
  145. package/dist/src/chat-commands/context.js +1 -1
  146. package/dist/src/cli/cli-log-level-preset.d.ts +1 -1
  147. package/dist/src/cli/cli-log-level-preset.js +2 -2
  148. package/dist/src/cli/cli-log-level-preset.js.map +1 -1
  149. package/dist/src/cli/commands/config.js +1 -1
  150. package/dist/src/cli/commands/doctor/checks/config-health.js +1 -1
  151. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  152. package/dist/src/cli/commands/doctor/checks/session-integrity.js +2 -2
  153. package/dist/src/cli/commands/doctor/checks/state-integrity.js +1 -1
  154. package/dist/src/cli/commands/doctor/checks/workspace-status.js +1 -1
  155. package/dist/src/cli/commands/extension-dev.js +1 -1
  156. package/dist/src/cli/commands/extension-marketplace.js +1 -1
  157. package/dist/src/cli/commands/extension-pack.js +1 -1
  158. package/dist/src/cli/commands/gateway/logs.js +1 -1
  159. package/dist/src/cli/commands/image.js +1 -1
  160. package/dist/src/cli/commands/init.js +4 -4
  161. package/dist/src/cli/commands/logs.js +3 -3
  162. package/dist/src/cli/commands/logs.js.map +1 -1
  163. package/dist/src/cli/commands/onboard.js +1 -1
  164. package/dist/src/cli/utils/init-workspace-core.js +2 -2
  165. package/dist/src/commands/agents.config.js +1 -1
  166. package/dist/src/config/agent-profile.js +1 -1
  167. package/dist/src/config/gateway-bind.js +1 -1
  168. package/dist/src/config/index.js +5 -5
  169. package/dist/src/config/loader.js +2 -2
  170. package/dist/src/config/models-json.js +2 -2
  171. package/dist/src/config/paths-state.js +1 -1
  172. package/dist/src/config/profile.js +2 -2
  173. package/dist/src/config/workspace-path.js +1 -1
  174. package/dist/src/cron/executor.js +9 -6
  175. package/dist/src/cron/executor.js.map +1 -1
  176. package/dist/src/cron/persistence.js +1 -1
  177. package/dist/src/cron/run-log-store.js +1 -1
  178. package/dist/src/daemon/constants.js +1 -1
  179. package/dist/src/daemon/install-plan.js +2 -2
  180. package/dist/src/daemon/launchd.js +2 -2
  181. package/dist/src/daemon/schtasks.js +2 -2
  182. package/dist/src/daemon/systemd.js +2 -2
  183. package/dist/src/extensions/bundle-mcp.js +1 -1
  184. package/dist/src/extensions/discover-extensions.js +1 -1
  185. package/dist/src/extensions/health.js +1 -1
  186. package/dist/src/extensions/loader.js +1 -1
  187. package/dist/src/extensions/lockfile.js +2 -2
  188. package/dist/src/extensions/update.js +1 -1
  189. package/dist/src/gateway/agents-admin.js +3 -3
  190. package/dist/src/gateway/file-path-classifier.js +2 -2
  191. package/dist/src/gateway/heartbeat/service.js +1 -1
  192. package/dist/src/gateway/hono/app.js +4 -1
  193. package/dist/src/gateway/hono/app.js.map +1 -1
  194. package/dist/src/gateway/hono/lib/config-payload.js +1 -1
  195. package/dist/src/gateway/hono/lib/extension-store.js +2 -2
  196. package/dist/src/gateway/hono/lib/route-logger.d.ts +6 -0
  197. package/dist/src/gateway/hono/lib/route-logger.js +31 -0
  198. package/dist/src/gateway/hono/lib/route-logger.js.map +1 -0
  199. package/dist/src/gateway/hono/lib/static-ui.js +2 -2
  200. package/dist/src/gateway/hono/middleware/auth.js +16 -3
  201. package/dist/src/gateway/hono/middleware/auth.js.map +1 -1
  202. package/dist/src/gateway/hono/middleware/logger.js +1 -1
  203. package/dist/src/gateway/hono/middleware/logger.js.map +1 -1
  204. package/dist/src/gateway/hono/middleware/route-errors.d.ts +5 -0
  205. package/dist/src/gateway/hono/middleware/route-errors.js +27 -0
  206. package/dist/src/gateway/hono/middleware/route-errors.js.map +1 -0
  207. package/dist/src/gateway/hono/oauth.js +1 -1
  208. package/dist/src/gateway/hono/routes/agent-stream.js +6 -0
  209. package/dist/src/gateway/hono/routes/agent-stream.js.map +1 -1
  210. package/dist/src/gateway/hono/routes/agents.js +1 -1
  211. package/dist/src/gateway/hono/routes/auth-registry-extensions.js +1 -1
  212. package/dist/src/gateway/hono/routes/browser-install.js +2 -4
  213. package/dist/src/gateway/hono/routes/browser-install.js.map +1 -1
  214. package/dist/src/gateway/hono/routes/config-patch/misc.js +1 -1
  215. package/dist/src/gateway/hono/routes/config.js +25 -11
  216. package/dist/src/gateway/hono/routes/config.js.map +1 -1
  217. package/dist/src/gateway/hono/routes/cron.js +5 -0
  218. package/dist/src/gateway/hono/routes/cron.js.map +1 -1
  219. package/dist/src/gateway/hono/routes/dreaming.js +1 -1
  220. package/dist/src/gateway/hono/routes/host-fs.js +4 -6
  221. package/dist/src/gateway/hono/routes/host-fs.js.map +1 -1
  222. package/dist/src/gateway/hono/routes/lazy-bundles.js +14 -1
  223. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  224. package/dist/src/gateway/hono/routes/lazy-fallback.js +3 -0
  225. package/dist/src/gateway/hono/routes/lazy-fallback.js.map +1 -1
  226. package/dist/src/gateway/hono/routes/logs.js +39 -7
  227. package/dist/src/gateway/hono/routes/logs.js.map +1 -1
  228. package/dist/src/gateway/hono/routes/mcp.d.ts +3 -0
  229. package/dist/src/gateway/hono/routes/mcp.js +107 -0
  230. package/dist/src/gateway/hono/routes/mcp.js.map +1 -0
  231. package/dist/src/gateway/hono/routes/models.js +1 -1
  232. package/dist/src/gateway/hono/routes/sessions.js +6 -0
  233. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  234. package/dist/src/gateway/hono/routes/shares.js +1 -1
  235. package/dist/src/gateway/hono/routes/update.js +2 -4
  236. package/dist/src/gateway/hono/routes/update.js.map +1 -1
  237. package/dist/src/gateway/hono/routes/voice.js +2 -4
  238. package/dist/src/gateway/hono/routes/voice.js.map +1 -1
  239. package/dist/src/gateway/hono/routes/workspace.js +4 -6
  240. package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
  241. package/dist/src/gateway/hono/sse.js +9 -2
  242. package/dist/src/gateway/hono/sse.js.map +1 -1
  243. package/dist/src/gateway/lock.js +3 -3
  244. package/dist/src/gateway/ports.js +1 -1
  245. package/dist/src/gateway/service/agent-runner.js +3 -3
  246. package/dist/src/gateway/service/agent-runner.js.map +1 -1
  247. package/dist/src/gateway/service/config-coordinator.js +14 -6
  248. package/dist/src/gateway/service/config-coordinator.js.map +1 -1
  249. package/dist/src/gateway/service/marketplace-service.js +3 -3
  250. package/dist/src/gateway/service/marketplace-service.js.map +1 -1
  251. package/dist/src/gateway/service/run-gateway-agent.js +22 -5
  252. package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
  253. package/dist/src/gateway/service/sse-hub.js +1 -1
  254. package/dist/src/gateway/service/sse-hub.js.map +1 -1
  255. package/dist/src/gateway/service.js +13 -6
  256. package/dist/src/gateway/service.js.map +1 -1
  257. package/dist/src/gateway/workspace-fs-file-list.js +1 -1
  258. package/dist/src/heartbeat/index.js +1 -1
  259. package/dist/src/infra/brew.js +1 -1
  260. package/dist/src/infra/package-json.js +1 -1
  261. package/dist/src/infra/package-update-steps.js +1 -1
  262. package/dist/src/infra/path-env.js +2 -2
  263. package/dist/src/infra/restart.js +2 -2
  264. package/dist/src/infra/stable-node-path.js +1 -1
  265. package/dist/src/infra/update-check.js +1 -1
  266. package/dist/src/infra/update-global.js +1 -1
  267. package/dist/src/infra/update-lock.js +3 -3
  268. package/dist/src/infra/update-runner.js +1 -1
  269. package/dist/src/infra/update-startup.js +2 -2
  270. package/dist/src/infra/write-file-atomic.js +2 -2
  271. package/dist/src/mcp/channel-bridge.js +26 -2
  272. package/dist/src/mcp/channel-bridge.js.map +1 -1
  273. package/dist/src/mcp/gateway-http-client.js +24 -2
  274. package/dist/src/mcp/gateway-http-client.js.map +1 -1
  275. package/dist/src/notes/store.js +2 -2
  276. package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
  277. package/dist/src/providers/index.js +2 -2
  278. package/dist/src/providers/model-registry.js +1 -1
  279. package/dist/src/session/config-store.js +12 -6
  280. package/dist/src/session/config-store.js.map +1 -1
  281. package/dist/src/session/index.d.ts +1 -1
  282. package/dist/src/session/index.js +2 -2
  283. package/dist/src/session/init-session-turn.js +2 -2
  284. package/dist/src/session/manager.js +8 -1
  285. package/dist/src/session/manager.js.map +1 -1
  286. package/dist/src/session/parity/jsonl-transcript-io.js +2 -2
  287. package/dist/src/session/parity/sessions-json-file.js +1 -1
  288. package/dist/src/session/parity/transcript-file-lock.js +2 -2
  289. package/dist/src/session/parity/transcript-paths.js +1 -1
  290. package/dist/src/session/resolve-session.js +4 -4
  291. package/dist/src/session/search-index-cache.js +1 -1
  292. package/dist/src/session/search-index.js +1 -1
  293. package/dist/src/session/session-title.d.ts +19 -3
  294. package/dist/src/session/session-title.js +84 -9
  295. package/dist/src/session/session-title.js.map +1 -1
  296. package/dist/src/session/store.js +6 -6
  297. package/dist/src/share/share-auto.js +2 -2
  298. package/dist/src/share/share-store.js +3 -3
  299. package/dist/src/share/share-thumbnail.js +2 -2
  300. package/dist/src/share/share-zip.js +1 -1
  301. package/dist/src/share/site-share-store.js +3 -3
  302. package/dist/src/share/site-static-serve.js +1 -1
  303. package/dist/src/tui/clipboard-image.js +3 -3
  304. package/dist/src/tui/theme-manager.js +1 -1
  305. package/dist/src/tui/tui-keybindings-file.js +1 -1
  306. package/dist/src/tui/tui-scoped-models.js +2 -2
  307. package/dist/src/tui/tui-settings.js +1 -1
  308. package/dist/src/tui/tui.js +3 -3
  309. package/dist/src/tunnel/frpc-binary.js +3 -3
  310. package/dist/src/tunnel/frpc-config.js +1 -1
  311. package/dist/src/tunnel/frpc-extract.js +1 -1
  312. package/dist/src/tunnel/tunnel-state.js +1 -1
  313. package/dist/src/utils/index.js +4 -4
  314. package/dist/src/utils/logger/audit.js +1 -1
  315. package/dist/src/utils/logger/config.js +2 -6
  316. package/dist/src/utils/logger/config.js.map +1 -1
  317. package/dist/src/utils/logger/context.d.ts +3 -22
  318. package/dist/src/utils/logger/context.js +4 -32
  319. package/dist/src/utils/logger/context.js.map +1 -1
  320. package/dist/src/utils/logger/index.d.ts +4 -7
  321. package/dist/src/utils/logger/index.js +9 -28
  322. package/dist/src/utils/logger/index.js.map +1 -1
  323. package/dist/src/utils/logger/log-store.d.ts +14 -32
  324. package/dist/src/utils/logger/log-store.js +68 -119
  325. package/dist/src/utils/logger/log-store.js.map +1 -1
  326. package/dist/src/utils/logger/log-stream.d.ts +5 -70
  327. package/dist/src/utils/logger/log-stream.js +67 -178
  328. package/dist/src/utils/logger/log-stream.js.map +1 -1
  329. package/dist/src/utils/logger/pino-record.d.ts +8 -0
  330. package/dist/src/utils/logger/pino-record.js +83 -0
  331. package/dist/src/utils/logger/pino-record.js.map +1 -0
  332. package/dist/src/utils/logger/rotation.js +1 -1
  333. package/dist/src/utils/logger/stats.d.ts +1 -1
  334. package/dist/src/utils/logger/stats.js +2 -2
  335. package/dist/src/utils/logger/stats.js.map +1 -1
  336. package/dist/src/utils/logger/streams.js +18 -0
  337. package/dist/src/utils/logger/streams.js.map +1 -1
  338. package/dist/src/utils/logger/types.d.ts +0 -9
  339. package/dist/src/utils/logger/types.js.map +1 -1
  340. package/dist/src/utils/logger.js +4 -4
  341. package/dist/src/voice/tts/audio.js +1 -1
  342. package/dist/src/voice/tts/providers/edge-speech.js +2 -2
  343. package/dist/src/workflows/store/event-store.js +1 -1
  344. package/dist/src/workflows/store/run-store.js +1 -1
  345. package/package.json +2 -1
  346. package/dist/gateway/static/root/assets/agents-C7tTJLP9.js +0 -222
  347. package/dist/gateway/static/root/assets/apps-page-BbzdMyrg.js +0 -1
  348. package/dist/gateway/static/root/assets/channels-settings-B49vG2hE.js +0 -1
  349. package/dist/gateway/static/root/assets/cron-page-Bjx7IOdR.js +0 -1
  350. package/dist/gateway/static/root/assets/index-CwDdudZM.css +0 -1
  351. package/dist/gateway/static/root/assets/logs-page-BxukQ-J-.js +0 -1
  352. package/dist/gateway/static/root/assets/sessions-page-BFD2_-Cl.js +0 -1
  353. package/dist/gateway/static/root/assets/settings-page-BiP5iH46.js +0 -2
  354. package/dist/gateway/static/root/assets/skills-page-CNDctFIn.js +0 -2
  355. package/dist/gateway/static/root/assets/voice-api-key-field-CalxUkxm.js +0 -1
  356. package/dist/gateway/static/root/assets/workflows-page-D2fRxXJG.js +0 -27
@@ -1,7 +1,7 @@
1
1
  import { init_write_file_atomic, writeTextAtomic } from "../../../infra/write-file-atomic.js";
2
- import { readFile } from "node:fs/promises";
3
- import { join } from "node:path";
4
2
  import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { readFile } from "node:fs/promises";
5
5
  //#region src/gateway/hono/lib/extension-store.ts
6
6
  init_write_file_atomic();
7
7
  /** Extension UI: write-through JSON KV per namespace under ~/.xopc/extensions/{namespace}/storage.json */
@@ -0,0 +1,6 @@
1
+ import type { Context } from 'hono';
2
+ import { type ContextualLogger } from '../../../utils/logger.js';
3
+ /** Stable gateway route logger prefix: `Gateway:<Name>`. */
4
+ export declare function createGatewayRouteLogger(name: string): ContextualLogger;
5
+ export declare function logRouteError(log: ContextualLogger, c: Context, err: unknown, phase: string, extra?: Record<string, unknown>): void;
6
+ export declare function logRouteWarn(log: ContextualLogger, c: Context, message: string, phase: string, extra?: Record<string, unknown>): void;
@@ -0,0 +1,31 @@
1
+ import { createLogger } from "../../../utils/logger/index.js";
2
+ import { init_logger } from "../../../utils/logger.js";
3
+ //#region src/gateway/hono/lib/route-logger.ts
4
+ init_logger();
5
+ /** Stable gateway route logger prefix: `Gateway:<Name>`. */
6
+ function createGatewayRouteLogger(name) {
7
+ return createLogger(`Gateway:${name}`);
8
+ }
9
+ function logRouteError(log, c, err, phase, extra) {
10
+ const em = err instanceof Error ? err.message : String(err);
11
+ log.error({
12
+ err,
13
+ errorMessage: em,
14
+ phase,
15
+ method: c.req.method,
16
+ path: c.req.path,
17
+ ...extra
18
+ }, `Route error ${c.req.method} ${c.req.path}: ${em}`);
19
+ }
20
+ function logRouteWarn(log, c, message, phase, extra) {
21
+ log.warn({
22
+ phase,
23
+ method: c.req.method,
24
+ path: c.req.path,
25
+ ...extra
26
+ }, message);
27
+ }
28
+ //#endregion
29
+ export { createGatewayRouteLogger, logRouteError, logRouteWarn };
30
+
31
+ //# sourceMappingURL=route-logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-logger.js","names":[],"sources":["../../../../../src/gateway/hono/lib/route-logger.ts"],"sourcesContent":["import type { Context } from 'hono';\n\nimport { createLogger, type ContextualLogger } from '../../../utils/logger.js';\n\n/** Stable gateway route logger prefix: `Gateway:<Name>`. */\nexport function createGatewayRouteLogger(name: string): ContextualLogger {\n return createLogger(`Gateway:${name}`);\n}\n\nexport function logRouteError(\n log: ContextualLogger,\n c: Context,\n err: unknown,\n phase: string,\n extra?: Record<string, unknown>,\n): void {\n const em = err instanceof Error ? err.message : String(err);\n log.error(\n {\n err,\n errorMessage: em,\n phase,\n method: c.req.method,\n path: c.req.path,\n ...extra,\n },\n `Route error ${c.req.method} ${c.req.path}: ${em}`,\n );\n}\n\nexport function logRouteWarn(\n log: ContextualLogger,\n c: Context,\n message: string,\n phase: string,\n extra?: Record<string, unknown>,\n): void {\n log.warn(\n {\n phase,\n method: c.req.method,\n path: c.req.path,\n ...extra,\n },\n message,\n );\n}\n"],"mappings":";;;aAE+E;;AAG/E,SAAgB,yBAAyB,MAAgC;AACvE,QAAO,aAAa,WAAW,OAAO;;AAGxC,SAAgB,cACd,KACA,GACA,KACA,OACA,OACM;CACN,MAAM,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC3D,KAAI,MACF;EACE;EACA,cAAc;EACd;EACA,QAAQ,EAAE,IAAI;EACd,MAAM,EAAE,IAAI;EACZ,GAAG;EACJ,EACD,eAAe,EAAE,IAAI,OAAO,GAAG,EAAE,IAAI,KAAK,IAAI,KAC/C;;AAGH,SAAgB,aACd,KACA,GACA,SACA,OACA,OACM;AACN,KAAI,KACF;EACE;EACA,QAAQ,EAAE,IAAI;EACd,MAAM,EAAE,IAAI;EACZ,GAAG;EACJ,EACD,QACD"}
@@ -1,6 +1,6 @@
1
- import { createHash } from "node:crypto";
2
- import { readFileSync } from "node:fs";
3
1
  import { dirname, resolve } from "node:path";
2
+ import { readFileSync } from "node:fs";
3
+ import { createHash } from "node:crypto";
4
4
  import { fileURLToPath } from "node:url";
5
5
  //#region src/gateway/hono/lib/static-ui.ts
6
6
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -1,3 +1,4 @@
1
+ import { logAuthEvent } from "../../../utils/logger/audit.js";
1
2
  import { createLogger } from "../../../utils/logger/index.js";
2
3
  import { init_logger } from "../../../utils/logger.js";
3
4
  import { safeEqualSecret } from "../../security/secret-equal.js";
@@ -12,7 +13,7 @@ import { createMiddleware } from "hono/factory";
12
13
  import { getConnInfo } from "@hono/node-server/conninfo";
13
14
  //#region src/gateway/hono/middleware/auth.ts
14
15
  init_logger();
15
- const log = createLogger("Hono:Auth");
16
+ const log = createLogger("Gateway:Auth");
16
17
  function validateToken(providedToken, expectedToken) {
17
18
  if (!providedToken) return false;
18
19
  return safeEqualSecret(providedToken, expectedToken);
@@ -192,8 +193,14 @@ function auth(config) {
192
193
  path: c.req.path,
193
194
  method: c.req.method,
194
195
  clientIp,
195
- reason: "missing_token"
196
+ reason: "missing_token",
197
+ phase: "gateway.http.auth"
196
198
  }, "HTTP auth rejected: no Bearer or ?token=");
199
+ logAuthEvent("auth.failed", {
200
+ ip: clientIp,
201
+ result: "denied",
202
+ reason: "missing_token"
203
+ });
197
204
  return c.json({
198
205
  error: "Unauthorized",
199
206
  code: "missing_token",
@@ -205,8 +212,14 @@ function auth(config) {
205
212
  path: c.req.path,
206
213
  method: c.req.method,
207
214
  clientIp,
208
- reason: "invalid_token"
215
+ reason: "invalid_token",
216
+ phase: "gateway.http.auth"
209
217
  }, "HTTP auth rejected: token mismatch");
218
+ logAuthEvent("auth.failed", {
219
+ ip: clientIp,
220
+ result: "failure",
221
+ reason: "invalid_token"
222
+ });
210
223
  return c.json({
211
224
  error: "Unauthorized",
212
225
  code: "invalid_token",
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","names":[],"sources":["../../../../../src/gateway/hono/middleware/auth.ts"],"sourcesContent":["import { createMiddleware } from 'hono/factory';\nimport type { Context } from 'hono';\nimport { getConnInfo } from '@hono/node-server/conninfo';\n\nimport type { GatewayAuthConfig } from '../../../config/schema.js';\nimport type { ResolvedGatewayAuth } from '../../auth.js';\nimport { resolveClientIpFromRequest } from '../../client-ip.js';\nimport {\n authPolicyConfig,\n buckets,\n isAuthRateLimitGloballyDisabled,\n resolveAuthRateLimit,\n resolveAuthTracking,\n type ResolvedAuthRateLimitConfig,\n} from '../../rate-limit/index.js';\nimport { getClientIpFromHeaders } from '../../security/loopback.js';\nimport { safeEqualSecret } from '../../security/secret-equal.js';\nimport { authorizeTrustedProxy } from '../../trusted-proxy.js';\nimport { createLogger } from '../../../utils/logger.js';\n\nconst log = createLogger('Hono:Auth');\n\nexport interface AuthConfig {\n token?: string;\n /** Current gateway auth from config (for rate-limit settings); optional. */\n getGatewayAuth?: () => GatewayAuthConfig | undefined;\n getResolvedAuth?: () => ResolvedGatewayAuth;\n getTrustedProxyContext?: () => {\n trustedProxies?: string[];\n allowRealIpFallback?: boolean;\n };\n}\n\nfunction validateToken(providedToken: string | undefined, expectedToken: string): boolean {\n if (!providedToken) return false;\n return safeEqualSecret(providedToken, expectedToken);\n}\n\nfunction extractTokenFromHeader(authHeader: string | null): string | null {\n if (!authHeader) return null;\n const parts = authHeader.split(' ');\n if (parts.length === 2 && parts[0].toLowerCase() === 'bearer') return parts[1];\n return authHeader;\n}\n\n/**\n * SECURITY: query-string tokens leak into server logs, Referer headers, and\n * browser history. We accept them only where the `Authorization` header cannot\n * be set — SSE/WebSocket (`EventSource`) and `<img>` subresource loads for agent\n * avatars. Note media uses Bearer-authenticated blob fetch in the gateway console.\n */\nfunction extractTokenFromQuery(url: string): string | null {\n return new URL(url).searchParams.get('token');\n}\n\nconst QUERY_TOKEN_ALLOWED_PATHS = new Set(['/api/events', '/api/ws']);\n\nconst AGENT_AVATAR_GET_PATH = /^\\/api\\/agents\\/[^/]+\\/avatar$/;\n\n/** Exported for gateway security tests. */\nexport function isQueryTokenAllowedPath(path: string, method: string): boolean {\n if (QUERY_TOKEN_ALLOWED_PATHS.has(path) || path.startsWith('/api/events')) {\n return true;\n }\n if (method === 'GET' && AGENT_AVATAR_GET_PATH.test(path)) {\n return true;\n }\n return false;\n}\n\nfunction resolveRemoteAddress(c: Context): string | undefined {\n try {\n return getConnInfo(c).remote.address;\n } catch {\n return undefined;\n }\n}\n\nfunction resolveMiddlewareClientIp(\n c: Context,\n trustedProxies?: string[],\n allowRealIpFallback?: boolean,\n): string {\n if (trustedProxies?.length) {\n return resolveClientIpFromRequest({\n remoteAddress: resolveRemoteAddress(c),\n getHeader: (name) => c.req.header(name),\n trustedProxies,\n allowRealIpFallback,\n });\n }\n return getClientIpFromHeaders({\n get: (name: string) => c.req.header(name) ?? undefined,\n });\n}\n\ntype RateLimitContext = {\n active: boolean;\n cfg: ResolvedAuthRateLimitConfig;\n /** `undefined` when the client is exempted (loopback, disabled, etc.). */\n trackingKey: string | undefined;\n};\n\nfunction buildRateLimitContext(\n getGatewayAuth: AuthConfig['getGatewayAuth'],\n clientIp: string,\n origin: string | undefined,\n): RateLimitContext {\n const cfg = resolveAuthRateLimit(getGatewayAuth?.()?.rateLimit);\n const active = cfg.enabled && !isAuthRateLimitGloballyDisabled();\n if (!active) return { active: false, cfg, trackingKey: undefined };\n const tracking = resolveAuthTracking({ clientIp, origin, cfg: authPolicyConfig(cfg) });\n return {\n active: true,\n cfg,\n trackingKey: tracking.exempt ? undefined : tracking.key,\n };\n}\n\nfunction checkBlocked(rl: RateLimitContext): { blocked: false } | { blocked: true; retryAfterSec: number } {\n if (!rl.active || rl.trackingKey === undefined) return { blocked: false };\n return buckets.authFailure(rl.cfg).check(rl.trackingKey);\n}\n\nfunction recordFailure(rl: RateLimitContext): void {\n if (!rl.active || rl.trackingKey === undefined) return;\n buckets.authFailure(rl.cfg).fail(rl.trackingKey);\n}\n\nfunction recordSuccess(rl: RateLimitContext): void {\n if (!rl.active || rl.trackingKey === undefined) return;\n buckets.authFailure(rl.cfg).succeed(rl.trackingKey);\n}\n\nfunction blockedResponse(c: Context, retryAfterSec: number) {\n c.header('Retry-After', String(retryAfterSec));\n return c.json(\n {\n error: 'Too Many Requests',\n code: 'auth_blocked',\n message: 'Too many authentication attempts',\n retryAfter: retryAfterSec,\n },\n 429,\n );\n}\n\nexport function auth(config?: AuthConfig) {\n const { token, getGatewayAuth, getResolvedAuth, getTrustedProxyContext } = config || {};\n\n return createMiddleware(async (c, next) => {\n const resolvedAuth = getResolvedAuth?.();\n const authMode = resolvedAuth?.mode ?? (token ? 'token' : 'none');\n\n if (authMode === 'trusted-proxy') {\n const proxyContext = getTrustedProxyContext?.();\n const trustedProxies = proxyContext?.trustedProxies;\n const trustedProxyConfig = resolvedAuth?.trustedProxy;\n\n const clientIp = resolveMiddlewareClientIp(c, trustedProxies, proxyContext?.allowRealIpFallback);\n const origin = c.req.header('origin');\n const rl = buildRateLimitContext(getGatewayAuth, clientIp, origin);\n\n // Server misconfiguration — not an attack signal. Don't count.\n if (!trustedProxyConfig) {\n log.warn(\n { path: c.req.path, method: c.req.method, clientIp, reason: 'trusted_proxy_config_missing' },\n 'HTTP auth rejected: trusted-proxy config missing',\n );\n return c.json(\n { error: 'Unauthorized', code: 'auth_unconfigured', message: 'Trusted-proxy auth is not configured' },\n 401,\n );\n }\n\n const blocked = checkBlocked(rl);\n if (blocked.blocked) {\n log.warn(\n { clientIp, origin: origin ?? undefined, path: c.req.path, method: c.req.method, retryAfterSec: blocked.retryAfterSec, reason: 'auth_blocked' },\n 'Auth rate limit blocked',\n );\n return blockedResponse(c, blocked.retryAfterSec);\n }\n\n const result = authorizeTrustedProxy({\n remoteAddress: resolveRemoteAddress(c),\n getHeader: (name) => c.req.header(name),\n trustedProxies,\n trustedProxyConfig,\n });\n\n if (result.ok === false) {\n recordFailure(rl);\n log.warn(\n { path: c.req.path, method: c.req.method, clientIp, reason: result.reason },\n `HTTP auth rejected: trusted-proxy validation failed (${result.reason})`,\n );\n return c.json(\n { error: 'Unauthorized', code: 'invalid_proxy_credentials', message: 'Trusted-proxy authentication failed' },\n 401,\n );\n }\n\n recordSuccess(rl);\n await next();\n return;\n }\n\n if (authMode === 'none' || !token) {\n return next();\n }\n\n const proxyContext = getTrustedProxyContext?.();\n const clientIp = resolveMiddlewareClientIp(c, proxyContext?.trustedProxies, proxyContext?.allowRealIpFallback);\n const origin = c.req.header('origin');\n const rl = buildRateLimitContext(getGatewayAuth, clientIp, origin);\n\n const authHeader = extractTokenFromHeader(c.req.header('authorization'));\n const requestPath = new URL(c.req.url).pathname;\n const queryToken = isQueryTokenAllowedPath(requestPath, c.req.method)\n ? extractTokenFromQuery(c.req.url)\n : null;\n\n if (!authHeader && queryToken === null && new URL(c.req.url).searchParams.has('token')) {\n log.warn(\n { path: requestPath, method: c.req.method, clientIp },\n 'Token in query string rejected: use Authorization header for this endpoint',\n );\n }\n\n const providedToken = authHeader || queryToken;\n\n if (providedToken && validateToken(providedToken, token)) {\n recordSuccess(rl);\n await next();\n return;\n }\n\n const blocked = checkBlocked(rl);\n if (blocked.blocked) {\n log.warn(\n { clientIp, origin: origin ?? undefined, path: requestPath, method: c.req.method, retryAfterSec: blocked.retryAfterSec, reason: 'auth_blocked' },\n 'Auth rate limit blocked',\n );\n return blockedResponse(c, blocked.retryAfterSec);\n }\n\n // Missing token is an unauthenticated request, not a brute-force signal —\n // page reloads / SDK cold starts often hit endpoints before the token is\n // attached. Counting this would lock users out of the token-entry path.\n if (!providedToken) {\n log.warn(\n { path: c.req.path, method: c.req.method, clientIp, reason: 'missing_token' },\n 'HTTP auth rejected: no Bearer or ?token=',\n );\n return c.json(\n { error: 'Unauthorized', code: 'missing_token', message: 'Missing authentication token' },\n 401,\n );\n }\n\n recordFailure(rl);\n log.warn(\n { path: c.req.path, method: c.req.method, clientIp, reason: 'invalid_token' },\n 'HTTP auth rejected: token mismatch',\n );\n return c.json(\n { error: 'Unauthorized', code: 'invalid_token', message: 'Invalid authentication token' },\n 401,\n );\n });\n}\n"],"mappings":";;;;;;;;;;;;;aAkBwD;AAExD,MAAM,MAAM,aAAa,YAAY;AAarC,SAAS,cAAc,eAAmC,eAAgC;AACxF,KAAI,CAAC,cAAe,QAAO;AAC3B,QAAO,gBAAgB,eAAe,cAAc;;AAGtD,SAAS,uBAAuB,YAA0C;AACxE,KAAI,CAAC,WAAY,QAAO;CACxB,MAAM,QAAQ,WAAW,MAAM,IAAI;AACnC,KAAI,MAAM,WAAW,KAAK,MAAM,GAAG,aAAa,KAAK,SAAU,QAAO,MAAM;AAC5E,QAAO;;;;;;;;AAST,SAAS,sBAAsB,KAA4B;AACzD,QAAO,IAAI,IAAI,IAAI,CAAC,aAAa,IAAI,QAAQ;;AAG/C,MAAM,4BAA4B,IAAI,IAAI,CAAC,eAAe,UAAU,CAAC;AAErE,MAAM,wBAAwB;;AAG9B,SAAgB,wBAAwB,MAAc,QAAyB;AAC7E,KAAI,0BAA0B,IAAI,KAAK,IAAI,KAAK,WAAW,cAAc,CACvE,QAAO;AAET,KAAI,WAAW,SAAS,sBAAsB,KAAK,KAAK,CACtD,QAAO;AAET,QAAO;;AAGT,SAAS,qBAAqB,GAAgC;AAC5D,KAAI;AACF,SAAO,YAAY,EAAE,CAAC,OAAO;SACvB;AACN;;;AAIJ,SAAS,0BACP,GACA,gBACA,qBACQ;AACR,KAAI,gBAAgB,OAClB,QAAO,2BAA2B;EAChC,eAAe,qBAAqB,EAAE;EACtC,YAAY,SAAS,EAAE,IAAI,OAAO,KAAK;EACvC;EACA;EACD,CAAC;AAEJ,QAAO,uBAAuB,EAC5B,MAAM,SAAiB,EAAE,IAAI,OAAO,KAAK,IAAI,KAAA,GAC9C,CAAC;;AAUJ,SAAS,sBACP,gBACA,UACA,QACkB;CAClB,MAAM,MAAM,qBAAqB,kBAAkB,EAAE,UAAU;AAE/D,KAAI,EADW,IAAI,WAAW,CAAC,iCAAiC,EACnD,QAAO;EAAE,QAAQ;EAAO;EAAK,aAAa,KAAA;EAAW;CAClE,MAAM,WAAW,oBAAoB;EAAE;EAAU;EAAQ,KAAK,iBAAiB,IAAI;EAAE,CAAC;AACtF,QAAO;EACL,QAAQ;EACR;EACA,aAAa,SAAS,SAAS,KAAA,IAAY,SAAS;EACrD;;AAGH,SAAS,aAAa,IAAqF;AACzG,KAAI,CAAC,GAAG,UAAU,GAAG,gBAAgB,KAAA,EAAW,QAAO,EAAE,SAAS,OAAO;AACzE,QAAO,QAAQ,YAAY,GAAG,IAAI,CAAC,MAAM,GAAG,YAAY;;AAG1D,SAAS,cAAc,IAA4B;AACjD,KAAI,CAAC,GAAG,UAAU,GAAG,gBAAgB,KAAA,EAAW;AAChD,SAAQ,YAAY,GAAG,IAAI,CAAC,KAAK,GAAG,YAAY;;AAGlD,SAAS,cAAc,IAA4B;AACjD,KAAI,CAAC,GAAG,UAAU,GAAG,gBAAgB,KAAA,EAAW;AAChD,SAAQ,YAAY,GAAG,IAAI,CAAC,QAAQ,GAAG,YAAY;;AAGrD,SAAS,gBAAgB,GAAY,eAAuB;AAC1D,GAAE,OAAO,eAAe,OAAO,cAAc,CAAC;AAC9C,QAAO,EAAE,KACP;EACE,OAAO;EACP,MAAM;EACN,SAAS;EACT,YAAY;EACb,EACD,IACD;;AAGH,SAAgB,KAAK,QAAqB;CACxC,MAAM,EAAE,OAAO,gBAAgB,iBAAiB,2BAA2B,UAAU,EAAE;AAEvF,QAAO,iBAAiB,OAAO,GAAG,SAAS;EACzC,MAAM,eAAe,mBAAmB;EACxC,MAAM,WAAW,cAAc,SAAS,QAAQ,UAAU;AAE1D,MAAI,aAAa,iBAAiB;GAChC,MAAM,eAAe,0BAA0B;GAC/C,MAAM,iBAAiB,cAAc;GACrC,MAAM,qBAAqB,cAAc;GAEzC,MAAM,WAAW,0BAA0B,GAAG,gBAAgB,cAAc,oBAAoB;GAChG,MAAM,SAAS,EAAE,IAAI,OAAO,SAAS;GACrC,MAAM,KAAK,sBAAsB,gBAAgB,UAAU,OAAO;AAGlE,OAAI,CAAC,oBAAoB;AACvB,QAAI,KACF;KAAE,MAAM,EAAE,IAAI;KAAM,QAAQ,EAAE,IAAI;KAAQ;KAAU,QAAQ;KAAgC,EAC5F,mDACD;AACD,WAAO,EAAE,KACP;KAAE,OAAO;KAAgB,MAAM;KAAqB,SAAS;KAAwC,EACrG,IACD;;GAGH,MAAM,UAAU,aAAa,GAAG;AAChC,OAAI,QAAQ,SAAS;AACnB,QAAI,KACF;KAAE;KAAU,QAAQ,UAAU,KAAA;KAAW,MAAM,EAAE,IAAI;KAAM,QAAQ,EAAE,IAAI;KAAQ,eAAe,QAAQ;KAAe,QAAQ;KAAgB,EAC/I,0BACD;AACD,WAAO,gBAAgB,GAAG,QAAQ,cAAc;;GAGlD,MAAM,SAAS,sBAAsB;IACnC,eAAe,qBAAqB,EAAE;IACtC,YAAY,SAAS,EAAE,IAAI,OAAO,KAAK;IACvC;IACA;IACD,CAAC;AAEF,OAAI,OAAO,OAAO,OAAO;AACvB,kBAAc,GAAG;AACjB,QAAI,KACF;KAAE,MAAM,EAAE,IAAI;KAAM,QAAQ,EAAE,IAAI;KAAQ;KAAU,QAAQ,OAAO;KAAQ,EAC3E,wDAAwD,OAAO,OAAO,GACvE;AACD,WAAO,EAAE,KACP;KAAE,OAAO;KAAgB,MAAM;KAA6B,SAAS;KAAuC,EAC5G,IACD;;AAGH,iBAAc,GAAG;AACjB,SAAM,MAAM;AACZ;;AAGF,MAAI,aAAa,UAAU,CAAC,MAC1B,QAAO,MAAM;EAGf,MAAM,eAAe,0BAA0B;EAC/C,MAAM,WAAW,0BAA0B,GAAG,cAAc,gBAAgB,cAAc,oBAAoB;EAC9G,MAAM,SAAS,EAAE,IAAI,OAAO,SAAS;EACrC,MAAM,KAAK,sBAAsB,gBAAgB,UAAU,OAAO;EAElE,MAAM,aAAa,uBAAuB,EAAE,IAAI,OAAO,gBAAgB,CAAC;EACxE,MAAM,cAAc,IAAI,IAAI,EAAE,IAAI,IAAI,CAAC;EACvC,MAAM,aAAa,wBAAwB,aAAa,EAAE,IAAI,OAAO,GACjE,sBAAsB,EAAE,IAAI,IAAI,GAChC;AAEJ,MAAI,CAAC,cAAc,eAAe,QAAQ,IAAI,IAAI,EAAE,IAAI,IAAI,CAAC,aAAa,IAAI,QAAQ,CACpF,KAAI,KACF;GAAE,MAAM;GAAa,QAAQ,EAAE,IAAI;GAAQ;GAAU,EACrD,6EACD;EAGH,MAAM,gBAAgB,cAAc;AAEpC,MAAI,iBAAiB,cAAc,eAAe,MAAM,EAAE;AACxD,iBAAc,GAAG;AACjB,SAAM,MAAM;AACZ;;EAGF,MAAM,UAAU,aAAa,GAAG;AAChC,MAAI,QAAQ,SAAS;AACnB,OAAI,KACF;IAAE;IAAU,QAAQ,UAAU,KAAA;IAAW,MAAM;IAAa,QAAQ,EAAE,IAAI;IAAQ,eAAe,QAAQ;IAAe,QAAQ;IAAgB,EAChJ,0BACD;AACD,UAAO,gBAAgB,GAAG,QAAQ,cAAc;;AAMlD,MAAI,CAAC,eAAe;AAClB,OAAI,KACF;IAAE,MAAM,EAAE,IAAI;IAAM,QAAQ,EAAE,IAAI;IAAQ;IAAU,QAAQ;IAAiB,EAC7E,2CACD;AACD,UAAO,EAAE,KACP;IAAE,OAAO;IAAgB,MAAM;IAAiB,SAAS;IAAgC,EACzF,IACD;;AAGH,gBAAc,GAAG;AACjB,MAAI,KACF;GAAE,MAAM,EAAE,IAAI;GAAM,QAAQ,EAAE,IAAI;GAAQ;GAAU,QAAQ;GAAiB,EAC7E,qCACD;AACD,SAAO,EAAE,KACP;GAAE,OAAO;GAAgB,MAAM;GAAiB,SAAS;GAAgC,EACzF,IACD;GACD"}
1
+ {"version":3,"file":"auth.js","names":[],"sources":["../../../../../src/gateway/hono/middleware/auth.ts"],"sourcesContent":["import { createMiddleware } from 'hono/factory';\nimport type { Context } from 'hono';\nimport { getConnInfo } from '@hono/node-server/conninfo';\n\nimport type { GatewayAuthConfig } from '../../../config/schema.js';\nimport type { ResolvedGatewayAuth } from '../../auth.js';\nimport { resolveClientIpFromRequest } from '../../client-ip.js';\nimport {\n authPolicyConfig,\n buckets,\n isAuthRateLimitGloballyDisabled,\n resolveAuthRateLimit,\n resolveAuthTracking,\n type ResolvedAuthRateLimitConfig,\n} from '../../rate-limit/index.js';\nimport { getClientIpFromHeaders } from '../../security/loopback.js';\nimport { safeEqualSecret } from '../../security/secret-equal.js';\nimport { authorizeTrustedProxy } from '../../trusted-proxy.js';\nimport { createLogger, logAuthEvent } from '../../../utils/logger.js';\n\nconst log = createLogger('Gateway:Auth');\n\nexport interface AuthConfig {\n token?: string;\n /** Current gateway auth from config (for rate-limit settings); optional. */\n getGatewayAuth?: () => GatewayAuthConfig | undefined;\n getResolvedAuth?: () => ResolvedGatewayAuth;\n getTrustedProxyContext?: () => {\n trustedProxies?: string[];\n allowRealIpFallback?: boolean;\n };\n}\n\nfunction validateToken(providedToken: string | undefined, expectedToken: string): boolean {\n if (!providedToken) return false;\n return safeEqualSecret(providedToken, expectedToken);\n}\n\nfunction extractTokenFromHeader(authHeader: string | null): string | null {\n if (!authHeader) return null;\n const parts = authHeader.split(' ');\n if (parts.length === 2 && parts[0].toLowerCase() === 'bearer') return parts[1];\n return authHeader;\n}\n\n/**\n * SECURITY: query-string tokens leak into server logs, Referer headers, and\n * browser history. We accept them only where the `Authorization` header cannot\n * be set — SSE/WebSocket (`EventSource`) and `<img>` subresource loads for agent\n * avatars. Note media uses Bearer-authenticated blob fetch in the gateway console.\n */\nfunction extractTokenFromQuery(url: string): string | null {\n return new URL(url).searchParams.get('token');\n}\n\nconst QUERY_TOKEN_ALLOWED_PATHS = new Set(['/api/events', '/api/ws']);\n\nconst AGENT_AVATAR_GET_PATH = /^\\/api\\/agents\\/[^/]+\\/avatar$/;\n\n/** Exported for gateway security tests. */\nexport function isQueryTokenAllowedPath(path: string, method: string): boolean {\n if (QUERY_TOKEN_ALLOWED_PATHS.has(path) || path.startsWith('/api/events')) {\n return true;\n }\n if (method === 'GET' && AGENT_AVATAR_GET_PATH.test(path)) {\n return true;\n }\n return false;\n}\n\nfunction resolveRemoteAddress(c: Context): string | undefined {\n try {\n return getConnInfo(c).remote.address;\n } catch {\n return undefined;\n }\n}\n\nfunction resolveMiddlewareClientIp(\n c: Context,\n trustedProxies?: string[],\n allowRealIpFallback?: boolean,\n): string {\n if (trustedProxies?.length) {\n return resolveClientIpFromRequest({\n remoteAddress: resolveRemoteAddress(c),\n getHeader: (name) => c.req.header(name),\n trustedProxies,\n allowRealIpFallback,\n });\n }\n return getClientIpFromHeaders({\n get: (name: string) => c.req.header(name) ?? undefined,\n });\n}\n\ntype RateLimitContext = {\n active: boolean;\n cfg: ResolvedAuthRateLimitConfig;\n /** `undefined` when the client is exempted (loopback, disabled, etc.). */\n trackingKey: string | undefined;\n};\n\nfunction buildRateLimitContext(\n getGatewayAuth: AuthConfig['getGatewayAuth'],\n clientIp: string,\n origin: string | undefined,\n): RateLimitContext {\n const cfg = resolveAuthRateLimit(getGatewayAuth?.()?.rateLimit);\n const active = cfg.enabled && !isAuthRateLimitGloballyDisabled();\n if (!active) return { active: false, cfg, trackingKey: undefined };\n const tracking = resolveAuthTracking({ clientIp, origin, cfg: authPolicyConfig(cfg) });\n return {\n active: true,\n cfg,\n trackingKey: tracking.exempt ? undefined : tracking.key,\n };\n}\n\nfunction checkBlocked(rl: RateLimitContext): { blocked: false } | { blocked: true; retryAfterSec: number } {\n if (!rl.active || rl.trackingKey === undefined) return { blocked: false };\n return buckets.authFailure(rl.cfg).check(rl.trackingKey);\n}\n\nfunction recordFailure(rl: RateLimitContext): void {\n if (!rl.active || rl.trackingKey === undefined) return;\n buckets.authFailure(rl.cfg).fail(rl.trackingKey);\n}\n\nfunction recordSuccess(rl: RateLimitContext): void {\n if (!rl.active || rl.trackingKey === undefined) return;\n buckets.authFailure(rl.cfg).succeed(rl.trackingKey);\n}\n\nfunction blockedResponse(c: Context, retryAfterSec: number) {\n c.header('Retry-After', String(retryAfterSec));\n return c.json(\n {\n error: 'Too Many Requests',\n code: 'auth_blocked',\n message: 'Too many authentication attempts',\n retryAfter: retryAfterSec,\n },\n 429,\n );\n}\n\nexport function auth(config?: AuthConfig) {\n const { token, getGatewayAuth, getResolvedAuth, getTrustedProxyContext } = config || {};\n\n return createMiddleware(async (c, next) => {\n const resolvedAuth = getResolvedAuth?.();\n const authMode = resolvedAuth?.mode ?? (token ? 'token' : 'none');\n\n if (authMode === 'trusted-proxy') {\n const proxyContext = getTrustedProxyContext?.();\n const trustedProxies = proxyContext?.trustedProxies;\n const trustedProxyConfig = resolvedAuth?.trustedProxy;\n\n const clientIp = resolveMiddlewareClientIp(c, trustedProxies, proxyContext?.allowRealIpFallback);\n const origin = c.req.header('origin');\n const rl = buildRateLimitContext(getGatewayAuth, clientIp, origin);\n\n // Server misconfiguration — not an attack signal. Don't count.\n if (!trustedProxyConfig) {\n log.warn(\n { path: c.req.path, method: c.req.method, clientIp, reason: 'trusted_proxy_config_missing' },\n 'HTTP auth rejected: trusted-proxy config missing',\n );\n return c.json(\n { error: 'Unauthorized', code: 'auth_unconfigured', message: 'Trusted-proxy auth is not configured' },\n 401,\n );\n }\n\n const blocked = checkBlocked(rl);\n if (blocked.blocked) {\n log.warn(\n { clientIp, origin: origin ?? undefined, path: c.req.path, method: c.req.method, retryAfterSec: blocked.retryAfterSec, reason: 'auth_blocked' },\n 'Auth rate limit blocked',\n );\n return blockedResponse(c, blocked.retryAfterSec);\n }\n\n const result = authorizeTrustedProxy({\n remoteAddress: resolveRemoteAddress(c),\n getHeader: (name) => c.req.header(name),\n trustedProxies,\n trustedProxyConfig,\n });\n\n if (result.ok === false) {\n recordFailure(rl);\n log.warn(\n { path: c.req.path, method: c.req.method, clientIp, reason: result.reason },\n `HTTP auth rejected: trusted-proxy validation failed (${result.reason})`,\n );\n return c.json(\n { error: 'Unauthorized', code: 'invalid_proxy_credentials', message: 'Trusted-proxy authentication failed' },\n 401,\n );\n }\n\n recordSuccess(rl);\n await next();\n return;\n }\n\n if (authMode === 'none' || !token) {\n return next();\n }\n\n const proxyContext = getTrustedProxyContext?.();\n const clientIp = resolveMiddlewareClientIp(c, proxyContext?.trustedProxies, proxyContext?.allowRealIpFallback);\n const origin = c.req.header('origin');\n const rl = buildRateLimitContext(getGatewayAuth, clientIp, origin);\n\n const authHeader = extractTokenFromHeader(c.req.header('authorization'));\n const requestPath = new URL(c.req.url).pathname;\n const queryToken = isQueryTokenAllowedPath(requestPath, c.req.method)\n ? extractTokenFromQuery(c.req.url)\n : null;\n\n if (!authHeader && queryToken === null && new URL(c.req.url).searchParams.has('token')) {\n log.warn(\n { path: requestPath, method: c.req.method, clientIp },\n 'Token in query string rejected: use Authorization header for this endpoint',\n );\n }\n\n const providedToken = authHeader || queryToken;\n\n if (providedToken && validateToken(providedToken, token)) {\n recordSuccess(rl);\n await next();\n return;\n }\n\n const blocked = checkBlocked(rl);\n if (blocked.blocked) {\n log.warn(\n { clientIp, origin: origin ?? undefined, path: requestPath, method: c.req.method, retryAfterSec: blocked.retryAfterSec, reason: 'auth_blocked' },\n 'Auth rate limit blocked',\n );\n return blockedResponse(c, blocked.retryAfterSec);\n }\n\n // Missing token is an unauthenticated request, not a brute-force signal —\n // page reloads / SDK cold starts often hit endpoints before the token is\n // attached. Counting this would lock users out of the token-entry path.\n if (!providedToken) {\n log.warn(\n { path: c.req.path, method: c.req.method, clientIp, reason: 'missing_token', phase: 'gateway.http.auth' },\n 'HTTP auth rejected: no Bearer or ?token=',\n );\n void logAuthEvent('auth.failed', {\n ip: clientIp,\n result: 'denied',\n reason: 'missing_token',\n });\n return c.json(\n { error: 'Unauthorized', code: 'missing_token', message: 'Missing authentication token' },\n 401,\n );\n }\n\n recordFailure(rl);\n log.warn(\n { path: c.req.path, method: c.req.method, clientIp, reason: 'invalid_token', phase: 'gateway.http.auth' },\n 'HTTP auth rejected: token mismatch',\n );\n void logAuthEvent('auth.failed', {\n ip: clientIp,\n result: 'failure',\n reason: 'invalid_token',\n });\n return c.json(\n { error: 'Unauthorized', code: 'invalid_token', message: 'Invalid authentication token' },\n 401,\n );\n });\n}\n"],"mappings":"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"}
@@ -6,7 +6,7 @@ import { createMiddleware } from "hono/factory";
6
6
  import { getConnInfo } from "@hono/node-server/conninfo";
7
7
  //#region src/gateway/hono/middleware/logger.ts
8
8
  init_logger();
9
- const log = createLogger("Hono:Request");
9
+ const log = createLogger("Gateway:HTTP");
10
10
  function resolveRemoteAddress(c) {
11
11
  try {
12
12
  return getConnInfo(c).remote.address;
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","names":[],"sources":["../../../../../src/gateway/hono/middleware/logger.ts"],"sourcesContent":["import { createMiddleware } from 'hono/factory';\nimport type { Context } from 'hono';\nimport { getConnInfo } from '@hono/node-server/conninfo';\n\nimport { getClientIpFromHeaders } from '../../security/loopback.js';\nimport { resolveClientIpFromRequest } from '../../client-ip.js';\nimport { createLogger } from '../../../utils/logger.js';\n\nconst log = createLogger('Hono:Request');\n\nexport interface LoggerMiddlewareConfig {\n trustedProxies?: string[];\n allowRealIpFallback?: boolean;\n}\n\nfunction resolveRemoteAddress(c: Context): string | undefined {\n try {\n return getConnInfo(c).remote.address;\n } catch {\n return undefined;\n }\n}\n\nfunction resolveRequestClientIp(c: Context, config?: LoggerMiddlewareConfig): string {\n const trustedProxies = config?.trustedProxies;\n if (trustedProxies?.length) {\n return resolveClientIpFromRequest({\n remoteAddress: resolveRemoteAddress(c),\n getHeader: (name) => c.req.header(name),\n trustedProxies,\n allowRealIpFallback: config?.allowRealIpFallback,\n });\n }\n return getClientIpFromHeaders({\n get: (name) => c.req.header(name) ?? undefined,\n });\n}\n\nexport function logger(config?: LoggerMiddlewareConfig) {\n return createMiddleware(async (c, next) => {\n const start = Date.now();\n\n const clientIp = resolveRequestClientIp(c, config);\n const userAgent = c.req.header('user-agent') ?? undefined;\n const contentLength = c.req.header('content-length');\n const referer = c.req.header('referer') ?? undefined;\n\n await next();\n\n const duration = Date.now() - start;\n const status = c.res.status;\n const isServerError = status >= 500;\n const isClientError = status >= 400 && status < 500;\n const isSlow = duration > 1000;\n\n const logData = {\n method: c.req.method,\n path: c.req.path,\n status,\n durationMs: duration,\n clientIp,\n ...(userAgent ? { userAgent } : {}),\n ...(contentLength ? { contentLength: Number(contentLength) } : {}),\n ...(referer ? { referer } : {}),\n };\n\n const msg = `HTTP ${c.req.method} ${c.req.path} → ${status} (${duration}ms)`;\n\n if (isServerError || isSlow) {\n log.warn(logData, msg);\n } else if (isClientError) {\n // 4xx: info avoids doubling warn noise from auth / rate-limit handlers\n log.info(logData, msg);\n } else {\n log.debug(logData, msg);\n }\n });\n}\n"],"mappings":";;;;;;;aAMwD;AAExD,MAAM,MAAM,aAAa,eAAe;AAOxC,SAAS,qBAAqB,GAAgC;AAC5D,KAAI;AACF,SAAO,YAAY,EAAE,CAAC,OAAO;SACvB;AACN;;;AAIJ,SAAS,uBAAuB,GAAY,QAAyC;CACnF,MAAM,iBAAiB,QAAQ;AAC/B,KAAI,gBAAgB,OAClB,QAAO,2BAA2B;EAChC,eAAe,qBAAqB,EAAE;EACtC,YAAY,SAAS,EAAE,IAAI,OAAO,KAAK;EACvC;EACA,qBAAqB,QAAQ;EAC9B,CAAC;AAEJ,QAAO,uBAAuB,EAC5B,MAAM,SAAS,EAAE,IAAI,OAAO,KAAK,IAAI,KAAA,GACtC,CAAC;;AAGJ,SAAgB,OAAO,QAAiC;AACtD,QAAO,iBAAiB,OAAO,GAAG,SAAS;EACzC,MAAM,QAAQ,KAAK,KAAK;EAExB,MAAM,WAAW,uBAAuB,GAAG,OAAO;EAClD,MAAM,YAAY,EAAE,IAAI,OAAO,aAAa,IAAI,KAAA;EAChD,MAAM,gBAAgB,EAAE,IAAI,OAAO,iBAAiB;EACpD,MAAM,UAAU,EAAE,IAAI,OAAO,UAAU,IAAI,KAAA;AAE3C,QAAM,MAAM;EAEZ,MAAM,WAAW,KAAK,KAAK,GAAG;EAC9B,MAAM,SAAS,EAAE,IAAI;EACrB,MAAM,gBAAgB,UAAU;EAChC,MAAM,gBAAgB,UAAU,OAAO,SAAS;EAChD,MAAM,SAAS,WAAW;EAE1B,MAAM,UAAU;GACd,QAAQ,EAAE,IAAI;GACd,MAAM,EAAE,IAAI;GACZ;GACA,YAAY;GACZ;GACA,GAAI,YAAY,EAAE,WAAW,GAAG,EAAE;GAClC,GAAI,gBAAgB,EAAE,eAAe,OAAO,cAAc,EAAE,GAAG,EAAE;GACjE,GAAI,UAAU,EAAE,SAAS,GAAG,EAAE;GAC/B;EAED,MAAM,MAAM,QAAQ,EAAE,IAAI,OAAO,GAAG,EAAE,IAAI,KAAK,KAAK,OAAO,IAAI,SAAS;AAExE,MAAI,iBAAiB,OACnB,KAAI,KAAK,SAAS,IAAI;WACb,cAET,KAAI,KAAK,SAAS,IAAI;MAEtB,KAAI,MAAM,SAAS,IAAI;GAEzB"}
1
+ {"version":3,"file":"logger.js","names":[],"sources":["../../../../../src/gateway/hono/middleware/logger.ts"],"sourcesContent":["import { createMiddleware } from 'hono/factory';\nimport type { Context } from 'hono';\nimport { getConnInfo } from '@hono/node-server/conninfo';\n\nimport { getClientIpFromHeaders } from '../../security/loopback.js';\nimport { resolveClientIpFromRequest } from '../../client-ip.js';\nimport { createLogger } from '../../../utils/logger.js';\n\nconst log = createLogger('Gateway:HTTP');\n\nexport interface LoggerMiddlewareConfig {\n trustedProxies?: string[];\n allowRealIpFallback?: boolean;\n}\n\nfunction resolveRemoteAddress(c: Context): string | undefined {\n try {\n return getConnInfo(c).remote.address;\n } catch {\n return undefined;\n }\n}\n\nfunction resolveRequestClientIp(c: Context, config?: LoggerMiddlewareConfig): string {\n const trustedProxies = config?.trustedProxies;\n if (trustedProxies?.length) {\n return resolveClientIpFromRequest({\n remoteAddress: resolveRemoteAddress(c),\n getHeader: (name) => c.req.header(name),\n trustedProxies,\n allowRealIpFallback: config?.allowRealIpFallback,\n });\n }\n return getClientIpFromHeaders({\n get: (name) => c.req.header(name) ?? undefined,\n });\n}\n\nexport function logger(config?: LoggerMiddlewareConfig) {\n return createMiddleware(async (c, next) => {\n const start = Date.now();\n\n const clientIp = resolveRequestClientIp(c, config);\n const userAgent = c.req.header('user-agent') ?? undefined;\n const contentLength = c.req.header('content-length');\n const referer = c.req.header('referer') ?? undefined;\n\n await next();\n\n const duration = Date.now() - start;\n const status = c.res.status;\n const isServerError = status >= 500;\n const isClientError = status >= 400 && status < 500;\n const isSlow = duration > 1000;\n\n const logData = {\n method: c.req.method,\n path: c.req.path,\n status,\n durationMs: duration,\n clientIp,\n ...(userAgent ? { userAgent } : {}),\n ...(contentLength ? { contentLength: Number(contentLength) } : {}),\n ...(referer ? { referer } : {}),\n };\n\n const msg = `HTTP ${c.req.method} ${c.req.path} → ${status} (${duration}ms)`;\n\n if (isServerError || isSlow) {\n log.warn(logData, msg);\n } else if (isClientError) {\n // 4xx: info avoids doubling warn noise from auth / rate-limit handlers\n log.info(logData, msg);\n } else {\n log.debug(logData, msg);\n }\n });\n}\n"],"mappings":";;;;;;;aAMwD;AAExD,MAAM,MAAM,aAAa,eAAe;AAOxC,SAAS,qBAAqB,GAAgC;AAC5D,KAAI;AACF,SAAO,YAAY,EAAE,CAAC,OAAO;SACvB;AACN;;;AAIJ,SAAS,uBAAuB,GAAY,QAAyC;CACnF,MAAM,iBAAiB,QAAQ;AAC/B,KAAI,gBAAgB,OAClB,QAAO,2BAA2B;EAChC,eAAe,qBAAqB,EAAE;EACtC,YAAY,SAAS,EAAE,IAAI,OAAO,KAAK;EACvC;EACA,qBAAqB,QAAQ;EAC9B,CAAC;AAEJ,QAAO,uBAAuB,EAC5B,MAAM,SAAS,EAAE,IAAI,OAAO,KAAK,IAAI,KAAA,GACtC,CAAC;;AAGJ,SAAgB,OAAO,QAAiC;AACtD,QAAO,iBAAiB,OAAO,GAAG,SAAS;EACzC,MAAM,QAAQ,KAAK,KAAK;EAExB,MAAM,WAAW,uBAAuB,GAAG,OAAO;EAClD,MAAM,YAAY,EAAE,IAAI,OAAO,aAAa,IAAI,KAAA;EAChD,MAAM,gBAAgB,EAAE,IAAI,OAAO,iBAAiB;EACpD,MAAM,UAAU,EAAE,IAAI,OAAO,UAAU,IAAI,KAAA;AAE3C,QAAM,MAAM;EAEZ,MAAM,WAAW,KAAK,KAAK,GAAG;EAC9B,MAAM,SAAS,EAAE,IAAI;EACrB,MAAM,gBAAgB,UAAU;EAChC,MAAM,gBAAgB,UAAU,OAAO,SAAS;EAChD,MAAM,SAAS,WAAW;EAE1B,MAAM,UAAU;GACd,QAAQ,EAAE,IAAI;GACd,MAAM,EAAE,IAAI;GACZ;GACA,YAAY;GACZ;GACA,GAAI,YAAY,EAAE,WAAW,GAAG,EAAE;GAClC,GAAI,gBAAgB,EAAE,eAAe,OAAO,cAAc,EAAE,GAAG,EAAE;GACjE,GAAI,UAAU,EAAE,SAAS,GAAG,EAAE;GAC/B;EAED,MAAM,MAAM,QAAQ,EAAE,IAAI,OAAO,GAAG,EAAE,IAAI,KAAK,KAAK,OAAO,IAAI,SAAS;AAExE,MAAI,iBAAiB,OACnB,KAAI,KAAK,SAAS,IAAI;WACb,cAET,KAAI,KAAK,SAAS,IAAI;MAEtB,KAAI,MAAM,SAAS,IAAI;GAEzB"}
@@ -0,0 +1,5 @@
1
+ import type { Context } from 'hono';
2
+ export declare function routeErrorMiddleware(): import("hono/dist/types/types.js").MiddlewareHandler<any, string, {}, Response>;
3
+ export declare function registerGatewayOnError(app: {
4
+ onError: (handler: (err: Error, c: Context) => Response | Promise<Response>) => void;
5
+ }): void;
@@ -0,0 +1,27 @@
1
+ import { createGatewayRouteLogger, logRouteError } from "../lib/route-logger.js";
2
+ import { createMiddleware } from "hono/factory";
3
+ //#region src/gateway/hono/middleware/route-errors.ts
4
+ const log = createGatewayRouteLogger("Routes");
5
+ function routeErrorMiddleware() {
6
+ return createMiddleware(async (c, next) => {
7
+ try {
8
+ await next();
9
+ } catch (err) {
10
+ logRouteError(log, c, err, "gateway.http.route");
11
+ throw err;
12
+ }
13
+ });
14
+ }
15
+ function registerGatewayOnError(app) {
16
+ app.onError((err, c) => {
17
+ logRouteError(log, c, err, "gateway.http.unhandled");
18
+ return c.json({
19
+ error: "Internal Server Error",
20
+ message: err instanceof Error ? err.message : "Unknown error"
21
+ }, 500);
22
+ });
23
+ }
24
+ //#endregion
25
+ export { registerGatewayOnError, routeErrorMiddleware };
26
+
27
+ //# sourceMappingURL=route-errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-errors.js","names":[],"sources":["../../../../../src/gateway/hono/middleware/route-errors.ts"],"sourcesContent":["import { createMiddleware } from 'hono/factory';\nimport type { Context } from 'hono';\n\nimport { createGatewayRouteLogger, logRouteError } from '../lib/route-logger.js';\n\nconst log = createGatewayRouteLogger('Routes');\n\nexport function routeErrorMiddleware() {\n return createMiddleware(async (c, next) => {\n try {\n await next();\n } catch (err) {\n logRouteError(log, c, err, 'gateway.http.route');\n throw err;\n }\n });\n}\n\nexport function registerGatewayOnError(app: { onError: (handler: (err: Error, c: Context) => Response | Promise<Response>) => void }) {\n app.onError((err, c) => {\n logRouteError(log, c, err, 'gateway.http.unhandled');\n return c.json(\n {\n error: 'Internal Server Error',\n message: err instanceof Error ? err.message : 'Unknown error',\n },\n 500,\n );\n });\n}\n"],"mappings":";;;AAKA,MAAM,MAAM,yBAAyB,SAAS;AAE9C,SAAgB,uBAAuB;AACrC,QAAO,iBAAiB,OAAO,GAAG,SAAS;AACzC,MAAI;AACF,SAAM,MAAM;WACL,KAAK;AACZ,iBAAc,KAAK,GAAG,KAAK,qBAAqB;AAChD,SAAM;;GAER;;AAGJ,SAAgB,uBAAuB,KAA+F;AACpI,KAAI,SAAS,KAAK,MAAM;AACtB,gBAAc,KAAK,GAAG,KAAK,yBAAyB;AACpD,SAAO,EAAE,KACP;GACE,OAAO;GACP,SAAS,eAAe,QAAQ,IAAI,UAAU;GAC/C,EACD,IACD;GACD"}
@@ -1,6 +1,6 @@
1
1
  import { CredentialResolver, init_credentials } from "../../auth/credentials.js";
2
- import { anthropicOAuthProvider } from "../../auth/oauth/anthropic.js";
3
2
  import { getProviderAuthState, init_providers, isProviderConfigured } from "../../providers/index.js";
3
+ import { anthropicOAuthProvider } from "../../auth/oauth/anthropic.js";
4
4
  import { minimaxOAuthProvider } from "../../auth/oauth/minimax.js";
5
5
  import { minimaxCnOAuthProvider } from "../../auth/oauth/minimax-cn.js";
6
6
  import { kimiCodingOAuthProvider } from "../../auth/oauth/kimi-coding.js";
@@ -1,5 +1,7 @@
1
+ import { createGatewayRouteLogger, logRouteWarn } from "../lib/route-logger.js";
1
2
  import { createAgentResumeHandler, createAgentSSEHandler, createEventsSSEHandler, createSendHandler } from "../sse.js";
2
3
  //#region src/gateway/hono/routes/agent-stream.ts
4
+ const log = createGatewayRouteLogger("AgentStream");
3
5
  function registerAgentStreamRoutes(authenticated, deps) {
4
6
  const { service, strictRateLimitMiddleware, sseConfig } = deps;
5
7
  authenticated.post("/api/agent", strictRateLimitMiddleware, createAgentSSEHandler(sseConfig));
@@ -40,6 +42,10 @@ function registerAgentStreamRoutes(authenticated, deps) {
40
42
  }, 400);
41
43
  const result = await service.steerWebchatAgent(chatId, message);
42
44
  if (result.ok === false) {
45
+ logRouteWarn(log, c, `Agent steer failed: ${result.code}`, "gateway.route.agent", {
46
+ chatId,
47
+ code: result.code
48
+ });
43
49
  const code = result.code;
44
50
  const status = code === "BAD_REQUEST" ? 400 : code === "NO_ACTIVE_RUN" ? 409 : 500;
45
51
  const msg = code === "NO_ACTIVE_RUN" ? "No active agent run for this chat" : code === "STEER_FAILED" ? "Steer failed" : "Message required";
@@ -1 +1 @@
1
- {"version":3,"file":"agent-stream.js","names":[],"sources":["../../../../../src/gateway/hono/routes/agent-stream.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport {\n createAgentResumeHandler,\n createAgentSSEHandler,\n createEventsSSEHandler,\n createSendHandler,\n} from '../sse.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nexport function registerAgentStreamRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service, strictRateLimitMiddleware, sseConfig } = deps;\n\n authenticated.post('/api/agent', strictRateLimitMiddleware, createAgentSSEHandler(sseConfig));\n\n authenticated.post('/api/agent/resume', strictRateLimitMiddleware, createAgentResumeHandler(sseConfig));\n\n authenticated.post('/api/agent/abort', strictRateLimitMiddleware, async (c) => {\n const body = await c.req.json().catch(() => null);\n const runId =\n body && typeof body === 'object' && typeof (body as { runId?: unknown }).runId === 'string'\n ? (body as { runId: string }).runId.trim()\n : '';\n if (!runId) {\n return c.json(\n { ok: false, error: { code: 'BAD_REQUEST', message: 'Missing runId' } },\n 400,\n );\n }\n const aborted = service.abortAgentRun(runId);\n return c.json({ ok: true, payload: { aborted } });\n });\n\n authenticated.post('/api/agent/steer', strictRateLimitMiddleware, async (c) => {\n const body = await c.req.json().catch(() => null);\n if (!body || typeof body !== 'object') {\n return c.json(\n { ok: false, error: { code: 'BAD_REQUEST', message: 'Invalid JSON body' } },\n 400,\n );\n }\n const chatId =\n typeof (body as { chatId?: unknown }).chatId === 'string'\n ? (body as { chatId: string }).chatId.trim()\n : '';\n const message =\n typeof (body as { message?: unknown }).message === 'string'\n ? (body as { message: string }).message\n : '';\n if (!chatId) {\n return c.json(\n { ok: false, error: { code: 'BAD_REQUEST', message: 'Missing chatId' } },\n 400,\n );\n }\n const result = await service.steerWebchatAgent(chatId, message);\n if (result.ok === false) {\n const code = result.code;\n const status = code === 'BAD_REQUEST' ? 400 : code === 'NO_ACTIVE_RUN' ? 409 : 500;\n const msg =\n code === 'NO_ACTIVE_RUN'\n ? 'No active agent run for this chat'\n : code === 'STEER_FAILED'\n ? 'Steer failed'\n : 'Message required';\n return c.json({ ok: false, error: { code, message: msg } }, status);\n }\n return c.json({ ok: true, payload: { steered: true } });\n });\n\n authenticated.post('/api/clarify/:requestId', strictRateLimitMiddleware, async (c) => {\n const requestId = c.req.param('requestId')?.trim() ?? '';\n if (!requestId) {\n return c.json(\n { ok: false, error: { code: 'BAD_REQUEST', message: 'Missing requestId' } },\n 400,\n );\n }\n const body = await c.req.json().catch(() => null);\n const skip =\n body &&\n typeof body === 'object' &&\n (body as { skip?: unknown }).skip === true;\n const rawAnswer =\n body && typeof body === 'object' && typeof (body as { answer?: unknown }).answer === 'string'\n ? (body as { answer: string }).answer\n : '';\n const answer = typeof rawAnswer === 'string' ? rawAnswer.trim() : '';\n if (!skip && !answer) {\n return c.json(\n { ok: false, error: { code: 'BAD_REQUEST', message: 'Missing answer field' } },\n 400,\n );\n }\n const handled = service.submitClarifyResponse(requestId, skip ? '' : answer);\n if (!handled) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: 'No pending clarification with this ID' } },\n 404,\n );\n }\n return c.json({ ok: true, payload: { received: true } });\n });\n\n authenticated.post('/api/send', strictRateLimitMiddleware, createSendHandler(sseConfig));\n\n authenticated.get('/api/events', createEventsSSEHandler(sseConfig));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"agent-stream.js","names":[],"sources":["../../../../../src/gateway/hono/routes/agent-stream.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport {\n createAgentResumeHandler,\n createAgentSSEHandler,\n createEventsSSEHandler,\n createSendHandler,\n} from '../sse.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\nimport { createGatewayRouteLogger, logRouteWarn } from '../lib/route-logger.js';\n\nconst log = createGatewayRouteLogger('AgentStream');\n\nexport function registerAgentStreamRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service, strictRateLimitMiddleware, sseConfig } = deps;\n\n authenticated.post('/api/agent', strictRateLimitMiddleware, createAgentSSEHandler(sseConfig));\n\n authenticated.post('/api/agent/resume', strictRateLimitMiddleware, createAgentResumeHandler(sseConfig));\n\n authenticated.post('/api/agent/abort', strictRateLimitMiddleware, async (c) => {\n const body = await c.req.json().catch(() => null);\n const runId =\n body && typeof body === 'object' && typeof (body as { runId?: unknown }).runId === 'string'\n ? (body as { runId: string }).runId.trim()\n : '';\n if (!runId) {\n return c.json(\n { ok: false, error: { code: 'BAD_REQUEST', message: 'Missing runId' } },\n 400,\n );\n }\n const aborted = service.abortAgentRun(runId);\n return c.json({ ok: true, payload: { aborted } });\n });\n\n authenticated.post('/api/agent/steer', strictRateLimitMiddleware, async (c) => {\n const body = await c.req.json().catch(() => null);\n if (!body || typeof body !== 'object') {\n return c.json(\n { ok: false, error: { code: 'BAD_REQUEST', message: 'Invalid JSON body' } },\n 400,\n );\n }\n const chatId =\n typeof (body as { chatId?: unknown }).chatId === 'string'\n ? (body as { chatId: string }).chatId.trim()\n : '';\n const message =\n typeof (body as { message?: unknown }).message === 'string'\n ? (body as { message: string }).message\n : '';\n if (!chatId) {\n return c.json(\n { ok: false, error: { code: 'BAD_REQUEST', message: 'Missing chatId' } },\n 400,\n );\n }\n const result = await service.steerWebchatAgent(chatId, message);\n if (result.ok === false) {\n logRouteWarn(log, c, `Agent steer failed: ${result.code}`, 'gateway.route.agent', { chatId, code: result.code });\n const code = result.code;\n const status = code === 'BAD_REQUEST' ? 400 : code === 'NO_ACTIVE_RUN' ? 409 : 500;\n const msg =\n code === 'NO_ACTIVE_RUN'\n ? 'No active agent run for this chat'\n : code === 'STEER_FAILED'\n ? 'Steer failed'\n : 'Message required';\n return c.json({ ok: false, error: { code, message: msg } }, status);\n }\n return c.json({ ok: true, payload: { steered: true } });\n });\n\n authenticated.post('/api/clarify/:requestId', strictRateLimitMiddleware, async (c) => {\n const requestId = c.req.param('requestId')?.trim() ?? '';\n if (!requestId) {\n return c.json(\n { ok: false, error: { code: 'BAD_REQUEST', message: 'Missing requestId' } },\n 400,\n );\n }\n const body = await c.req.json().catch(() => null);\n const skip =\n body &&\n typeof body === 'object' &&\n (body as { skip?: unknown }).skip === true;\n const rawAnswer =\n body && typeof body === 'object' && typeof (body as { answer?: unknown }).answer === 'string'\n ? (body as { answer: string }).answer\n : '';\n const answer = typeof rawAnswer === 'string' ? rawAnswer.trim() : '';\n if (!skip && !answer) {\n return c.json(\n { ok: false, error: { code: 'BAD_REQUEST', message: 'Missing answer field' } },\n 400,\n );\n }\n const handled = service.submitClarifyResponse(requestId, skip ? '' : answer);\n if (!handled) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: 'No pending clarification with this ID' } },\n 404,\n );\n }\n return c.json({ ok: true, payload: { received: true } });\n });\n\n authenticated.post('/api/send', strictRateLimitMiddleware, createSendHandler(sseConfig));\n\n authenticated.get('/api/events', createEventsSSEHandler(sseConfig));\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import { init_agent_scope, normalizeAgentId } from "../../../agent/agent-scope.js";
2
1
  import { init_localized_text, normalizeLocalizedText } from "../../../config/localized-text.js";
3
2
  import { init_schema, parseModelRef } from "../../../config/schema.js";
3
+ import { init_agent_scope, normalizeAgentId } from "../../../agent/agent-scope.js";
4
4
  import { init_providers, isProviderConfigured, resolveModel } from "../../../providers/index.js";
5
5
  import { getVoiceModelsConfig } from "../../../config/voice.js";
6
6
  import { deleteAgentAvatarFile, finalizeCreateAgentDirs, listAgentProfileFiles, listGatewayAgents, prepareCreateAgent, prepareCreateAgentsBatch, prepareDeleteAgent, prepareUpdateAgent, readAgentAvatarFile, readAgentProfileFile, runAfterDeletePurge, writeAgentAvatarFromBase64, writeAgentProfileFile } from "../../agents-admin.js";
@@ -7,8 +7,8 @@ import { createOAuthHandler } from "../oauth.js";
7
7
  import { createOAuthAsyncHandler } from "../oauth-async.js";
8
8
  import { extensionAssetMimeType } from "../lib/extension-assets.js";
9
9
  import { loadExtensionStore, saveExtensionStore } from "../lib/extension-store.js";
10
- import { existsSync, readFileSync, statSync } from "node:fs";
11
10
  import { relative, resolve } from "node:path";
11
+ import { existsSync, readFileSync, statSync } from "node:fs";
12
12
  //#region src/gateway/hono/routes/auth-registry-extensions.ts
13
13
  init_providers();
14
14
  const EXTENSION_ASSET_CSP = "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; connect-src 'none'; frame-ancestors 'self'; frame-src 'none'; base-uri 'none'; object-src 'none'; form-action 'none'";
@@ -1,11 +1,9 @@
1
- import { createLogger } from "../../../utils/logger/index.js";
2
- import { init_logger } from "../../../utils/logger.js";
3
1
  import { acquireBrowserInstallLock, cancelBrowserInstall } from "../../../browser/install-lock.js";
2
+ import { createGatewayRouteLogger } from "../lib/route-logger.js";
4
3
  import { runPlaywrightChromiumInstallWithProgress } from "../../../browser/providers/playwright-install.js";
5
4
  import { streamSSE } from "hono/streaming";
6
5
  //#region src/gateway/hono/routes/browser-install.ts
7
- init_logger();
8
- const log = createLogger("GatewayBrowserInstall");
6
+ const log = createGatewayRouteLogger("BrowserInstall");
9
7
  function parseCloakInstallBody(body) {
10
8
  const input = body && typeof body === "object" && !Array.isArray(body) ? body : {};
11
9
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"browser-install.js","names":[],"sources":["../../../../../src/gateway/hono/routes/browser-install.ts"],"sourcesContent":["import type { Hono } from 'hono';\nimport { streamSSE } from 'hono/streaming';\n\nimport {\n acquireBrowserInstallLock,\n cancelBrowserInstall,\n type BrowserInstallKind,\n} from '../../../browser/install-lock.js';\nimport type { BrowserInstallProgress } from '../../../browser/install-progress.js';\nimport { runPlaywrightChromiumInstallWithProgress } from '../../../browser/providers/playwright-install.js';\nimport { createLogger } from '../../../utils/logger.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nconst log = createLogger('GatewayBrowserInstall');\n\nfunction parseCloakInstallBody(body: unknown): { cacheDir?: string; binaryPath?: string } {\n const input = body && typeof body === 'object' && !Array.isArray(body)\n ? (body as Record<string, unknown>)\n : {};\n const cacheDir = typeof input.cacheDir === 'string' && input.cacheDir.trim()\n ? input.cacheDir.trim()\n : undefined;\n const binaryPath = typeof input.binaryPath === 'string' && input.binaryPath.trim()\n ? input.binaryPath.trim()\n : undefined;\n return { cacheDir, binaryPath };\n}\n\nfunction isInstallCancelled(err: unknown, signal?: AbortSignal): boolean {\n if (signal?.aborted) return true;\n return err instanceof Error && err.message === 'Install cancelled';\n}\n\nasync function writeInstallProgress(\n stream: { writeSSE: (payload: { event: string; data: string }) => Promise<void> },\n progress: BrowserInstallProgress,\n): Promise<void> {\n try {\n await stream.writeSSE({\n event: 'progress',\n data: JSON.stringify(progress),\n });\n } catch {\n // Client navigated away — install continues unless user hit cancel.\n }\n}\n\nfunction createInstallProgressEmitter(\n stream: { writeSSE: (payload: { event: string; data: string }) => Promise<void> },\n): (progress: BrowserInstallProgress) => Promise<void> {\n let chain = Promise.resolve();\n return (progress) => {\n chain = chain.then(() => writeInstallProgress(stream, progress));\n return chain;\n };\n}\n\nasync function runBrowserInstallStream(\n kind: BrowserInstallKind,\n stream: { writeSSE: (payload: { event: string; data: string }) => Promise<void> },\n run: (\n signal: AbortSignal,\n emitProgress: (progress: BrowserInstallProgress) => Promise<void>,\n ) => Promise<unknown>,\n): Promise<void> {\n const lock = acquireBrowserInstallLock(kind);\n if (!lock) {\n await stream.writeSSE({\n event: 'result',\n data: JSON.stringify({\n ok: false,\n error: 'busy',\n message: 'An install for this browser type is already in progress.',\n }),\n });\n return;\n }\n\n try {\n const emitProgress = createInstallProgressEmitter(stream);\n const payload = await run(lock.signal, emitProgress);\n await emitProgress({ phase: 'ready', message: 'Install complete', percent: 100 });\n try {\n await stream.writeSSE({\n event: 'result',\n data: JSON.stringify({ ok: true, payload }),\n });\n } catch {\n log.info({ kind }, 'Gateway: browser install finished after client disconnected');\n }\n } catch (err) {\n if (isInstallCancelled(err, lock.signal)) {\n log.info({ kind }, 'Gateway: browser install cancelled by user');\n try {\n await stream.writeSSE({\n event: 'result',\n data: JSON.stringify({\n ok: false,\n error: 'cancelled',\n message: 'Install cancelled',\n }),\n });\n } catch {\n /* client gone */\n }\n return;\n }\n const message = err instanceof Error ? err.message : String(err);\n log.warn({ kind, errorMessage: message }, 'Gateway: streamed browser install failed');\n try {\n await stream.writeSSE({\n event: 'result',\n data: JSON.stringify({ ok: false, error: 'install-failed', message }),\n });\n } catch {\n /* client gone */\n }\n } finally {\n lock.release();\n }\n}\n\nfunction registerInstallCancelRoute(\n authenticated: Hono,\n deps: AuthenticatedRouteDeps,\n kind: BrowserInstallKind,\n path: string,\n): void {\n const { strictRateLimitMiddleware } = deps;\n authenticated.post(path, strictRateLimitMiddleware, (c) => {\n const cancelled = cancelBrowserInstall(kind);\n return c.json({ ok: true, payload: { cancelled } });\n });\n}\n\nexport function registerBrowserInstallRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { strictRateLimitMiddleware } = deps;\n\n registerInstallCancelRoute(\n authenticated,\n deps,\n 'playwright',\n '/api/browser/playwright/install/cancel',\n );\n registerInstallCancelRoute(\n authenticated,\n deps,\n 'cloakbrowser',\n '/api/browser/cloakbrowser/install/cancel',\n );\n\n authenticated.post('/api/browser/playwright/install/stream', strictRateLimitMiddleware, async (c) => {\n return streamSSE(c, async (stream) => {\n await runBrowserInstallStream('playwright', stream, async (signal, emitProgress) => {\n log.info('Gateway: starting streamed Playwright Chromium install');\n await runPlaywrightChromiumInstallWithProgress({\n signal,\n onProgress: emitProgress,\n });\n const { playwrightChromiumDoctor } = await import('../../../browser/providers/playwright-doctor.js');\n const payload = await playwrightChromiumDoctor();\n if (!payload.installed) {\n throw new Error(payload.reason ?? 'Chromium not found after install');\n }\n return payload;\n });\n });\n });\n\n authenticated.post('/api/browser/cloakbrowser/install/stream', strictRateLimitMiddleware, async (c) => {\n let body: unknown;\n try {\n body = await c.req.json();\n } catch {\n body = {};\n }\n const { cacheDir, binaryPath } = parseCloakInstallBody(body);\n\n return streamSSE(c, async (stream) => {\n await runBrowserInstallStream('cloakbrowser', stream, async (signal, emitProgress) => {\n log.info(\n { cacheDir, binaryPath: binaryPath ? '(custom)' : undefined },\n 'Gateway: starting streamed CloakBrowser install',\n );\n const { installCloakBrowser } = await import('../../../browser/providers/cloakbrowser.js');\n return installCloakBrowser({\n cacheDir,\n binaryPath,\n signal,\n onProgress: emitProgress,\n });\n });\n });\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"browser-install.js","names":[],"sources":["../../../../../src/gateway/hono/routes/browser-install.ts"],"sourcesContent":["import type { Hono } from 'hono';\nimport { streamSSE } from 'hono/streaming';\n\nimport {\n acquireBrowserInstallLock,\n cancelBrowserInstall,\n type BrowserInstallKind,\n} from '../../../browser/install-lock.js';\nimport type { BrowserInstallProgress } from '../../../browser/install-progress.js';\nimport { runPlaywrightChromiumInstallWithProgress } from '../../../browser/providers/playwright-install.js';\nimport { createGatewayRouteLogger } from '../lib/route-logger.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nconst log = createGatewayRouteLogger('BrowserInstall');\n\nfunction parseCloakInstallBody(body: unknown): { cacheDir?: string; binaryPath?: string } {\n const input = body && typeof body === 'object' && !Array.isArray(body)\n ? (body as Record<string, unknown>)\n : {};\n const cacheDir = typeof input.cacheDir === 'string' && input.cacheDir.trim()\n ? input.cacheDir.trim()\n : undefined;\n const binaryPath = typeof input.binaryPath === 'string' && input.binaryPath.trim()\n ? input.binaryPath.trim()\n : undefined;\n return { cacheDir, binaryPath };\n}\n\nfunction isInstallCancelled(err: unknown, signal?: AbortSignal): boolean {\n if (signal?.aborted) return true;\n return err instanceof Error && err.message === 'Install cancelled';\n}\n\nasync function writeInstallProgress(\n stream: { writeSSE: (payload: { event: string; data: string }) => Promise<void> },\n progress: BrowserInstallProgress,\n): Promise<void> {\n try {\n await stream.writeSSE({\n event: 'progress',\n data: JSON.stringify(progress),\n });\n } catch {\n // Client navigated away — install continues unless user hit cancel.\n }\n}\n\nfunction createInstallProgressEmitter(\n stream: { writeSSE: (payload: { event: string; data: string }) => Promise<void> },\n): (progress: BrowserInstallProgress) => Promise<void> {\n let chain = Promise.resolve();\n return (progress) => {\n chain = chain.then(() => writeInstallProgress(stream, progress));\n return chain;\n };\n}\n\nasync function runBrowserInstallStream(\n kind: BrowserInstallKind,\n stream: { writeSSE: (payload: { event: string; data: string }) => Promise<void> },\n run: (\n signal: AbortSignal,\n emitProgress: (progress: BrowserInstallProgress) => Promise<void>,\n ) => Promise<unknown>,\n): Promise<void> {\n const lock = acquireBrowserInstallLock(kind);\n if (!lock) {\n await stream.writeSSE({\n event: 'result',\n data: JSON.stringify({\n ok: false,\n error: 'busy',\n message: 'An install for this browser type is already in progress.',\n }),\n });\n return;\n }\n\n try {\n const emitProgress = createInstallProgressEmitter(stream);\n const payload = await run(lock.signal, emitProgress);\n await emitProgress({ phase: 'ready', message: 'Install complete', percent: 100 });\n try {\n await stream.writeSSE({\n event: 'result',\n data: JSON.stringify({ ok: true, payload }),\n });\n } catch {\n log.info({ kind }, 'Gateway: browser install finished after client disconnected');\n }\n } catch (err) {\n if (isInstallCancelled(err, lock.signal)) {\n log.info({ kind }, 'Gateway: browser install cancelled by user');\n try {\n await stream.writeSSE({\n event: 'result',\n data: JSON.stringify({\n ok: false,\n error: 'cancelled',\n message: 'Install cancelled',\n }),\n });\n } catch {\n /* client gone */\n }\n return;\n }\n const message = err instanceof Error ? err.message : String(err);\n log.warn({ kind, errorMessage: message }, 'Gateway: streamed browser install failed');\n try {\n await stream.writeSSE({\n event: 'result',\n data: JSON.stringify({ ok: false, error: 'install-failed', message }),\n });\n } catch {\n /* client gone */\n }\n } finally {\n lock.release();\n }\n}\n\nfunction registerInstallCancelRoute(\n authenticated: Hono,\n deps: AuthenticatedRouteDeps,\n kind: BrowserInstallKind,\n path: string,\n): void {\n const { strictRateLimitMiddleware } = deps;\n authenticated.post(path, strictRateLimitMiddleware, (c) => {\n const cancelled = cancelBrowserInstall(kind);\n return c.json({ ok: true, payload: { cancelled } });\n });\n}\n\nexport function registerBrowserInstallRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { strictRateLimitMiddleware } = deps;\n\n registerInstallCancelRoute(\n authenticated,\n deps,\n 'playwright',\n '/api/browser/playwright/install/cancel',\n );\n registerInstallCancelRoute(\n authenticated,\n deps,\n 'cloakbrowser',\n '/api/browser/cloakbrowser/install/cancel',\n );\n\n authenticated.post('/api/browser/playwright/install/stream', strictRateLimitMiddleware, async (c) => {\n return streamSSE(c, async (stream) => {\n await runBrowserInstallStream('playwright', stream, async (signal, emitProgress) => {\n log.info('Gateway: starting streamed Playwright Chromium install');\n await runPlaywrightChromiumInstallWithProgress({\n signal,\n onProgress: emitProgress,\n });\n const { playwrightChromiumDoctor } = await import('../../../browser/providers/playwright-doctor.js');\n const payload = await playwrightChromiumDoctor();\n if (!payload.installed) {\n throw new Error(payload.reason ?? 'Chromium not found after install');\n }\n return payload;\n });\n });\n });\n\n authenticated.post('/api/browser/cloakbrowser/install/stream', strictRateLimitMiddleware, async (c) => {\n let body: unknown;\n try {\n body = await c.req.json();\n } catch {\n body = {};\n }\n const { cacheDir, binaryPath } = parseCloakInstallBody(body);\n\n return streamSSE(c, async (stream) => {\n await runBrowserInstallStream('cloakbrowser', stream, async (signal, emitProgress) => {\n log.info(\n { cacheDir, binaryPath: binaryPath ? '(custom)' : undefined },\n 'Gateway: starting streamed CloakBrowser install',\n );\n const { installCloakBrowser } = await import('../../../browser/providers/cloakbrowser.js');\n return installCloakBrowser({\n cacheDir,\n binaryPath,\n signal,\n onProgress: emitProgress,\n });\n });\n });\n });\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { CredentialResolver, init_credentials } from "../../../../auth/credentials.js";
2
1
  import { BindingsConfigSchema, McpConfigSchema, init_schema } from "../../../../config/schema.js";
2
+ import { CredentialResolver, init_credentials } from "../../../../auth/credentials.js";
3
3
  import { canonicalizeConfiguredMcpServer } from "../../../../config/mcp-config-normalize.js";
4
4
  import { mergeCronConfigPatch, mergeGatewaySkillsMarketplacePatch, mergeGoalsConfigPatch, mergeSessionConfigPatch, mergeUpdateConfigPatch } from "../../../../config/web-patch.js";
5
5
  import { assertGatewayAuthConfigured, resolveGatewayAuth } from "../../../auth.js";
@@ -68,16 +68,9 @@ function registerConfigRoutes(authenticated, deps) {
68
68
  payload: { config: safeConfig }
69
69
  });
70
70
  });
71
- /** POST /api/gateway/reveal-auth-secret plaintext gateway.auth token/password from config only. */
72
- authenticated.post("/api/gateway/reveal-auth-secret", strictRateLimitMiddleware, async (c) => {
73
- let field;
74
- try {
75
- const body = await c.req.json();
76
- field = body && typeof body === "object" ? body.field : void 0;
77
- } catch {
78
- field = void 0;
79
- }
80
- if (field !== "token" && field !== "password") return c.json({
71
+ const revealGatewayAuthSecretHandler = async (c) => {
72
+ const field = await resolveRevealGatewayAuthField(c);
73
+ if (!field) return c.json({
81
74
  ok: false,
82
75
  error: { message: "field must be token or password" }
83
76
  }, 400);
@@ -91,7 +84,13 @@ function registerConfigRoutes(authenticated, deps) {
91
84
  source: secret ? "config" : "none"
92
85
  }
93
86
  });
94
- });
87
+ };
88
+ /**
89
+ * POST /api/gateway/reveal-auth-secret/:field — preferred; no JSON body required.
90
+ * POST /api/gateway/reveal-auth-secret — legacy JSON body `{ field: "token" | "password" }`.
91
+ */
92
+ authenticated.post("/api/gateway/reveal-auth-secret/:field", strictRateLimitMiddleware, revealGatewayAuthSecretHandler);
93
+ authenticated.post("/api/gateway/reveal-auth-secret", strictRateLimitMiddleware, revealGatewayAuthSecretHandler);
95
94
  /** POST /api/agents/browser/reveal-cloud-api-key — plaintext browser cloud apiKey from config only. */
96
95
  authenticated.post("/api/agents/browser/reveal-cloud-api-key", strictRateLimitMiddleware, async (c) => {
97
96
  const apiKey = service.currentConfig.agents?.defaults?.browser?.cloud?.apiKey?.trim() || null;
@@ -128,6 +127,21 @@ function registerConfigRoutes(authenticated, deps) {
128
127
  });
129
128
  });
130
129
  }
130
+ function normalizeRevealGatewayAuthField(raw) {
131
+ return raw === "token" || raw === "password" ? raw : null;
132
+ }
133
+ async function resolveRevealGatewayAuthField(c) {
134
+ const fromPath = normalizeRevealGatewayAuthField(c.req.param("field"));
135
+ if (fromPath) return fromPath;
136
+ const fromQuery = normalizeRevealGatewayAuthField(c.req.query("field"));
137
+ if (fromQuery) return fromQuery;
138
+ try {
139
+ const body = await c.req.json();
140
+ return normalizeRevealGatewayAuthField(body && typeof body === "object" && !Array.isArray(body) ? body.field : void 0);
141
+ } catch {
142
+ return null;
143
+ }
144
+ }
131
145
  //#endregion
132
146
  export { registerConfigRoutes };
133
147
 
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","names":[],"sources":["../../../../../src/gateway/hono/routes/config.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport type { Config } from '../../../config/schema.js';\nimport { buildSafeWebConfigPayload } from '../lib/config-payload.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\nimport {\n applyAgentsPatch,\n applyChannelsPatch,\n applyGatewayPatch,\n applyMiscPatch,\n validateGatewayAfterPatch,\n} from './config-patch/index.js';\n\nexport function registerConfigRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service, strictRateLimitMiddleware } = deps;\n\n authenticated.post('/api/config/reload', strictRateLimitMiddleware, async (c) => {\n const result = await service.reloadConfig();\n return c.json({ ok: true, payload: result });\n });\n\n authenticated.post('/api/heartbeat/trigger', strictRateLimitMiddleware, async (c) => {\n let reason = 'manual';\n try {\n const body = await c.req.json();\n if (body && typeof body === 'object' && typeof (body as { reason?: unknown }).reason === 'string') {\n const r = (body as { reason: string }).reason.trim();\n if (r) reason = r.slice(0, 120);\n }\n } catch {\n /* empty or invalid body */\n }\n service.requestHeartbeatNow({ reason });\n return c.json({ ok: true, payload: { scheduled: true } });\n });\n\n authenticated.get('/api/config', async (c) => {\n const safeConfig = await buildSafeWebConfigPayload(service);\n return c.json({ ok: true, payload: { config: safeConfig } });\n });\n\n // PATCH /api/config — section patchers run sequentially against the live\n // config object (mutate-in-place is intentional: the route handler reads\n // `service.currentConfig` and rewrites it through `saveConfig`). Each\n // patcher only touches the keys it owns and returns `{ ok: false }` with a\n // 400 body when validation fails; we surface the first failure verbatim.\n authenticated.patch('/api/config', strictRateLimitMiddleware, async (c) => {\n const body = await c.req.json();\n const config: Config = service.currentConfig as Config;\n\n applyAgentsPatch(config, body);\n applyChannelsPatch(config, body);\n\n const gatewayResult = applyGatewayPatch(config, body);\n if (gatewayResult.ok === false) {\n return c.json({ ok: false, error: gatewayResult.error }, gatewayResult.status as 400 | 500);\n }\n\n const miscResult = await applyMiscPatch(config, body);\n if (miscResult.ok === false) {\n return c.json({ ok: false, error: miscResult.error }, miscResult.status as 400 | 500);\n }\n\n const finalGwCheck = validateGatewayAfterPatch(config, body);\n if (finalGwCheck.ok === false) {\n return c.json({ ok: false, error: finalGwCheck.error }, finalGwCheck.status as 400 | 500);\n }\n\n const result = await service.saveConfig(config);\n if (!result.saved) {\n return c.json({ ok: false, error: result.error }, 500);\n }\n\n if (body.gateway?.heartbeat !== undefined && typeof body.gateway.heartbeat === 'object') {\n service.reloadHeartbeatFromCurrentConfig();\n }\n\n const safeConfig = await buildSafeWebConfigPayload(service);\n return c.json({ ok: true, payload: { config: safeConfig } });\n });\n\n /** POST /api/gateway/reveal-auth-secret — plaintext gateway.auth token/password from config only. */\n authenticated.post('/api/gateway/reveal-auth-secret', strictRateLimitMiddleware, async (c) => {\n let field: unknown;\n try {\n const body = await c.req.json();\n field = body && typeof body === 'object' ? (body as { field?: unknown }).field : undefined;\n } catch {\n field = undefined;\n }\n if (field !== 'token' && field !== 'password') {\n return c.json({ ok: false, error: { message: 'field must be token or password' } }, 400);\n }\n const config = service.currentConfig as Config;\n const secret =\n field === 'token'\n ? config.gateway?.auth?.token?.trim() || null\n : config.gateway?.auth?.password?.trim() || null;\n return c.json({\n ok: true,\n payload: { field, secret, source: secret ? ('config' as const) : ('none' as const) },\n });\n });\n\n /** POST /api/agents/browser/reveal-cloud-api-key — plaintext browser cloud apiKey from config only. */\n authenticated.post('/api/agents/browser/reveal-cloud-api-key', strictRateLimitMiddleware, async (c) => {\n const config = service.currentConfig as Config;\n const apiKey = config.agents?.defaults?.browser?.cloud?.apiKey?.trim() || null;\n return c.json({\n ok: true,\n payload: { apiKey, source: apiKey ? ('config' as const) : ('none' as const) },\n });\n });\n\n /** POST /api/tools/web/reveal-search-api-key — plaintext search provider apiKey by index. */\n authenticated.post('/api/tools/web/reveal-search-api-key', strictRateLimitMiddleware, async (c) => {\n let index = -1;\n try {\n const body = await c.req.json();\n if (body && typeof body === 'object' && typeof (body as { index?: unknown }).index === 'number') {\n index = Math.floor((body as { index: number }).index);\n }\n } catch {\n index = -1;\n }\n const providers = service.currentConfig.tools?.web?.search?.providers ?? [];\n if (index < 0 || index >= providers.length) {\n return c.json({ ok: false, error: { message: 'Invalid provider index' } }, 400);\n }\n const apiKey = providers[index]?.apiKey?.trim() || null;\n return c.json({\n ok: true,\n payload: { index, apiKey, source: apiKey ? ('config' as const) : ('none' as const) },\n });\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"config.js","names":[],"sources":["../../../../../src/gateway/hono/routes/config.ts"],"sourcesContent":["import type { Context, Hono } from 'hono';\n\nimport type { Config } from '../../../config/schema.js';\nimport { buildSafeWebConfigPayload } from '../lib/config-payload.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\nimport {\n applyAgentsPatch,\n applyChannelsPatch,\n applyGatewayPatch,\n applyMiscPatch,\n validateGatewayAfterPatch,\n} from './config-patch/index.js';\n\nexport function registerConfigRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service, strictRateLimitMiddleware } = deps;\n\n authenticated.post('/api/config/reload', strictRateLimitMiddleware, async (c) => {\n const result = await service.reloadConfig();\n return c.json({ ok: true, payload: result });\n });\n\n authenticated.post('/api/heartbeat/trigger', strictRateLimitMiddleware, async (c) => {\n let reason = 'manual';\n try {\n const body = await c.req.json();\n if (body && typeof body === 'object' && typeof (body as { reason?: unknown }).reason === 'string') {\n const r = (body as { reason: string }).reason.trim();\n if (r) reason = r.slice(0, 120);\n }\n } catch {\n /* empty or invalid body */\n }\n service.requestHeartbeatNow({ reason });\n return c.json({ ok: true, payload: { scheduled: true } });\n });\n\n authenticated.get('/api/config', async (c) => {\n const safeConfig = await buildSafeWebConfigPayload(service);\n return c.json({ ok: true, payload: { config: safeConfig } });\n });\n\n // PATCH /api/config — section patchers run sequentially against the live\n // config object (mutate-in-place is intentional: the route handler reads\n // `service.currentConfig` and rewrites it through `saveConfig`). Each\n // patcher only touches the keys it owns and returns `{ ok: false }` with a\n // 400 body when validation fails; we surface the first failure verbatim.\n authenticated.patch('/api/config', strictRateLimitMiddleware, async (c) => {\n const body = await c.req.json();\n const config: Config = service.currentConfig as Config;\n\n applyAgentsPatch(config, body);\n applyChannelsPatch(config, body);\n\n const gatewayResult = applyGatewayPatch(config, body);\n if (gatewayResult.ok === false) {\n return c.json({ ok: false, error: gatewayResult.error }, gatewayResult.status as 400 | 500);\n }\n\n const miscResult = await applyMiscPatch(config, body);\n if (miscResult.ok === false) {\n return c.json({ ok: false, error: miscResult.error }, miscResult.status as 400 | 500);\n }\n\n const finalGwCheck = validateGatewayAfterPatch(config, body);\n if (finalGwCheck.ok === false) {\n return c.json({ ok: false, error: finalGwCheck.error }, finalGwCheck.status as 400 | 500);\n }\n\n const result = await service.saveConfig(config);\n if (!result.saved) {\n return c.json({ ok: false, error: result.error }, 500);\n }\n\n if (body.gateway?.heartbeat !== undefined && typeof body.gateway.heartbeat === 'object') {\n service.reloadHeartbeatFromCurrentConfig();\n }\n\n const safeConfig = await buildSafeWebConfigPayload(service);\n return c.json({ ok: true, payload: { config: safeConfig } });\n });\n\n const revealGatewayAuthSecretHandler = async (c: Context) => {\n const field = await resolveRevealGatewayAuthField(c);\n if (!field) {\n return c.json({ ok: false, error: { message: 'field must be token or password' } }, 400);\n }\n const config = service.currentConfig as Config;\n const secret =\n field === 'token'\n ? config.gateway?.auth?.token?.trim() || null\n : config.gateway?.auth?.password?.trim() || null;\n return c.json({\n ok: true,\n payload: { field, secret, source: secret ? ('config' as const) : ('none' as const) },\n });\n };\n\n /**\n * POST /api/gateway/reveal-auth-secret/:field — preferred; no JSON body required.\n * POST /api/gateway/reveal-auth-secret — legacy JSON body `{ field: \"token\" | \"password\" }`.\n */\n authenticated.post(\n '/api/gateway/reveal-auth-secret/:field',\n strictRateLimitMiddleware,\n revealGatewayAuthSecretHandler,\n );\n authenticated.post('/api/gateway/reveal-auth-secret', strictRateLimitMiddleware, revealGatewayAuthSecretHandler);\n\n /** POST /api/agents/browser/reveal-cloud-api-key — plaintext browser cloud apiKey from config only. */\n authenticated.post('/api/agents/browser/reveal-cloud-api-key', strictRateLimitMiddleware, async (c) => {\n const config = service.currentConfig as Config;\n const apiKey = config.agents?.defaults?.browser?.cloud?.apiKey?.trim() || null;\n return c.json({\n ok: true,\n payload: { apiKey, source: apiKey ? ('config' as const) : ('none' as const) },\n });\n });\n\n /** POST /api/tools/web/reveal-search-api-key — plaintext search provider apiKey by index. */\n authenticated.post('/api/tools/web/reveal-search-api-key', strictRateLimitMiddleware, async (c) => {\n let index = -1;\n try {\n const body = await c.req.json();\n if (body && typeof body === 'object' && typeof (body as { index?: unknown }).index === 'number') {\n index = Math.floor((body as { index: number }).index);\n }\n } catch {\n index = -1;\n }\n const providers = service.currentConfig.tools?.web?.search?.providers ?? [];\n if (index < 0 || index >= providers.length) {\n return c.json({ ok: false, error: { message: 'Invalid provider index' } }, 400);\n }\n const apiKey = providers[index]?.apiKey?.trim() || null;\n return c.json({\n ok: true,\n payload: { index, apiKey, source: apiKey ? ('config' as const) : ('none' as const) },\n });\n });\n}\n\nfunction normalizeRevealGatewayAuthField(raw: unknown): 'token' | 'password' | null {\n return raw === 'token' || raw === 'password' ? raw : null;\n}\n\nasync function resolveRevealGatewayAuthField(c: Context): Promise<'token' | 'password' | null> {\n const fromPath = normalizeRevealGatewayAuthField(c.req.param('field'));\n if (fromPath) return fromPath;\n\n const fromQuery = normalizeRevealGatewayAuthField(c.req.query('field'));\n if (fromQuery) return fromQuery;\n\n try {\n const body = await c.req.json();\n return normalizeRevealGatewayAuthField(\n body && typeof body === 'object' && !Array.isArray(body)\n ? (body as { field?: unknown }).field\n : undefined,\n );\n } catch {\n return null;\n }\n}\n"],"mappings":";;;;;;;AAaA,SAAgB,qBAAqB,eAAqB,MAAoC;CAC5F,MAAM,EAAE,SAAS,8BAA8B;AAE/C,eAAc,KAAK,sBAAsB,2BAA2B,OAAO,MAAM;EAC/E,MAAM,SAAS,MAAM,QAAQ,cAAc;AAC3C,SAAO,EAAE,KAAK;GAAE,IAAI;GAAM,SAAS;GAAQ,CAAC;GAC5C;AAEF,eAAc,KAAK,0BAA0B,2BAA2B,OAAO,MAAM;EACnF,IAAI,SAAS;AACb,MAAI;GACF,MAAM,OAAO,MAAM,EAAE,IAAI,MAAM;AAC/B,OAAI,QAAQ,OAAO,SAAS,YAAY,OAAQ,KAA8B,WAAW,UAAU;IACjG,MAAM,IAAK,KAA4B,OAAO,MAAM;AACpD,QAAI,EAAG,UAAS,EAAE,MAAM,GAAG,IAAI;;UAE3B;AAGR,UAAQ,oBAAoB,EAAE,QAAQ,CAAC;AACvC,SAAO,EAAE,KAAK;GAAE,IAAI;GAAM,SAAS,EAAE,WAAW,MAAM;GAAE,CAAC;GACzD;AAEF,eAAc,IAAI,eAAe,OAAO,MAAM;EAC5C,MAAM,aAAa,MAAM,0BAA0B,QAAQ;AAC3D,SAAO,EAAE,KAAK;GAAE,IAAI;GAAM,SAAS,EAAE,QAAQ,YAAY;GAAE,CAAC;GAC5D;AAOF,eAAc,MAAM,eAAe,2BAA2B,OAAO,MAAM;EACzE,MAAM,OAAO,MAAM,EAAE,IAAI,MAAM;EAC/B,MAAM,SAAiB,QAAQ;AAE/B,mBAAiB,QAAQ,KAAK;AAC9B,qBAAmB,QAAQ,KAAK;EAEhC,MAAM,gBAAgB,kBAAkB,QAAQ,KAAK;AACrD,MAAI,cAAc,OAAO,MACvB,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,cAAc;GAAO,EAAE,cAAc,OAAoB;EAG7F,MAAM,aAAa,MAAM,eAAe,QAAQ,KAAK;AACrD,MAAI,WAAW,OAAO,MACpB,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,WAAW;GAAO,EAAE,WAAW,OAAoB;EAGvF,MAAM,eAAe,0BAA0B,QAAQ,KAAK;AAC5D,MAAI,aAAa,OAAO,MACtB,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,aAAa;GAAO,EAAE,aAAa,OAAoB;EAG3F,MAAM,SAAS,MAAM,QAAQ,WAAW,OAAO;AAC/C,MAAI,CAAC,OAAO,MACV,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,OAAO;GAAO,EAAE,IAAI;AAGxD,MAAI,KAAK,SAAS,cAAc,KAAA,KAAa,OAAO,KAAK,QAAQ,cAAc,SAC7E,SAAQ,kCAAkC;EAG5C,MAAM,aAAa,MAAM,0BAA0B,QAAQ;AAC3D,SAAO,EAAE,KAAK;GAAE,IAAI;GAAM,SAAS,EAAE,QAAQ,YAAY;GAAE,CAAC;GAC5D;CAEF,MAAM,iCAAiC,OAAO,MAAe;EAC3D,MAAM,QAAQ,MAAM,8BAA8B,EAAE;AACpD,MAAI,CAAC,MACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,mCAAmC;GAAE,EAAE,IAAI;EAE1F,MAAM,SAAS,QAAQ;EACvB,MAAM,SACJ,UAAU,UACN,OAAO,SAAS,MAAM,OAAO,MAAM,IAAI,OACvC,OAAO,SAAS,MAAM,UAAU,MAAM,IAAI;AAChD,SAAO,EAAE,KAAK;GACZ,IAAI;GACJ,SAAS;IAAE;IAAO;IAAQ,QAAQ,SAAU,WAAsB;IAAkB;GACrF,CAAC;;;;;;AAOJ,eAAc,KACZ,0CACA,2BACA,+BACD;AACD,eAAc,KAAK,mCAAmC,2BAA2B,+BAA+B;;AAGhH,eAAc,KAAK,4CAA4C,2BAA2B,OAAO,MAAM;EAErG,MAAM,SADS,QAAQ,cACD,QAAQ,UAAU,SAAS,OAAO,QAAQ,MAAM,IAAI;AAC1E,SAAO,EAAE,KAAK;GACZ,IAAI;GACJ,SAAS;IAAE;IAAQ,QAAQ,SAAU,WAAsB;IAAkB;GAC9E,CAAC;GACF;;AAGF,eAAc,KAAK,wCAAwC,2BAA2B,OAAO,MAAM;EACjG,IAAI,QAAQ;AACZ,MAAI;GACF,MAAM,OAAO,MAAM,EAAE,IAAI,MAAM;AAC/B,OAAI,QAAQ,OAAO,SAAS,YAAY,OAAQ,KAA6B,UAAU,SACrF,SAAQ,KAAK,MAAO,KAA2B,MAAM;UAEjD;AACN,WAAQ;;EAEV,MAAM,YAAY,QAAQ,cAAc,OAAO,KAAK,QAAQ,aAAa,EAAE;AAC3E,MAAI,QAAQ,KAAK,SAAS,UAAU,OAClC,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,0BAA0B;GAAE,EAAE,IAAI;EAEjF,MAAM,SAAS,UAAU,QAAQ,QAAQ,MAAM,IAAI;AACnD,SAAO,EAAE,KAAK;GACZ,IAAI;GACJ,SAAS;IAAE;IAAO;IAAQ,QAAQ,SAAU,WAAsB;IAAkB;GACrF,CAAC;GACF;;AAGJ,SAAS,gCAAgC,KAA2C;AAClF,QAAO,QAAQ,WAAW,QAAQ,aAAa,MAAM;;AAGvD,eAAe,8BAA8B,GAAkD;CAC7F,MAAM,WAAW,gCAAgC,EAAE,IAAI,MAAM,QAAQ,CAAC;AACtE,KAAI,SAAU,QAAO;CAErB,MAAM,YAAY,gCAAgC,EAAE,IAAI,MAAM,QAAQ,CAAC;AACvE,KAAI,UAAW,QAAO;AAEtB,KAAI;EACF,MAAM,OAAO,MAAM,EAAE,IAAI,MAAM;AAC/B,SAAO,gCACL,QAAQ,OAAO,SAAS,YAAY,CAAC,MAAM,QAAQ,KAAK,GACnD,KAA6B,QAC9B,KAAA,EACL;SACK;AACN,SAAO"}
@@ -1,4 +1,6 @@
1
+ import { createGatewayRouteLogger, logRouteError } from "../lib/route-logger.js";
1
2
  //#region src/gateway/hono/routes/cron.ts
3
+ const log = createGatewayRouteLogger("Cron");
2
4
  function registerCronRoutes(authenticated, deps) {
3
5
  const { service } = deps;
4
6
  authenticated.get("/api/cron", async (c) => {
@@ -21,6 +23,7 @@ function registerCronRoutes(authenticated, deps) {
21
23
  });
22
24
  return c.json(result, 201);
23
25
  } catch (err) {
26
+ logRouteError(log, c, err, "gateway.route.cron", { operation: "addJob" });
24
27
  return c.json({ error: err instanceof Error ? err.message : "Failed to add job" }, 400);
25
28
  }
26
29
  });
@@ -53,6 +56,7 @@ function registerCronRoutes(authenticated, deps) {
53
56
  await service.cronServiceInstance.runJobNow(id);
54
57
  return c.json({ triggered: true });
55
58
  } catch (err) {
59
+ logRouteError(log, c, err, "gateway.route.cron", { operation: "runJob" });
56
60
  return c.json({ error: err instanceof Error ? err.message : "Failed to run job" }, 400);
57
61
  }
58
62
  });
@@ -70,6 +74,7 @@ function registerCronRoutes(authenticated, deps) {
70
74
  const result = await service.cronServiceInstance.updateJob(id, body);
71
75
  return c.json({ updated: result });
72
76
  } catch (err) {
77
+ logRouteError(log, c, err, "gateway.route.cron", { operation: "updateJob" });
73
78
  return c.json({ error: err instanceof Error ? err.message : "Failed to update job" }, 400);
74
79
  }
75
80
  });
@@ -1 +1 @@
1
- {"version":3,"file":"cron.js","names":[],"sources":["../../../../../src/gateway/hono/routes/cron.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nexport function registerCronRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service } = deps;\n\n // ========== Cron REST API (/api/cron) ==========\n\n // GET /api/cron - List all jobs\n authenticated.get('/api/cron', async (c) => {\n const jobs = await service.cronServiceInstance.listJobs();\n return c.json({ jobs });\n });\n\n // POST /api/cron - Add new job\n authenticated.post('/api/cron', async (c) => {\n const body = await c.req.json();\n const { schedule, name, timezone, sessionTarget, agentId, workingDirectory, model, delivery, payload } = body;\n\n if (!schedule || !payload) {\n return c.json({ error: 'Missing required fields: schedule, payload' }, 400);\n }\n\n try {\n const result = await service.cronServiceInstance.addJob(schedule, {\n name,\n timezone,\n sessionTarget,\n ...(typeof agentId === 'string' && agentId.trim() ? { agentId: agentId.trim() } : {}),\n ...(typeof workingDirectory === 'string' && workingDirectory.trim()\n ? { workingDirectory: workingDirectory.trim() }\n : {}),\n model,\n delivery,\n payload,\n });\n return c.json(result, 201);\n } catch (err) {\n return c.json({ error: err instanceof Error ? err.message : 'Failed to add job' }, 400);\n }\n });\n\n // GET /api/cron/metrics - Get cron metrics (must be before /:id)\n authenticated.get('/api/cron/metrics', async (c) => {\n const metrics = await service.cronServiceInstance.getMetrics();\n return c.json(metrics);\n });\n\n // GET /api/cron/runs/history - Recent runs across all jobs (must be before /:id)\n authenticated.get('/api/cron/runs/history', async (c) => {\n const raw = c.req.query('limit');\n const limit = raw ? parseInt(raw, 10) : 50;\n const runs = await service.cronServiceInstance.getAllRunsHistory(Number.isFinite(limit) ? limit : 50);\n return c.json({ runs });\n });\n\n // GET /api/cron/:id - Get single job (must be after /metrics)\n authenticated.get('/api/cron/:id', async (c) => {\n const id = c.req.param('id');\n const job = await service.cronServiceInstance.getJob(id);\n if (!job) {\n return c.json({ error: 'Job not found' }, 404);\n }\n return c.json({ job });\n });\n\n // POST /api/cron/:id/toggle - Toggle job enabled\n authenticated.post('/api/cron/:id/toggle', async (c) => {\n const id = c.req.param('id');\n const body = await c.req.json();\n const { enabled } = body;\n\n if (typeof enabled !== 'boolean') {\n return c.json({ error: 'Missing required field: enabled' }, 400);\n }\n\n const result = await service.cronServiceInstance.toggleJob(id, enabled);\n return c.json({ toggled: result });\n });\n\n // POST /api/cron/:id/run - Trigger job manually\n authenticated.post('/api/cron/:id/run', async (c) => {\n const id = c.req.param('id');\n\n try {\n await service.cronServiceInstance.runJobNow(id);\n return c.json({ triggered: true });\n } catch (err) {\n return c.json({ error: err instanceof Error ? err.message : 'Failed to run job' }, 400);\n }\n });\n\n // GET /api/cron/:id/history - Get job execution history\n authenticated.get('/api/cron/:id/history', async (c) => {\n const id = c.req.param('id');\n const raw = c.req.query('limit');\n const limit = raw ? parseInt(raw, 10) : 10;\n const history = await service.cronServiceInstance.getJobHistory(id, Number.isFinite(limit) ? limit : 10);\n return c.json({ history });\n });\n\n // PATCH /api/cron/:id - Update job\n authenticated.patch('/api/cron/:id', async (c) => {\n const id = c.req.param('id');\n const body = await c.req.json();\n\n try {\n const result = await service.cronServiceInstance.updateJob(id, body);\n return c.json({ updated: result });\n } catch (err) {\n return c.json({ error: err instanceof Error ? err.message : 'Failed to update job' }, 400);\n }\n });\n\n // DELETE /api/cron/:id - Remove job\n authenticated.delete('/api/cron/:id', async (c) => {\n const id = c.req.param('id');\n const result = await service.cronServiceInstance.removeJob(id);\n return c.json({ removed: result });\n });\n}\n"],"mappings":";AAIA,SAAgB,mBAAmB,eAAqB,MAAoC;CAC1F,MAAM,EAAE,YAAY;AAKpB,eAAc,IAAI,aAAa,OAAO,MAAM;EAC1C,MAAM,OAAO,MAAM,QAAQ,oBAAoB,UAAU;AACzD,SAAO,EAAE,KAAK,EAAE,MAAM,CAAC;GACvB;AAGF,eAAc,KAAK,aAAa,OAAO,MAAM;EAE3C,MAAM,EAAE,UAAU,MAAM,UAAU,eAAe,SAAS,kBAAkB,OAAO,UAAU,YAAY,MADtF,EAAE,IAAI,MAAM;AAG/B,MAAI,CAAC,YAAY,CAAC,QAChB,QAAO,EAAE,KAAK,EAAE,OAAO,8CAA8C,EAAE,IAAI;AAG7E,MAAI;GACF,MAAM,SAAS,MAAM,QAAQ,oBAAoB,OAAO,UAAU;IAChE;IACA;IACA;IACA,GAAI,OAAO,YAAY,YAAY,QAAQ,MAAM,GAAG,EAAE,SAAS,QAAQ,MAAM,EAAE,GAAG,EAAE;IACpF,GAAI,OAAO,qBAAqB,YAAY,iBAAiB,MAAM,GAC/D,EAAE,kBAAkB,iBAAiB,MAAM,EAAE,GAC7C,EAAE;IACN;IACA;IACA;IACD,CAAC;AACF,UAAO,EAAE,KAAK,QAAQ,IAAI;WACnB,KAAK;AACZ,UAAO,EAAE,KAAK,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,qBAAqB,EAAE,IAAI;;GAEzF;AAGF,eAAc,IAAI,qBAAqB,OAAO,MAAM;EAClD,MAAM,UAAU,MAAM,QAAQ,oBAAoB,YAAY;AAC9D,SAAO,EAAE,KAAK,QAAQ;GACtB;AAGF,eAAc,IAAI,0BAA0B,OAAO,MAAM;EACvD,MAAM,MAAM,EAAE,IAAI,MAAM,QAAQ;EAChC,MAAM,QAAQ,MAAM,SAAS,KAAK,GAAG,GAAG;EACxC,MAAM,OAAO,MAAM,QAAQ,oBAAoB,kBAAkB,OAAO,SAAS,MAAM,GAAG,QAAQ,GAAG;AACrG,SAAO,EAAE,KAAK,EAAE,MAAM,CAAC;GACvB;AAGF,eAAc,IAAI,iBAAiB,OAAO,MAAM;EAC9C,MAAM,KAAK,EAAE,IAAI,MAAM,KAAK;EAC5B,MAAM,MAAM,MAAM,QAAQ,oBAAoB,OAAO,GAAG;AACxD,MAAI,CAAC,IACH,QAAO,EAAE,KAAK,EAAE,OAAO,iBAAiB,EAAE,IAAI;AAEhD,SAAO,EAAE,KAAK,EAAE,KAAK,CAAC;GACtB;AAGF,eAAc,KAAK,wBAAwB,OAAO,MAAM;EACtD,MAAM,KAAK,EAAE,IAAI,MAAM,KAAK;EAE5B,MAAM,EAAE,YAAY,MADD,EAAE,IAAI,MAAM;AAG/B,MAAI,OAAO,YAAY,UACrB,QAAO,EAAE,KAAK,EAAE,OAAO,mCAAmC,EAAE,IAAI;EAGlE,MAAM,SAAS,MAAM,QAAQ,oBAAoB,UAAU,IAAI,QAAQ;AACvE,SAAO,EAAE,KAAK,EAAE,SAAS,QAAQ,CAAC;GAClC;AAGF,eAAc,KAAK,qBAAqB,OAAO,MAAM;EACnD,MAAM,KAAK,EAAE,IAAI,MAAM,KAAK;AAE5B,MAAI;AACF,SAAM,QAAQ,oBAAoB,UAAU,GAAG;AAC/C,UAAO,EAAE,KAAK,EAAE,WAAW,MAAM,CAAC;WAC3B,KAAK;AACZ,UAAO,EAAE,KAAK,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,qBAAqB,EAAE,IAAI;;GAEzF;AAGF,eAAc,IAAI,yBAAyB,OAAO,MAAM;EACtD,MAAM,KAAK,EAAE,IAAI,MAAM,KAAK;EAC5B,MAAM,MAAM,EAAE,IAAI,MAAM,QAAQ;EAChC,MAAM,QAAQ,MAAM,SAAS,KAAK,GAAG,GAAG;EACxC,MAAM,UAAU,MAAM,QAAQ,oBAAoB,cAAc,IAAI,OAAO,SAAS,MAAM,GAAG,QAAQ,GAAG;AACxG,SAAO,EAAE,KAAK,EAAE,SAAS,CAAC;GAC1B;AAGF,eAAc,MAAM,iBAAiB,OAAO,MAAM;EAChD,MAAM,KAAK,EAAE,IAAI,MAAM,KAAK;EAC5B,MAAM,OAAO,MAAM,EAAE,IAAI,MAAM;AAE/B,MAAI;GACF,MAAM,SAAS,MAAM,QAAQ,oBAAoB,UAAU,IAAI,KAAK;AACpE,UAAO,EAAE,KAAK,EAAE,SAAS,QAAQ,CAAC;WAC3B,KAAK;AACZ,UAAO,EAAE,KAAK,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,wBAAwB,EAAE,IAAI;;GAE5F;AAGF,eAAc,OAAO,iBAAiB,OAAO,MAAM;EACjD,MAAM,KAAK,EAAE,IAAI,MAAM,KAAK;EAC5B,MAAM,SAAS,MAAM,QAAQ,oBAAoB,UAAU,GAAG;AAC9D,SAAO,EAAE,KAAK,EAAE,SAAS,QAAQ,CAAC;GAClC"}
1
+ {"version":3,"file":"cron.js","names":[],"sources":["../../../../../src/gateway/hono/routes/cron.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport type { AuthenticatedRouteDeps } from './deps.js';\nimport { createGatewayRouteLogger, logRouteError } from '../lib/route-logger.js';\n\nconst log = createGatewayRouteLogger('Cron');\n\nexport function registerCronRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service } = deps;\n\n // ========== Cron REST API (/api/cron) ==========\n\n // GET /api/cron - List all jobs\n authenticated.get('/api/cron', async (c) => {\n const jobs = await service.cronServiceInstance.listJobs();\n return c.json({ jobs });\n });\n\n // POST /api/cron - Add new job\n authenticated.post('/api/cron', async (c) => {\n const body = await c.req.json();\n const { schedule, name, timezone, sessionTarget, agentId, workingDirectory, model, delivery, payload } = body;\n\n if (!schedule || !payload) {\n return c.json({ error: 'Missing required fields: schedule, payload' }, 400);\n }\n\n try {\n const result = await service.cronServiceInstance.addJob(schedule, {\n name,\n timezone,\n sessionTarget,\n ...(typeof agentId === 'string' && agentId.trim() ? { agentId: agentId.trim() } : {}),\n ...(typeof workingDirectory === 'string' && workingDirectory.trim()\n ? { workingDirectory: workingDirectory.trim() }\n : {}),\n model,\n delivery,\n payload,\n });\n return c.json(result, 201);\n } catch (err) {\n logRouteError(log, c, err, 'gateway.route.cron', { operation: 'addJob' });\n return c.json({ error: err instanceof Error ? err.message : 'Failed to add job' }, 400);\n }\n });\n\n // GET /api/cron/metrics - Get cron metrics (must be before /:id)\n authenticated.get('/api/cron/metrics', async (c) => {\n const metrics = await service.cronServiceInstance.getMetrics();\n return c.json(metrics);\n });\n\n // GET /api/cron/runs/history - Recent runs across all jobs (must be before /:id)\n authenticated.get('/api/cron/runs/history', async (c) => {\n const raw = c.req.query('limit');\n const limit = raw ? parseInt(raw, 10) : 50;\n const runs = await service.cronServiceInstance.getAllRunsHistory(Number.isFinite(limit) ? limit : 50);\n return c.json({ runs });\n });\n\n // GET /api/cron/:id - Get single job (must be after /metrics)\n authenticated.get('/api/cron/:id', async (c) => {\n const id = c.req.param('id');\n const job = await service.cronServiceInstance.getJob(id);\n if (!job) {\n return c.json({ error: 'Job not found' }, 404);\n }\n return c.json({ job });\n });\n\n // POST /api/cron/:id/toggle - Toggle job enabled\n authenticated.post('/api/cron/:id/toggle', async (c) => {\n const id = c.req.param('id');\n const body = await c.req.json();\n const { enabled } = body;\n\n if (typeof enabled !== 'boolean') {\n return c.json({ error: 'Missing required field: enabled' }, 400);\n }\n\n const result = await service.cronServiceInstance.toggleJob(id, enabled);\n return c.json({ toggled: result });\n });\n\n // POST /api/cron/:id/run - Trigger job manually\n authenticated.post('/api/cron/:id/run', async (c) => {\n const id = c.req.param('id');\n\n try {\n await service.cronServiceInstance.runJobNow(id);\n return c.json({ triggered: true });\n } catch (err) {\n logRouteError(log, c, err, 'gateway.route.cron', { operation: 'runJob' });\n return c.json({ error: err instanceof Error ? err.message : 'Failed to run job' }, 400);\n }\n });\n\n // GET /api/cron/:id/history - Get job execution history\n authenticated.get('/api/cron/:id/history', async (c) => {\n const id = c.req.param('id');\n const raw = c.req.query('limit');\n const limit = raw ? parseInt(raw, 10) : 10;\n const history = await service.cronServiceInstance.getJobHistory(id, Number.isFinite(limit) ? limit : 10);\n return c.json({ history });\n });\n\n // PATCH /api/cron/:id - Update job\n authenticated.patch('/api/cron/:id', async (c) => {\n const id = c.req.param('id');\n const body = await c.req.json();\n\n try {\n const result = await service.cronServiceInstance.updateJob(id, body);\n return c.json({ updated: result });\n } catch (err) {\n logRouteError(log, c, err, 'gateway.route.cron', { operation: 'updateJob' });\n return c.json({ error: err instanceof Error ? err.message : 'Failed to update job' }, 400);\n }\n });\n\n // DELETE /api/cron/:id - Remove job\n authenticated.delete('/api/cron/:id', async (c) => {\n const id = c.req.param('id');\n const result = await service.cronServiceInstance.removeJob(id);\n return c.json({ removed: result });\n });\n}\n"],"mappings":"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"}
@@ -5,8 +5,8 @@ import { getWorkspacePath } from "../../../config/workspace-path-helpers.js";
5
5
  import { parseDreamingLastRunFile } from "../../../agent/memory/dreaming/last-run.js";
6
6
  import { readDreamingEvents } from "../../../agent/memory/dreaming/events.js";
7
7
  import { previewDreamingDeepPromotion } from "../../../agent/memory/dreaming/preview.js";
8
- import fs from "node:fs/promises";
9
8
  import path from "node:path";
9
+ import fs from "node:fs/promises";
10
10
  //#region src/gateway/hono/routes/dreaming.ts
11
11
  function isRecord(v) {
12
12
  return v !== null && typeof v === "object" && !Array.isArray(v);