@xopcai/xopc 0.0.92 → 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 (194) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/agents-OqhbJkMf.js +222 -0
  4. package/dist/gateway/static/root/assets/apps-page-OHXW9XP8.js +1 -0
  5. package/dist/gateway/static/root/assets/channels-settings-4N2R-jof.js +1 -0
  6. package/dist/gateway/static/root/assets/{channels-status-swr-XzddfJW2.js → channels-status-swr-Bv6f9kDq.js} +1 -1
  7. package/dist/gateway/static/root/assets/{cron-api--I8LJ44S.js → cron-api-BtaQaHJq.js} +1 -1
  8. package/dist/gateway/static/root/assets/cron-page-Dah32HJK.js +1 -0
  9. package/dist/gateway/static/root/assets/{dist-CYgHMQO0.js → dist-BJfD9Qvs.js} +1 -1
  10. package/dist/gateway/static/root/assets/{extension-debug-page-6cRP0nA9.js → extension-debug-page-DnYuMzmH.js} +1 -1
  11. package/dist/gateway/static/root/assets/{extension-page-DpwIkspI.js → extension-page-CJfc-6XV.js} +1 -1
  12. package/dist/gateway/static/root/assets/{extension-settings-page-DYbnQUxH.js → extension-settings-page-BxdfYQMG.js} +1 -1
  13. package/dist/gateway/static/root/assets/{fetch-DTN0w7rV.js → fetch-B0aeeY0q.js} +1 -1
  14. package/dist/gateway/static/root/assets/{field-primitives-CslW6HwD.js → field-primitives-DOLHwowi.js} +1 -1
  15. package/dist/gateway/static/root/assets/{heartbeat-config-api-2UiKevxG.js → heartbeat-config-api-Bj2INAf5.js} +1 -1
  16. package/dist/gateway/static/root/assets/index-Bj_l8QDp.css +1 -0
  17. package/dist/gateway/static/root/assets/{index-DnevRVa6.js → index-DuQ1XPoA.js} +99 -98
  18. package/dist/gateway/static/root/assets/logs-page-AsOgLNJE.js +2 -0
  19. package/dist/gateway/static/root/assets/{note-detail-page-DvW2qg4i.js → note-detail-page-24J4mVP-.js} +53 -53
  20. package/dist/gateway/static/root/assets/{note-time-BEiibLJv.js → note-time-JBszYV3s.js} +1 -1
  21. package/dist/gateway/static/root/assets/notes-page-BApAirFB.js +1 -0
  22. package/dist/gateway/static/root/assets/sessions-page-DX9huWsA.js +1 -0
  23. package/dist/gateway/static/root/assets/{settings-advanced-gate-BctKqHcf.js → settings-advanced-gate-DWvhsTuz.js} +1 -1
  24. package/dist/gateway/static/root/assets/{settings-form-section-QJh5ruel.js → settings-form-section-CxMjaMiy.js} +1 -1
  25. package/dist/gateway/static/root/assets/settings-page-4VmUTzQs.js +3 -0
  26. package/dist/gateway/static/root/assets/{share-preview-page-DBsvvbmD.js → share-preview-page-IX0TJvRd.js} +1 -1
  27. package/dist/gateway/static/root/assets/skills-page-CGKGKfwe.js +2 -0
  28. package/dist/gateway/static/root/assets/{theme-store-ht5iswWS.js → theme-store-Cg_SuBw0.js} +1 -1
  29. package/dist/gateway/static/root/assets/url-BHHmdJYc.js +3 -0
  30. package/dist/gateway/static/root/assets/{utils-DhPv9xoB.js → utils-BmlcxR2j.js} +1 -1
  31. package/dist/gateway/static/root/assets/voice-api-key-field-DaGm2N4J.js +1 -0
  32. package/dist/gateway/static/root/assets/{workflow-page.utils-CJqnPWkW.js → workflow-page.utils-D0vsIGHD.js} +1 -1
  33. package/dist/gateway/static/root/assets/workflows-page-BFCrD3nw.js +27 -0
  34. package/dist/gateway/static/root/index.html +5 -5
  35. package/dist/package.js +1 -1
  36. package/dist/src/agent/inbound/turn-dispatcher.d.ts +1 -0
  37. package/dist/src/agent/inbound/turn-dispatcher.js +3 -0
  38. package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
  39. package/dist/src/agent/lifecycle/handlers/compaction.js +1 -1
  40. package/dist/src/agent/lifecycle/handlers/compaction.js.map +1 -1
  41. package/dist/src/agent/mcp/bundle-mcp-materialize.js +1 -1
  42. package/dist/src/agent/mcp/bundle-mcp-materialize.js.map +1 -1
  43. package/dist/src/agent/mcp/bundle-mcp-runtime.js +17 -4
  44. package/dist/src/agent/mcp/bundle-mcp-runtime.js.map +1 -1
  45. package/dist/src/agent/mcp/mcp-transport-config.js +10 -3
  46. package/dist/src/agent/mcp/mcp-transport-config.js.map +1 -1
  47. package/dist/src/agent/mcp/mcp-transport.js +1 -1
  48. package/dist/src/agent/mcp/mcp-transport.js.map +1 -1
  49. package/dist/src/agent/service/process-direct-streaming.d.ts +1 -0
  50. package/dist/src/agent/service/process-direct-streaming.js +15 -12
  51. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  52. package/dist/src/agent/service.d.ts +4 -2
  53. package/dist/src/agent/service.js +20 -4
  54. package/dist/src/agent/service.js.map +1 -1
  55. package/dist/src/agent/service.types.d.ts +3 -1
  56. package/dist/src/agent/tools/browser/tool/browser-use-tool.js +1 -1
  57. package/dist/src/agent/tools/browser/tool/browser-use-tool.js.map +1 -1
  58. package/dist/src/agent/tools/search/registry.js +1 -1
  59. package/dist/src/agent/tools/search/registry.js.map +1 -1
  60. package/dist/src/agent/tools/session-search-tool.js +1 -1
  61. package/dist/src/agent/tools/session-search-tool.js.map +1 -1
  62. package/dist/src/agent/tools/workflow-tool.js +1 -1
  63. package/dist/src/agent/tools/workflow-tool.js.map +1 -1
  64. package/dist/src/agent/workflow/progress-broker.js +1 -1
  65. package/dist/src/agent/workflow/progress-broker.js.map +1 -1
  66. package/dist/src/agent/workflow/subagent-runner.js +1 -1
  67. package/dist/src/agent/workflow/subagent-runner.js.map +1 -1
  68. package/dist/src/channels/pipeline.js +3 -2
  69. package/dist/src/channels/pipeline.js.map +1 -1
  70. package/dist/src/cli/cli-log-level-preset.d.ts +1 -1
  71. package/dist/src/cli/cli-log-level-preset.js +2 -2
  72. package/dist/src/cli/cli-log-level-preset.js.map +1 -1
  73. package/dist/src/cli/commands/logs.js +3 -3
  74. package/dist/src/cli/commands/logs.js.map +1 -1
  75. package/dist/src/cron/executor.js +7 -4
  76. package/dist/src/cron/executor.js.map +1 -1
  77. package/dist/src/gateway/hono/app.js +4 -1
  78. package/dist/src/gateway/hono/app.js.map +1 -1
  79. package/dist/src/gateway/hono/lib/route-logger.d.ts +6 -0
  80. package/dist/src/gateway/hono/lib/route-logger.js +31 -0
  81. package/dist/src/gateway/hono/lib/route-logger.js.map +1 -0
  82. package/dist/src/gateway/hono/middleware/auth.js +16 -3
  83. package/dist/src/gateway/hono/middleware/auth.js.map +1 -1
  84. package/dist/src/gateway/hono/middleware/logger.js +1 -1
  85. package/dist/src/gateway/hono/middleware/logger.js.map +1 -1
  86. package/dist/src/gateway/hono/middleware/route-errors.d.ts +5 -0
  87. package/dist/src/gateway/hono/middleware/route-errors.js +27 -0
  88. package/dist/src/gateway/hono/middleware/route-errors.js.map +1 -0
  89. package/dist/src/gateway/hono/routes/agent-stream.js +6 -0
  90. package/dist/src/gateway/hono/routes/agent-stream.js.map +1 -1
  91. package/dist/src/gateway/hono/routes/browser-install.js +2 -4
  92. package/dist/src/gateway/hono/routes/browser-install.js.map +1 -1
  93. package/dist/src/gateway/hono/routes/config.js +25 -11
  94. package/dist/src/gateway/hono/routes/config.js.map +1 -1
  95. package/dist/src/gateway/hono/routes/cron.js +5 -0
  96. package/dist/src/gateway/hono/routes/cron.js.map +1 -1
  97. package/dist/src/gateway/hono/routes/host-fs.js +2 -4
  98. package/dist/src/gateway/hono/routes/host-fs.js.map +1 -1
  99. package/dist/src/gateway/hono/routes/lazy-bundles.js +14 -1
  100. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  101. package/dist/src/gateway/hono/routes/lazy-fallback.js +3 -0
  102. package/dist/src/gateway/hono/routes/lazy-fallback.js.map +1 -1
  103. package/dist/src/gateway/hono/routes/logs.js +39 -7
  104. package/dist/src/gateway/hono/routes/logs.js.map +1 -1
  105. package/dist/src/gateway/hono/routes/mcp.d.ts +3 -0
  106. package/dist/src/gateway/hono/routes/mcp.js +107 -0
  107. package/dist/src/gateway/hono/routes/mcp.js.map +1 -0
  108. package/dist/src/gateway/hono/routes/notes.js +105 -1
  109. package/dist/src/gateway/hono/routes/notes.js.map +1 -1
  110. package/dist/src/gateway/hono/routes/sessions.js +6 -0
  111. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  112. package/dist/src/gateway/hono/routes/update.js +2 -4
  113. package/dist/src/gateway/hono/routes/update.js.map +1 -1
  114. package/dist/src/gateway/hono/routes/voice.js +2 -4
  115. package/dist/src/gateway/hono/routes/voice.js.map +1 -1
  116. package/dist/src/gateway/hono/routes/workspace.js +2 -4
  117. package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
  118. package/dist/src/gateway/hono/sse.js +9 -2
  119. package/dist/src/gateway/hono/sse.js.map +1 -1
  120. package/dist/src/gateway/host.d.ts +2 -0
  121. package/dist/src/gateway/host.js +6 -3
  122. package/dist/src/gateway/host.js.map +1 -1
  123. package/dist/src/gateway/service/agent-runner.js +1 -1
  124. package/dist/src/gateway/service/agent-runner.js.map +1 -1
  125. package/dist/src/gateway/service/config-coordinator.js +14 -6
  126. package/dist/src/gateway/service/config-coordinator.js.map +1 -1
  127. package/dist/src/gateway/service/marketplace-service.js +1 -1
  128. package/dist/src/gateway/service/marketplace-service.js.map +1 -1
  129. package/dist/src/gateway/service/run-gateway-agent.js +22 -5
  130. package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
  131. package/dist/src/gateway/service/sse-hub.js +1 -1
  132. package/dist/src/gateway/service/sse-hub.js.map +1 -1
  133. package/dist/src/gateway/service.js +12 -5
  134. package/dist/src/gateway/service.js.map +1 -1
  135. package/dist/src/mcp/channel-bridge.js +26 -2
  136. package/dist/src/mcp/channel-bridge.js.map +1 -1
  137. package/dist/src/mcp/gateway-http-client.js +24 -2
  138. package/dist/src/mcp/gateway-http-client.js.map +1 -1
  139. package/dist/src/notes/service.d.ts +13 -1
  140. package/dist/src/notes/service.js +237 -0
  141. package/dist/src/notes/service.js.map +1 -1
  142. package/dist/src/notes/store.d.ts +3 -0
  143. package/dist/src/notes/store.js +6 -2
  144. package/dist/src/notes/store.js.map +1 -1
  145. package/dist/src/notes/types.d.ts +31 -0
  146. package/dist/src/session/config-store.js +10 -4
  147. package/dist/src/session/config-store.js.map +1 -1
  148. package/dist/src/session/index.d.ts +1 -1
  149. package/dist/src/session/index.js +2 -2
  150. package/dist/src/session/manager.js +8 -1
  151. package/dist/src/session/manager.js.map +1 -1
  152. package/dist/src/session/session-title.d.ts +19 -3
  153. package/dist/src/session/session-title.js +82 -7
  154. package/dist/src/session/session-title.js.map +1 -1
  155. package/dist/src/utils/index.js +4 -4
  156. package/dist/src/utils/logger/config.js +2 -6
  157. package/dist/src/utils/logger/config.js.map +1 -1
  158. package/dist/src/utils/logger/context.d.ts +3 -22
  159. package/dist/src/utils/logger/context.js +4 -32
  160. package/dist/src/utils/logger/context.js.map +1 -1
  161. package/dist/src/utils/logger/index.d.ts +4 -7
  162. package/dist/src/utils/logger/index.js +9 -28
  163. package/dist/src/utils/logger/index.js.map +1 -1
  164. package/dist/src/utils/logger/log-store.d.ts +14 -32
  165. package/dist/src/utils/logger/log-store.js +67 -118
  166. package/dist/src/utils/logger/log-store.js.map +1 -1
  167. package/dist/src/utils/logger/log-stream.d.ts +5 -70
  168. package/dist/src/utils/logger/log-stream.js +67 -178
  169. package/dist/src/utils/logger/log-stream.js.map +1 -1
  170. package/dist/src/utils/logger/pino-record.d.ts +8 -0
  171. package/dist/src/utils/logger/pino-record.js +83 -0
  172. package/dist/src/utils/logger/pino-record.js.map +1 -0
  173. package/dist/src/utils/logger/stats.d.ts +1 -1
  174. package/dist/src/utils/logger/stats.js +2 -2
  175. package/dist/src/utils/logger/stats.js.map +1 -1
  176. package/dist/src/utils/logger/streams.js +18 -0
  177. package/dist/src/utils/logger/streams.js.map +1 -1
  178. package/dist/src/utils/logger/types.d.ts +0 -9
  179. package/dist/src/utils/logger/types.js.map +1 -1
  180. package/dist/src/utils/logger.js +4 -4
  181. package/package.json +6 -1
  182. package/dist/gateway/static/root/assets/agents-uwPn7ZW9.js +0 -222
  183. package/dist/gateway/static/root/assets/apps-page-CWKdhSPU.js +0 -1
  184. package/dist/gateway/static/root/assets/channels-settings-hEhW7Mbk.js +0 -1
  185. package/dist/gateway/static/root/assets/cron-page-B0kvgZGR.js +0 -1
  186. package/dist/gateway/static/root/assets/index-BUKUv7QW.css +0 -1
  187. package/dist/gateway/static/root/assets/logs-page-sOP4TXJ4.js +0 -1
  188. package/dist/gateway/static/root/assets/notes-page-BFQaquHU.js +0 -1
  189. package/dist/gateway/static/root/assets/sessions-page-CptjDKAX.js +0 -1
  190. package/dist/gateway/static/root/assets/settings-page-V3p-hISB.js +0 -2
  191. package/dist/gateway/static/root/assets/skills-page-q2zPUJAR.js +0 -2
  192. package/dist/gateway/static/root/assets/url-CWWpfkq1.js +0 -3
  193. package/dist/gateway/static/root/assets/voice-api-key-field-DLSKUipa.js +0 -1
  194. package/dist/gateway/static/root/assets/workflows-page-DRRQ1A0l.js +0 -27
@@ -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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"}
@@ -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":"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"}
@@ -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":"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"}
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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"}
@@ -1,11 +1,9 @@
1
- import { createLogger } from "../../../utils/logger/index.js";
2
- import { init_logger } from "../../../utils/logger.js";
1
+ import { createGatewayRouteLogger } from "../lib/route-logger.js";
3
2
  import * as os$1 from "node:os";
4
3
  import * as path$1 from "node:path";
5
4
  import { readdir, realpath, stat } from "node:fs/promises";
6
5
  //#region src/gateway/hono/routes/host-fs.ts
7
- init_logger();
8
- const log = createLogger("HostFs");
6
+ const log = createGatewayRouteLogger("HostFs");
9
7
  function jsonError(status, message) {
10
8
  return Response.json({
11
9
  ok: false,
@@ -1 +1 @@
1
- {"version":3,"file":"host-fs.js","names":["path","os"],"sources":["../../../../../src/gateway/hono/routes/host-fs.ts"],"sourcesContent":["/**\n * Authenticated host filesystem browse API for Web UI (e.g. session working directory).\n * Lists directories the gateway process can read — intended for trusted operators only.\n */\nimport type { Hono } from 'hono';\nimport { readdir, realpath, stat } from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\n\nimport { createLogger } from '../../../utils/logger.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nconst log = createLogger('HostFs');\n\nfunction jsonError(status: number, message: string) {\n return Response.json({ ok: false, error: { message } }, { status });\n}\n\nfunction skipDotName(name: string): boolean {\n return name.startsWith('.');\n}\n\n/** Windows: true if `p` is a drive root like `C:\\`. */\nfunction isWindowsDriveRoot(p: string): boolean {\n return /^[A-Za-z]:\\\\?$/.test(path.normalize(p).replace(/\\\\$/, '\\\\'));\n}\n\nfunction parentDirectory(absNormalized: string): string | null {\n if (process.platform === 'win32') {\n const n = path.normalize(absNormalized);\n if (isWindowsDriveRoot(n)) {\n return null;\n }\n const d = path.dirname(n);\n if (d === n) return null;\n return d;\n }\n const n = path.normalize(absNormalized);\n if (n === '/' || n === path.parse(n).root) {\n return null;\n }\n const d = path.dirname(n);\n if (d === n) return null;\n return d;\n}\n\nasync function listWindowsDrives(): Promise<\n { name: string; absolutePath: string; isDirectory: boolean }[]\n> {\n const entries: { name: string; absolutePath: string; isDirectory: boolean }[] = [];\n for (let i = 0; i < 26; i++) {\n const letter = String.fromCharCode(65 + i);\n const root = `${letter}:\\\\`;\n try {\n await stat(root);\n entries.push({ name: root, absolutePath: root, isDirectory: true });\n } catch {\n /* not mounted */\n }\n }\n entries.sort((a, b) => a.name.localeCompare(b.name));\n return entries;\n}\n\nexport function registerHostFsRoutes(authenticated: Hono, _deps: AuthenticatedRouteDeps): void {\n authenticated.get('/api/host/fs/meta', (c) => {\n return c.json({\n ok: true,\n payload: {\n hostname: os.hostname(),\n platform: process.platform,\n pathSeparator: path.sep,\n },\n });\n });\n\n /**\n * GET /api/host/fs/list?path=\n * - Omit or empty `path`: root — `/` on POSIX; drive letters on Windows.\n * - Otherwise: absolute path on the gateway host (URL-encoded).\n */\n authenticated.get('/api/host/fs/list', async (c) => {\n const raw = c.req.query('path');\n const trimmed = typeof raw === 'string' ? raw.trim() : '';\n\n if (!trimmed) {\n if (process.platform === 'win32') {\n try {\n const entries = await listWindowsDrives();\n return c.json({\n ok: true,\n payload: {\n currentPath: '',\n parentPath: null,\n entries,\n },\n });\n } catch (err) {\n log.warn({ err }, 'host fs list drives failed');\n return jsonError(500, 'Failed to list drives');\n }\n }\n\n try {\n const root = await realpath('/');\n const dirents = await readdir(root, { withFileTypes: true });\n const entries: { name: string; absolutePath: string; isDirectory: boolean }[] = [];\n for (const e of dirents) {\n if (skipDotName(e.name)) continue;\n const fullPath = path.join(root, e.name);\n if (e.isDirectory()) {\n entries.push({ name: e.name, absolutePath: fullPath, isDirectory: true });\n } else {\n entries.push({ name: e.name, absolutePath: fullPath, isDirectory: false });\n }\n }\n entries.sort((a, b) => {\n if (a.isDirectory !== b.isDirectory) return a.isDirectory ? -1 : 1;\n return a.name.localeCompare(b.name);\n });\n return c.json({\n ok: true,\n payload: {\n currentPath: root,\n parentPath: null,\n entries,\n },\n });\n } catch (err) {\n log.warn({ err }, 'host fs list root failed');\n const msg = err instanceof Error ? err.message : String(err);\n return jsonError(500, msg || 'Failed to read root');\n }\n }\n\n let resolved: string;\n try {\n const normalized = path.normalize(trimmed);\n resolved = await realpath(normalized);\n } catch (err) {\n log.warn({ err, path: trimmed }, 'host fs realpath failed');\n return jsonError(404, 'Path not found');\n }\n\n let st;\n try {\n st = await stat(resolved);\n } catch (err) {\n log.warn({ err, path: resolved }, 'host fs stat failed');\n return jsonError(404, 'Path not found');\n }\n\n if (!st.isDirectory()) {\n return jsonError(400, 'Not a directory');\n }\n\n const parentPath = parentDirectory(resolved);\n\n try {\n const dirents = await readdir(resolved, { withFileTypes: true });\n const entries: { name: string; absolutePath: string; isDirectory: boolean }[] = [];\n for (const e of dirents) {\n if (skipDotName(e.name)) continue;\n const fullPath = path.join(resolved, e.name);\n entries.push({\n name: e.name,\n absolutePath: fullPath,\n isDirectory: e.isDirectory(),\n });\n }\n entries.sort((a, b) => {\n if (a.isDirectory !== b.isDirectory) return a.isDirectory ? -1 : 1;\n return a.name.localeCompare(b.name);\n });\n return c.json({\n ok: true,\n payload: {\n currentPath: resolved,\n parentPath,\n entries,\n },\n });\n } catch (err) {\n log.warn({ err, path: resolved }, 'host fs readdir failed');\n const msg = err instanceof Error ? err.message : String(err);\n if ((err as NodeJS.ErrnoException)?.code === 'EACCES') {\n return jsonError(403, 'Permission denied');\n }\n return jsonError(500, msg || 'Failed to read directory');\n }\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"host-fs.js","names":["path","os"],"sources":["../../../../../src/gateway/hono/routes/host-fs.ts"],"sourcesContent":["/**\n * Authenticated host filesystem browse API for Web UI (e.g. session working directory).\n * Lists directories the gateway process can read — intended for trusted operators only.\n */\nimport type { Hono } from 'hono';\nimport { readdir, realpath, stat } from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\n\nimport { createGatewayRouteLogger } from '../lib/route-logger.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nconst log = createGatewayRouteLogger('HostFs');\n\nfunction jsonError(status: number, message: string) {\n return Response.json({ ok: false, error: { message } }, { status });\n}\n\nfunction skipDotName(name: string): boolean {\n return name.startsWith('.');\n}\n\n/** Windows: true if `p` is a drive root like `C:\\`. */\nfunction isWindowsDriveRoot(p: string): boolean {\n return /^[A-Za-z]:\\\\?$/.test(path.normalize(p).replace(/\\\\$/, '\\\\'));\n}\n\nfunction parentDirectory(absNormalized: string): string | null {\n if (process.platform === 'win32') {\n const n = path.normalize(absNormalized);\n if (isWindowsDriveRoot(n)) {\n return null;\n }\n const d = path.dirname(n);\n if (d === n) return null;\n return d;\n }\n const n = path.normalize(absNormalized);\n if (n === '/' || n === path.parse(n).root) {\n return null;\n }\n const d = path.dirname(n);\n if (d === n) return null;\n return d;\n}\n\nasync function listWindowsDrives(): Promise<\n { name: string; absolutePath: string; isDirectory: boolean }[]\n> {\n const entries: { name: string; absolutePath: string; isDirectory: boolean }[] = [];\n for (let i = 0; i < 26; i++) {\n const letter = String.fromCharCode(65 + i);\n const root = `${letter}:\\\\`;\n try {\n await stat(root);\n entries.push({ name: root, absolutePath: root, isDirectory: true });\n } catch {\n /* not mounted */\n }\n }\n entries.sort((a, b) => a.name.localeCompare(b.name));\n return entries;\n}\n\nexport function registerHostFsRoutes(authenticated: Hono, _deps: AuthenticatedRouteDeps): void {\n authenticated.get('/api/host/fs/meta', (c) => {\n return c.json({\n ok: true,\n payload: {\n hostname: os.hostname(),\n platform: process.platform,\n pathSeparator: path.sep,\n },\n });\n });\n\n /**\n * GET /api/host/fs/list?path=\n * - Omit or empty `path`: root — `/` on POSIX; drive letters on Windows.\n * - Otherwise: absolute path on the gateway host (URL-encoded).\n */\n authenticated.get('/api/host/fs/list', async (c) => {\n const raw = c.req.query('path');\n const trimmed = typeof raw === 'string' ? raw.trim() : '';\n\n if (!trimmed) {\n if (process.platform === 'win32') {\n try {\n const entries = await listWindowsDrives();\n return c.json({\n ok: true,\n payload: {\n currentPath: '',\n parentPath: null,\n entries,\n },\n });\n } catch (err) {\n log.warn({ err }, 'host fs list drives failed');\n return jsonError(500, 'Failed to list drives');\n }\n }\n\n try {\n const root = await realpath('/');\n const dirents = await readdir(root, { withFileTypes: true });\n const entries: { name: string; absolutePath: string; isDirectory: boolean }[] = [];\n for (const e of dirents) {\n if (skipDotName(e.name)) continue;\n const fullPath = path.join(root, e.name);\n if (e.isDirectory()) {\n entries.push({ name: e.name, absolutePath: fullPath, isDirectory: true });\n } else {\n entries.push({ name: e.name, absolutePath: fullPath, isDirectory: false });\n }\n }\n entries.sort((a, b) => {\n if (a.isDirectory !== b.isDirectory) return a.isDirectory ? -1 : 1;\n return a.name.localeCompare(b.name);\n });\n return c.json({\n ok: true,\n payload: {\n currentPath: root,\n parentPath: null,\n entries,\n },\n });\n } catch (err) {\n log.warn({ err }, 'host fs list root failed');\n const msg = err instanceof Error ? err.message : String(err);\n return jsonError(500, msg || 'Failed to read root');\n }\n }\n\n let resolved: string;\n try {\n const normalized = path.normalize(trimmed);\n resolved = await realpath(normalized);\n } catch (err) {\n log.warn({ err, path: trimmed }, 'host fs realpath failed');\n return jsonError(404, 'Path not found');\n }\n\n let st;\n try {\n st = await stat(resolved);\n } catch (err) {\n log.warn({ err, path: resolved }, 'host fs stat failed');\n return jsonError(404, 'Path not found');\n }\n\n if (!st.isDirectory()) {\n return jsonError(400, 'Not a directory');\n }\n\n const parentPath = parentDirectory(resolved);\n\n try {\n const dirents = await readdir(resolved, { withFileTypes: true });\n const entries: { name: string; absolutePath: string; isDirectory: boolean }[] = [];\n for (const e of dirents) {\n if (skipDotName(e.name)) continue;\n const fullPath = path.join(resolved, e.name);\n entries.push({\n name: e.name,\n absolutePath: fullPath,\n isDirectory: e.isDirectory(),\n });\n }\n entries.sort((a, b) => {\n if (a.isDirectory !== b.isDirectory) return a.isDirectory ? -1 : 1;\n return a.name.localeCompare(b.name);\n });\n return c.json({\n ok: true,\n payload: {\n currentPath: resolved,\n parentPath,\n entries,\n },\n });\n } catch (err) {\n log.warn({ err, path: resolved }, 'host fs readdir failed');\n const msg = err instanceof Error ? err.message : String(err);\n if ((err as NodeJS.ErrnoException)?.code === 'EACCES') {\n return jsonError(403, 'Permission denied');\n }\n return jsonError(500, msg || 'Failed to read directory');\n }\n });\n}\n"],"mappings":"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"}
@@ -45,7 +45,12 @@ const AUTHENTICATED_LAZY_ROUTE_BUNDLES = [
45
45
  },
46
46
  {
47
47
  id: "config",
48
- match: (path) => startsWithAny(path, ["/api/config", "/api/heartbeat/trigger"]),
48
+ match: (path) => startsWithAny(path, [
49
+ "/api/config",
50
+ "/api/heartbeat/trigger",
51
+ "/api/gateway/reveal-auth-secret",
52
+ "/api/tools/web/reveal-search-api-key"
53
+ ]),
49
54
  load: async () => {
50
55
  const { registerConfigRoutes } = await import("./config.js");
51
56
  return { register: registerConfigRoutes };
@@ -221,6 +226,14 @@ const AUTHENTICATED_LAZY_ROUTE_BUNDLES = [
221
226
  const { registerConnectorRoutes } = await import("./connectors.js");
222
227
  return { register: registerConnectorRoutes };
223
228
  }
229
+ },
230
+ {
231
+ id: "mcp",
232
+ match: (path) => startsWithAny(path, ["/api/mcp"]),
233
+ load: async () => {
234
+ const { registerMcpRoutes } = await import("./mcp.js");
235
+ return { register: registerMcpRoutes };
236
+ }
224
237
  }
225
238
  ];
226
239
  const APP_LAZY_ROUTE_BUNDLES = [{
@@ -1 +1 @@
1
- {"version":3,"file":"lazy-bundles.js","names":[],"sources":["../../../../../src/gateway/hono/routes/lazy-bundles.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport type { GatewayService } from '../../service.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nexport type AuthenticatedLazyRouteBundle = {\n id: string;\n match: (path: string) => boolean;\n load: () => Promise<{ register: (authenticated: Hono, deps: AuthenticatedRouteDeps) => void }>;\n};\n\nexport type AppLazyRouteBundle = {\n id: string;\n prefixes: readonly string[];\n match: (path: string) => boolean;\n load: () => Promise<{\n registerOnApp: (app: Hono, service: GatewayService) => void;\n }>;\n};\n\nfunction startsWithAny(path: string, prefixes: readonly string[]): boolean {\n return prefixes.some((prefix) => path === prefix || path.startsWith(`${prefix}/`));\n}\n\nexport const AUTHENTICATED_LAZY_ROUTE_BUNDLES: readonly AuthenticatedLazyRouteBundle[] = [\n {\n id: 'workspace',\n match: (path) => startsWithAny(path, ['/api/workspace']),\n load: async () => {\n const { registerWorkspaceRoutes } = await import('./workspace.js');\n return { register: registerWorkspaceRoutes };\n },\n },\n {\n id: 'host-fs',\n match: (path) => startsWithAny(path, ['/api/host/fs']),\n load: async () => {\n const { registerHostFsRoutes } = await import('./host-fs.js');\n return { register: registerHostFsRoutes };\n },\n },\n {\n id: 'channels',\n match: (path) => startsWithAny(path, ['/api/channels']),\n load: async () => {\n const { registerChannelRoutes } = await import('./channels.js');\n return { register: registerChannelRoutes };\n },\n },\n {\n id: 'browser-install',\n match: (path) =>\n path === '/api/browser/playwright/install/stream' ||\n path === '/api/browser/cloakbrowser/install/stream',\n load: async () => {\n const { registerBrowserInstallRoutes } = await import('./browser-install.js');\n return { register: registerBrowserInstallRoutes };\n },\n },\n {\n id: 'browser',\n // `browser-install` above already matched the SSE install streams; this\n // catches the remaining /api/browser/* handlers (extension, cdp,\n // cloakbrowser doctor/launch/install, playwright doctor/install, cloud).\n match: (path) => startsWithAny(path, ['/api/browser']),\n load: async () => {\n const { registerBrowserRoutes } = await import('./browser.js');\n return { register: registerBrowserRoutes };\n },\n },\n {\n id: 'config',\n match: (path) =>\n startsWithAny(path, ['/api/config', '/api/heartbeat/trigger']),\n load: async () => {\n const { registerConfigRoutes } = await import('./config.js');\n return { register: registerConfigRoutes };\n },\n },\n {\n id: 'doctor',\n match: (path) => startsWithAny(path, ['/api/doctor']),\n load: async () => {\n const { registerDoctorRoutes } = await import('./doctor.js');\n return { register: registerDoctorRoutes };\n },\n },\n {\n id: 'dreaming',\n match: (path) => startsWithAny(path, ['/api/dreaming']),\n load: async () => {\n const { registerDreamingRoutes } = await import('./dreaming.js');\n return { register: registerDreamingRoutes };\n },\n },\n {\n id: 'agents',\n match: (path) => startsWithAny(path, ['/api/agents', '/api/voice/models']),\n load: async () => {\n const { registerAgentsRoutes } = await import('./agents.js');\n return { register: registerAgentsRoutes };\n },\n },\n {\n id: 'auth-registry-extensions',\n match: (path) =>\n startsWithAny(path, [\n '/api/auth',\n '/api/registry',\n '/api/extensions',\n '/api/context',\n '/api/marketplace',\n ]),\n load: async () => {\n const { registerAuthRegistryExtensionsRoutes } = await import('./auth-registry-extensions.js');\n return { register: registerAuthRegistryExtensionsRoutes };\n },\n },\n {\n id: 'models',\n match: (path) =>\n startsWithAny(path, ['/api/models', '/api/models-json', '/api/providers', '/api/image']),\n load: async () => {\n const { registerModelsRoutes } = await import('./models.js');\n return { register: registerModelsRoutes };\n },\n },\n {\n id: 'commands-skills',\n match: (path) => startsWithAny(path, ['/api/commands', '/api/skills']),\n load: async () => {\n const { registerCommandsSkillsRoutes } = await import('./commands-skills.js');\n return { register: registerCommandsSkillsRoutes };\n },\n },\n {\n id: 'cron',\n match: (path) => startsWithAny(path, ['/api/cron']),\n load: async () => {\n const { registerCronRoutes } = await import('./cron.js');\n return { register: registerCronRoutes };\n },\n },\n {\n id: 'goals',\n match: (path) => startsWithAny(path, ['/api/goals']),\n load: async () => {\n const { registerGoalsRoutes } = await import('./goals.js');\n return { register: registerGoalsRoutes };\n },\n },\n {\n id: 'notes',\n match: (path) => startsWithAny(path, ['/api/notes']),\n load: async () => {\n const { registerNotesRoutes } = await import('./notes.js');\n return { register: registerNotesRoutes };\n },\n },\n {\n id: 'home',\n match: (path) => startsWithAny(path, ['/api/home']),\n load: async () => {\n const { registerHomeRoutes } = await import('./home.js');\n return { register: registerHomeRoutes };\n },\n },\n {\n id: 'workflows',\n match: (path) => startsWithAny(path, ['/api/workflows']),\n load: async () => {\n const { registerWorkflowRoutes } = await import('./workflows.js');\n return { register: registerWorkflowRoutes };\n },\n },\n {\n id: 'logs',\n match: (path) => startsWithAny(path, ['/api/logs']),\n load: async () => {\n const { registerLogsRoutes } = await import('./logs.js');\n return { register: registerLogsRoutes };\n },\n },\n {\n id: 'shares',\n match: (path) => startsWithAny(path, ['/api/shares']),\n load: async () => {\n const { registerShareRoutes } = await import('./shares.js');\n return { register: registerShareRoutes };\n },\n },\n {\n id: 'site-shares',\n match: (path) => startsWithAny(path, ['/api/site-shares']),\n load: async () => {\n const { registerSiteShareRoutes } = await import('./site-shares.js');\n return { register: registerSiteShareRoutes };\n },\n },\n {\n id: 'tunnel',\n match: (path) => startsWithAny(path, ['/api/tunnel']),\n load: async () => {\n const { registerTunnelRoutes } = await import('./tunnel.js');\n return { register: registerTunnelRoutes };\n },\n },\n {\n id: 'exposure',\n match: (path) => startsWithAny(path, ['/api/exposure']),\n load: async () => {\n const { registerExposureRoutes } = await import('./exposure.js');\n return { register: registerExposureRoutes };\n },\n },\n {\n id: 'extension-gateway',\n match: (path) => startsWithAny(path, ['/api/gateway']),\n load: async () => {\n const { registerExtensionGatewayRoutes } = await import('./extension-gateway.js');\n return { register: registerExtensionGatewayRoutes };\n },\n },\n {\n id: 'update',\n match: (path) => startsWithAny(path, ['/api/update']),\n load: async () => {\n const { registerUpdateRoutes } = await import('./update.js');\n return { register: registerUpdateRoutes };\n },\n },\n {\n id: 'voice',\n match: (path) => startsWithAny(path, ['/api/voice']) && path !== '/api/voice/models',\n load: async () => {\n const { registerVoiceRoutes } = await import('./voice.js');\n return { register: registerVoiceRoutes };\n },\n },\n {\n id: 'connectors',\n match: (path) => startsWithAny(path, ['/api/connectors']),\n load: async () => {\n const { registerConnectorRoutes } = await import('./connectors.js');\n return { register: registerConnectorRoutes };\n },\n },\n];\n\nexport const APP_LAZY_ROUTE_BUNDLES: readonly AppLazyRouteBundle[] = [\n {\n id: 'shares-public',\n prefixes: ['/s'],\n match: (path) => startsWithAny(path, ['/s']),\n load: async () => {\n const { registerSharePublicRoutes } = await import('./shares.js');\n return { registerOnApp: registerSharePublicRoutes };\n },\n },\n {\n id: 'tunnel-public',\n prefixes: [\n '/api/tunnel/pair/ping',\n '/api/tunnel/pair/validate-url',\n '/api/tunnel/exchange-token',\n ],\n match: (path) =>\n path === '/api/tunnel/exchange-token' ||\n path === '/api/tunnel/pair/ping' ||\n path === '/api/tunnel/pair/validate-url',\n load: async () => {\n const { registerTunnelPublicRoutes } = await import('./tunnel.js');\n return { registerOnApp: registerTunnelPublicRoutes };\n },\n },\n];\n\nexport function findAuthenticatedLazyRouteBundle(path: string): AuthenticatedLazyRouteBundle | undefined {\n return AUTHENTICATED_LAZY_ROUTE_BUNDLES.find((bundle) => bundle.match(path));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"lazy-bundles.js","names":[],"sources":["../../../../../src/gateway/hono/routes/lazy-bundles.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport type { GatewayService } from '../../service.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nexport type AuthenticatedLazyRouteBundle = {\n id: string;\n match: (path: string) => boolean;\n load: () => Promise<{ register: (authenticated: Hono, deps: AuthenticatedRouteDeps) => void }>;\n};\n\nexport type AppLazyRouteBundle = {\n id: string;\n prefixes: readonly string[];\n match: (path: string) => boolean;\n load: () => Promise<{\n registerOnApp: (app: Hono, service: GatewayService) => void;\n }>;\n};\n\nfunction startsWithAny(path: string, prefixes: readonly string[]): boolean {\n return prefixes.some((prefix) => path === prefix || path.startsWith(`${prefix}/`));\n}\n\nexport const AUTHENTICATED_LAZY_ROUTE_BUNDLES: readonly AuthenticatedLazyRouteBundle[] = [\n {\n id: 'workspace',\n match: (path) => startsWithAny(path, ['/api/workspace']),\n load: async () => {\n const { registerWorkspaceRoutes } = await import('./workspace.js');\n return { register: registerWorkspaceRoutes };\n },\n },\n {\n id: 'host-fs',\n match: (path) => startsWithAny(path, ['/api/host/fs']),\n load: async () => {\n const { registerHostFsRoutes } = await import('./host-fs.js');\n return { register: registerHostFsRoutes };\n },\n },\n {\n id: 'channels',\n match: (path) => startsWithAny(path, ['/api/channels']),\n load: async () => {\n const { registerChannelRoutes } = await import('./channels.js');\n return { register: registerChannelRoutes };\n },\n },\n {\n id: 'browser-install',\n match: (path) =>\n path === '/api/browser/playwright/install/stream' ||\n path === '/api/browser/cloakbrowser/install/stream',\n load: async () => {\n const { registerBrowserInstallRoutes } = await import('./browser-install.js');\n return { register: registerBrowserInstallRoutes };\n },\n },\n {\n id: 'browser',\n // `browser-install` above already matched the SSE install streams; this\n // catches the remaining /api/browser/* handlers (extension, cdp,\n // cloakbrowser doctor/launch/install, playwright doctor/install, cloud).\n match: (path) => startsWithAny(path, ['/api/browser']),\n load: async () => {\n const { registerBrowserRoutes } = await import('./browser.js');\n return { register: registerBrowserRoutes };\n },\n },\n {\n id: 'config',\n match: (path) =>\n startsWithAny(path, [\n '/api/config',\n '/api/heartbeat/trigger',\n // Secret reveal handlers live in config routes but use /api/gateway and\n // /api/tools paths; without these prefixes the extension-gateway bundle\n // matches first and returns 404 (no handler for multi-segment paths).\n '/api/gateway/reveal-auth-secret',\n '/api/tools/web/reveal-search-api-key',\n ]),\n load: async () => {\n const { registerConfigRoutes } = await import('./config.js');\n return { register: registerConfigRoutes };\n },\n },\n {\n id: 'doctor',\n match: (path) => startsWithAny(path, ['/api/doctor']),\n load: async () => {\n const { registerDoctorRoutes } = await import('./doctor.js');\n return { register: registerDoctorRoutes };\n },\n },\n {\n id: 'dreaming',\n match: (path) => startsWithAny(path, ['/api/dreaming']),\n load: async () => {\n const { registerDreamingRoutes } = await import('./dreaming.js');\n return { register: registerDreamingRoutes };\n },\n },\n {\n id: 'agents',\n match: (path) => startsWithAny(path, ['/api/agents', '/api/voice/models']),\n load: async () => {\n const { registerAgentsRoutes } = await import('./agents.js');\n return { register: registerAgentsRoutes };\n },\n },\n {\n id: 'auth-registry-extensions',\n match: (path) =>\n startsWithAny(path, [\n '/api/auth',\n '/api/registry',\n '/api/extensions',\n '/api/context',\n '/api/marketplace',\n ]),\n load: async () => {\n const { registerAuthRegistryExtensionsRoutes } = await import('./auth-registry-extensions.js');\n return { register: registerAuthRegistryExtensionsRoutes };\n },\n },\n {\n id: 'models',\n match: (path) =>\n startsWithAny(path, ['/api/models', '/api/models-json', '/api/providers', '/api/image']),\n load: async () => {\n const { registerModelsRoutes } = await import('./models.js');\n return { register: registerModelsRoutes };\n },\n },\n {\n id: 'commands-skills',\n match: (path) => startsWithAny(path, ['/api/commands', '/api/skills']),\n load: async () => {\n const { registerCommandsSkillsRoutes } = await import('./commands-skills.js');\n return { register: registerCommandsSkillsRoutes };\n },\n },\n {\n id: 'cron',\n match: (path) => startsWithAny(path, ['/api/cron']),\n load: async () => {\n const { registerCronRoutes } = await import('./cron.js');\n return { register: registerCronRoutes };\n },\n },\n {\n id: 'goals',\n match: (path) => startsWithAny(path, ['/api/goals']),\n load: async () => {\n const { registerGoalsRoutes } = await import('./goals.js');\n return { register: registerGoalsRoutes };\n },\n },\n {\n id: 'notes',\n match: (path) => startsWithAny(path, ['/api/notes']),\n load: async () => {\n const { registerNotesRoutes } = await import('./notes.js');\n return { register: registerNotesRoutes };\n },\n },\n {\n id: 'home',\n match: (path) => startsWithAny(path, ['/api/home']),\n load: async () => {\n const { registerHomeRoutes } = await import('./home.js');\n return { register: registerHomeRoutes };\n },\n },\n {\n id: 'workflows',\n match: (path) => startsWithAny(path, ['/api/workflows']),\n load: async () => {\n const { registerWorkflowRoutes } = await import('./workflows.js');\n return { register: registerWorkflowRoutes };\n },\n },\n {\n id: 'logs',\n match: (path) => startsWithAny(path, ['/api/logs']),\n load: async () => {\n const { registerLogsRoutes } = await import('./logs.js');\n return { register: registerLogsRoutes };\n },\n },\n {\n id: 'shares',\n match: (path) => startsWithAny(path, ['/api/shares']),\n load: async () => {\n const { registerShareRoutes } = await import('./shares.js');\n return { register: registerShareRoutes };\n },\n },\n {\n id: 'site-shares',\n match: (path) => startsWithAny(path, ['/api/site-shares']),\n load: async () => {\n const { registerSiteShareRoutes } = await import('./site-shares.js');\n return { register: registerSiteShareRoutes };\n },\n },\n {\n id: 'tunnel',\n match: (path) => startsWithAny(path, ['/api/tunnel']),\n load: async () => {\n const { registerTunnelRoutes } = await import('./tunnel.js');\n return { register: registerTunnelRoutes };\n },\n },\n {\n id: 'exposure',\n match: (path) => startsWithAny(path, ['/api/exposure']),\n load: async () => {\n const { registerExposureRoutes } = await import('./exposure.js');\n return { register: registerExposureRoutes };\n },\n },\n {\n id: 'extension-gateway',\n match: (path) => startsWithAny(path, ['/api/gateway']),\n load: async () => {\n const { registerExtensionGatewayRoutes } = await import('./extension-gateway.js');\n return { register: registerExtensionGatewayRoutes };\n },\n },\n {\n id: 'update',\n match: (path) => startsWithAny(path, ['/api/update']),\n load: async () => {\n const { registerUpdateRoutes } = await import('./update.js');\n return { register: registerUpdateRoutes };\n },\n },\n {\n id: 'voice',\n match: (path) => startsWithAny(path, ['/api/voice']) && path !== '/api/voice/models',\n load: async () => {\n const { registerVoiceRoutes } = await import('./voice.js');\n return { register: registerVoiceRoutes };\n },\n },\n {\n id: 'connectors',\n match: (path) => startsWithAny(path, ['/api/connectors']),\n load: async () => {\n const { registerConnectorRoutes } = await import('./connectors.js');\n return { register: registerConnectorRoutes };\n },\n },\n {\n id: 'mcp',\n match: (path) => startsWithAny(path, ['/api/mcp']),\n load: async () => {\n const { registerMcpRoutes } = await import('./mcp.js');\n return { register: registerMcpRoutes };\n },\n },\n];\n\nexport const APP_LAZY_ROUTE_BUNDLES: readonly AppLazyRouteBundle[] = [\n {\n id: 'shares-public',\n prefixes: ['/s'],\n match: (path) => startsWithAny(path, ['/s']),\n load: async () => {\n const { registerSharePublicRoutes } = await import('./shares.js');\n return { registerOnApp: registerSharePublicRoutes };\n },\n },\n {\n id: 'tunnel-public',\n prefixes: [\n '/api/tunnel/pair/ping',\n '/api/tunnel/pair/validate-url',\n '/api/tunnel/exchange-token',\n ],\n match: (path) =>\n path === '/api/tunnel/exchange-token' ||\n path === '/api/tunnel/pair/ping' ||\n path === '/api/tunnel/pair/validate-url',\n load: async () => {\n const { registerTunnelPublicRoutes } = await import('./tunnel.js');\n return { registerOnApp: registerTunnelPublicRoutes };\n },\n },\n];\n\nexport function findAuthenticatedLazyRouteBundle(path: string): AuthenticatedLazyRouteBundle | undefined {\n return AUTHENTICATED_LAZY_ROUTE_BUNDLES.find((bundle) => bundle.match(path));\n}\n"],"mappings":"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"}
@@ -1,6 +1,8 @@
1
+ import { createGatewayRouteLogger, logRouteError } from "../lib/route-logger.js";
1
2
  import { APP_LAZY_ROUTE_BUNDLES, AUTHENTICATED_LAZY_ROUTE_BUNDLES, findAuthenticatedLazyRouteBundle } from "./lazy-bundles.js";
2
3
  import { Hono } from "hono";
3
4
  //#region src/gateway/hono/routes/lazy-fallback.ts
5
+ const log = createGatewayRouteLogger("LazyRoutes");
4
6
  const authenticatedSubApps = /* @__PURE__ */ new Map();
5
7
  const appSubApps = /* @__PURE__ */ new Map();
6
8
  const authenticatedLoadPromises = /* @__PURE__ */ new Map();
@@ -60,6 +62,7 @@ async function forwardToSubApp(c, sub) {
60
62
  return await sub.fetch(c.req.raw, c.env, c.executionCtx);
61
63
  } catch (error) {
62
64
  if (error instanceof Error && error.message.includes("ExecutionContext")) return sub.fetch(c.req.raw, c.env);
65
+ logRouteError(log, c, error, "gateway.route.lazy_forward");
63
66
  throw error;
64
67
  }
65
68
  }
@@ -1 +1 @@
1
- {"version":3,"file":"lazy-fallback.js","names":[],"sources":["../../../../../src/gateway/hono/routes/lazy-fallback.ts"],"sourcesContent":["import { Hono, type Context } from 'hono';\n\nimport type { GatewayService } from '../../service.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\nimport {\n APP_LAZY_ROUTE_BUNDLES,\n AUTHENTICATED_LAZY_ROUTE_BUNDLES,\n findAuthenticatedLazyRouteBundle,\n} from './lazy-bundles.js';\n\nconst authenticatedSubApps = new Map<string, Hono>();\nconst appSubApps = new Map<string, Hono>();\nconst authenticatedLoadPromises = new Map<string, Promise<Hono>>();\nconst appLoadPromises = new Map<string, Promise<Hono>>();\n\nexport function getLoadedLazyRouteBundleIdsForTests(): {\n authenticated: string[];\n app: string[];\n} {\n return {\n authenticated: [...authenticatedSubApps.keys()],\n app: [...appSubApps.keys()],\n };\n}\n\nexport function resetLazyRouteBundlesForTests(): void {\n authenticatedSubApps.clear();\n appSubApps.clear();\n authenticatedLoadPromises.clear();\n appLoadPromises.clear();\n}\n\nasync function ensureAuthenticatedLazyBundle(\n bundleId: string,\n deps: AuthenticatedRouteDeps,\n): Promise<Hono | null> {\n const existing = authenticatedSubApps.get(bundleId);\n if (existing) {\n return existing;\n }\n\n let pending = authenticatedLoadPromises.get(bundleId);\n if (!pending) {\n const bundle = AUTHENTICATED_LAZY_ROUTE_BUNDLES.find((entry) => entry.id === bundleId);\n if (!bundle) {\n return null;\n }\n pending = (async () => {\n const mod = await bundle.load();\n const sub = new Hono();\n mod.register(sub, deps);\n authenticatedSubApps.set(bundleId, sub);\n authenticatedLoadPromises.delete(bundleId);\n return sub;\n })();\n authenticatedLoadPromises.set(bundleId, pending);\n }\n\n return pending;\n}\n\nasync function ensureAppLazyBundle(\n bundleId: string,\n params: { service: GatewayService; deps: AuthenticatedRouteDeps },\n): Promise<Hono | null> {\n const existing = appSubApps.get(bundleId);\n if (existing) {\n return existing;\n }\n\n let pending = appLoadPromises.get(bundleId);\n if (!pending) {\n const bundle = APP_LAZY_ROUTE_BUNDLES.find((entry) => entry.id === bundleId);\n if (!bundle) {\n return null;\n }\n pending = (async () => {\n const mod = await bundle.load();\n const sub = new Hono();\n if (mod.registerOnApp) {\n mod.registerOnApp(sub, params.service);\n }\n appSubApps.set(bundleId, sub);\n appLoadPromises.delete(bundleId);\n return sub;\n })();\n appLoadPromises.set(bundleId, pending);\n }\n\n return pending;\n}\n\nasync function forwardToSubApp(c: Context, sub: Hono): Promise<Response> {\n try {\n return await sub.fetch(c.req.raw, c.env, c.executionCtx);\n } catch (error) {\n if (error instanceof Error && error.message.includes('ExecutionContext')) {\n return sub.fetch(c.req.raw, c.env);\n }\n throw error;\n }\n}\n\nexport function registerAuthenticatedLazyRouteFallback(\n authenticated: Hono,\n deps: AuthenticatedRouteDeps,\n): void {\n authenticated.all('*', async (c) => {\n const bundle = findAuthenticatedLazyRouteBundle(c.req.path);\n if (!bundle) {\n return c.json({ error: 'Not found' }, 404);\n }\n const sub = await ensureAuthenticatedLazyBundle(bundle.id, deps);\n if (!sub) {\n return c.json({ error: 'Not found' }, 404);\n }\n return forwardToSubApp(c, sub);\n });\n}\n\nexport function mountAppLazyRoutePrefixes(\n app: Hono,\n params: { service: GatewayService; deps: AuthenticatedRouteDeps },\n): void {\n for (const bundle of APP_LAZY_ROUTE_BUNDLES) {\n const handler = async (c: Context) => {\n const sub = await ensureAppLazyBundle(bundle.id, params);\n if (!sub) {\n return c.json({ error: 'Not found' }, 404);\n }\n return forwardToSubApp(c, sub);\n };\n\n for (const prefix of bundle.prefixes) {\n app.all(prefix, handler);\n app.all(`${prefix}/*`, handler);\n }\n }\n}\n"],"mappings":";;;AAUA,MAAM,uCAAuB,IAAI,KAAmB;AACpD,MAAM,6BAAa,IAAI,KAAmB;AAC1C,MAAM,4CAA4B,IAAI,KAA4B;AAClE,MAAM,kCAAkB,IAAI,KAA4B;AAExD,SAAgB,sCAGd;AACA,QAAO;EACL,eAAe,CAAC,GAAG,qBAAqB,MAAM,CAAC;EAC/C,KAAK,CAAC,GAAG,WAAW,MAAM,CAAC;EAC5B;;AAGH,SAAgB,gCAAsC;AACpD,sBAAqB,OAAO;AAC5B,YAAW,OAAO;AAClB,2BAA0B,OAAO;AACjC,iBAAgB,OAAO;;AAGzB,eAAe,8BACb,UACA,MACsB;CACtB,MAAM,WAAW,qBAAqB,IAAI,SAAS;AACnD,KAAI,SACF,QAAO;CAGT,IAAI,UAAU,0BAA0B,IAAI,SAAS;AACrD,KAAI,CAAC,SAAS;EACZ,MAAM,SAAS,iCAAiC,MAAM,UAAU,MAAM,OAAO,SAAS;AACtF,MAAI,CAAC,OACH,QAAO;AAET,aAAW,YAAY;GACrB,MAAM,MAAM,MAAM,OAAO,MAAM;GAC/B,MAAM,MAAM,IAAI,MAAM;AACtB,OAAI,SAAS,KAAK,KAAK;AACvB,wBAAqB,IAAI,UAAU,IAAI;AACvC,6BAA0B,OAAO,SAAS;AAC1C,UAAO;MACL;AACJ,4BAA0B,IAAI,UAAU,QAAQ;;AAGlD,QAAO;;AAGT,eAAe,oBACb,UACA,QACsB;CACtB,MAAM,WAAW,WAAW,IAAI,SAAS;AACzC,KAAI,SACF,QAAO;CAGT,IAAI,UAAU,gBAAgB,IAAI,SAAS;AAC3C,KAAI,CAAC,SAAS;EACZ,MAAM,SAAS,uBAAuB,MAAM,UAAU,MAAM,OAAO,SAAS;AAC5E,MAAI,CAAC,OACH,QAAO;AAET,aAAW,YAAY;GACrB,MAAM,MAAM,MAAM,OAAO,MAAM;GAC/B,MAAM,MAAM,IAAI,MAAM;AACtB,OAAI,IAAI,cACN,KAAI,cAAc,KAAK,OAAO,QAAQ;AAExC,cAAW,IAAI,UAAU,IAAI;AAC7B,mBAAgB,OAAO,SAAS;AAChC,UAAO;MACL;AACJ,kBAAgB,IAAI,UAAU,QAAQ;;AAGxC,QAAO;;AAGT,eAAe,gBAAgB,GAAY,KAA8B;AACvE,KAAI;AACF,SAAO,MAAM,IAAI,MAAM,EAAE,IAAI,KAAK,EAAE,KAAK,EAAE,aAAa;UACjD,OAAO;AACd,MAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,mBAAmB,CACtE,QAAO,IAAI,MAAM,EAAE,IAAI,KAAK,EAAE,IAAI;AAEpC,QAAM;;;AAIV,SAAgB,uCACd,eACA,MACM;AACN,eAAc,IAAI,KAAK,OAAO,MAAM;EAClC,MAAM,SAAS,iCAAiC,EAAE,IAAI,KAAK;AAC3D,MAAI,CAAC,OACH,QAAO,EAAE,KAAK,EAAE,OAAO,aAAa,EAAE,IAAI;EAE5C,MAAM,MAAM,MAAM,8BAA8B,OAAO,IAAI,KAAK;AAChE,MAAI,CAAC,IACH,QAAO,EAAE,KAAK,EAAE,OAAO,aAAa,EAAE,IAAI;AAE5C,SAAO,gBAAgB,GAAG,IAAI;GAC9B;;AAGJ,SAAgB,0BACd,KACA,QACM;AACN,MAAK,MAAM,UAAU,wBAAwB;EAC3C,MAAM,UAAU,OAAO,MAAe;GACpC,MAAM,MAAM,MAAM,oBAAoB,OAAO,IAAI,OAAO;AACxD,OAAI,CAAC,IACH,QAAO,EAAE,KAAK,EAAE,OAAO,aAAa,EAAE,IAAI;AAE5C,UAAO,gBAAgB,GAAG,IAAI;;AAGhC,OAAK,MAAM,UAAU,OAAO,UAAU;AACpC,OAAI,IAAI,QAAQ,QAAQ;AACxB,OAAI,IAAI,GAAG,OAAO,KAAK,QAAQ"}
1
+ {"version":3,"file":"lazy-fallback.js","names":[],"sources":["../../../../../src/gateway/hono/routes/lazy-fallback.ts"],"sourcesContent":["import { Hono, type Context } from 'hono';\n\nimport type { GatewayService } from '../../service.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\nimport {\n APP_LAZY_ROUTE_BUNDLES,\n AUTHENTICATED_LAZY_ROUTE_BUNDLES,\n findAuthenticatedLazyRouteBundle,\n} from './lazy-bundles.js';\nimport { createGatewayRouteLogger, logRouteError } from '../lib/route-logger.js';\n\nconst log = createGatewayRouteLogger('LazyRoutes');\n\nconst authenticatedSubApps = new Map<string, Hono>();\nconst appSubApps = new Map<string, Hono>();\nconst authenticatedLoadPromises = new Map<string, Promise<Hono>>();\nconst appLoadPromises = new Map<string, Promise<Hono>>();\n\nexport function getLoadedLazyRouteBundleIdsForTests(): {\n authenticated: string[];\n app: string[];\n} {\n return {\n authenticated: [...authenticatedSubApps.keys()],\n app: [...appSubApps.keys()],\n };\n}\n\nexport function resetLazyRouteBundlesForTests(): void {\n authenticatedSubApps.clear();\n appSubApps.clear();\n authenticatedLoadPromises.clear();\n appLoadPromises.clear();\n}\n\nasync function ensureAuthenticatedLazyBundle(\n bundleId: string,\n deps: AuthenticatedRouteDeps,\n): Promise<Hono | null> {\n const existing = authenticatedSubApps.get(bundleId);\n if (existing) {\n return existing;\n }\n\n let pending = authenticatedLoadPromises.get(bundleId);\n if (!pending) {\n const bundle = AUTHENTICATED_LAZY_ROUTE_BUNDLES.find((entry) => entry.id === bundleId);\n if (!bundle) {\n return null;\n }\n pending = (async () => {\n const mod = await bundle.load();\n const sub = new Hono();\n mod.register(sub, deps);\n authenticatedSubApps.set(bundleId, sub);\n authenticatedLoadPromises.delete(bundleId);\n return sub;\n })();\n authenticatedLoadPromises.set(bundleId, pending);\n }\n\n return pending;\n}\n\nasync function ensureAppLazyBundle(\n bundleId: string,\n params: { service: GatewayService; deps: AuthenticatedRouteDeps },\n): Promise<Hono | null> {\n const existing = appSubApps.get(bundleId);\n if (existing) {\n return existing;\n }\n\n let pending = appLoadPromises.get(bundleId);\n if (!pending) {\n const bundle = APP_LAZY_ROUTE_BUNDLES.find((entry) => entry.id === bundleId);\n if (!bundle) {\n return null;\n }\n pending = (async () => {\n const mod = await bundle.load();\n const sub = new Hono();\n if (mod.registerOnApp) {\n mod.registerOnApp(sub, params.service);\n }\n appSubApps.set(bundleId, sub);\n appLoadPromises.delete(bundleId);\n return sub;\n })();\n appLoadPromises.set(bundleId, pending);\n }\n\n return pending;\n}\n\nasync function forwardToSubApp(c: Context, sub: Hono): Promise<Response> {\n try {\n return await sub.fetch(c.req.raw, c.env, c.executionCtx);\n } catch (error) {\n if (error instanceof Error && error.message.includes('ExecutionContext')) {\n return sub.fetch(c.req.raw, c.env);\n }\n logRouteError(log, c, error, 'gateway.route.lazy_forward');\n throw error;\n }\n}\n\nexport function registerAuthenticatedLazyRouteFallback(\n authenticated: Hono,\n deps: AuthenticatedRouteDeps,\n): void {\n authenticated.all('*', async (c) => {\n const bundle = findAuthenticatedLazyRouteBundle(c.req.path);\n if (!bundle) {\n return c.json({ error: 'Not found' }, 404);\n }\n const sub = await ensureAuthenticatedLazyBundle(bundle.id, deps);\n if (!sub) {\n return c.json({ error: 'Not found' }, 404);\n }\n return forwardToSubApp(c, sub);\n });\n}\n\nexport function mountAppLazyRoutePrefixes(\n app: Hono,\n params: { service: GatewayService; deps: AuthenticatedRouteDeps },\n): void {\n for (const bundle of APP_LAZY_ROUTE_BUNDLES) {\n const handler = async (c: Context) => {\n const sub = await ensureAppLazyBundle(bundle.id, params);\n if (!sub) {\n return c.json({ error: 'Not found' }, 404);\n }\n return forwardToSubApp(c, sub);\n };\n\n for (const prefix of bundle.prefixes) {\n app.all(prefix, handler);\n app.all(`${prefix}/*`, handler);\n }\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,7 @@
1
- import { LOG_DIR, getLogFiles, getLogLevels, getLogModules, getLogStats, queryLogs } from "../../../utils/logger/log-store.js";
1
+ import { createGatewayRouteLogger } from "../lib/route-logger.js";
2
+ import { LOG_DIR, getFileLogStats, getLogErrorSummary, getLogFiles, getLogLevels, getLogModules, queryLogs } from "../../../utils/logger/log-store.js";
2
3
  //#region src/gateway/hono/routes/logs.ts
4
+ const log = createGatewayRouteLogger("Logs");
3
5
  function registerLogsRoutes(authenticated, _deps) {
4
6
  authenticated.get("/api/logs", async (c) => {
5
7
  const query = c.req.query();
@@ -9,6 +11,8 @@ function registerLogsRoutes(authenticated, _deps) {
9
11
  to: query.to,
10
12
  q: query.q,
11
13
  module: query.module,
14
+ requestId: query.requestId,
15
+ sessionId: query.sessionId,
12
16
  limit: query.limit ? parseInt(query.limit) : 100,
13
17
  offset: query.offset ? parseInt(query.offset) : 0
14
18
  });
@@ -22,8 +26,26 @@ function registerLogsRoutes(authenticated, _deps) {
22
26
  return c.json({ files });
23
27
  });
24
28
  authenticated.get("/api/logs/stats", async (c) => {
25
- const stats = getLogStats();
26
- return c.json(stats);
29
+ const { getRuntimeLogStats } = await import("../../../utils/logger.js");
30
+ const [fileStats, runtimeStats] = await Promise.all([getFileLogStats(), Promise.resolve(getRuntimeLogStats())]);
31
+ return c.json({
32
+ byLevel: fileStats.byLevel,
33
+ runtime: {
34
+ byLevel: runtimeStats.byLevel,
35
+ byModule: runtimeStats.byModule,
36
+ errorsLast24h: runtimeStats.errorsLast24h,
37
+ uptimeMs: runtimeStats.uptimeMs
38
+ }
39
+ });
40
+ });
41
+ authenticated.get("/api/logs/errors/summary", async (c) => {
42
+ const query = c.req.query();
43
+ const items = await getLogErrorSummary({
44
+ from: query.from,
45
+ to: query.to,
46
+ limit: query.limit ? parseInt(query.limit, 10) : 20
47
+ });
48
+ return c.json({ items });
27
49
  });
28
50
  authenticated.get("/api/logs/levels", async (c) => {
29
51
  return c.json({ levels: getLogLevels() });
@@ -36,9 +58,9 @@ function registerLogsRoutes(authenticated, _deps) {
36
58
  return c.json({ dir: LOG_DIR });
37
59
  });
38
60
  authenticated.get("/api/logs/health", async (c) => {
39
- const { getLogDir, getLogStats, isLoggerShuttingDown } = await import("../../../utils/logger.js");
61
+ const { getLogDir, getRuntimeLogStats, isLoggerShuttingDown } = await import("../../../utils/logger.js");
40
62
  const { getLogFiles } = await import("../../../utils/logger/log-store.js");
41
- const stats = getLogStats();
63
+ const stats = getRuntimeLogStats();
42
64
  const files = getLogFiles().slice(0, 5);
43
65
  const isShuttingDown = isLoggerShuttingDown();
44
66
  return c.json({
@@ -78,15 +100,25 @@ function registerLogsRoutes(authenticated, _deps) {
78
100
  setLogLevel(level);
79
101
  let autoRevertAt = null;
80
102
  if (duration) {
81
- const durationMs = parseInt(duration) * 6e4;
103
+ const durationMs = parseInt(duration, 10) * 6e4;
82
104
  if (!isNaN(durationMs) && durationMs > 0) {
83
105
  autoRevertAt = new Date(Date.now() + durationMs).toISOString();
84
106
  setTimeout(() => {
85
107
  setLogLevel(previousLevel);
86
- console.log(`[Logger] Auto-reverted log level to ${previousLevel}`);
108
+ log.info({
109
+ phase: "gateway.logs.level",
110
+ previousLevel,
111
+ reverted: true
112
+ }, `Log level auto-reverted to ${previousLevel}`);
87
113
  }, durationMs);
88
114
  }
89
115
  }
116
+ log.info({
117
+ phase: "gateway.logs.level",
118
+ previousLevel,
119
+ current: level,
120
+ autoRevertAt
121
+ }, `Log level changed to ${level}`);
90
122
  return c.json({
91
123
  previous: previousLevel,
92
124
  current: level,
@@ -1 +1 @@
1
- {"version":3,"file":"logs.js","names":[],"sources":["../../../../../src/gateway/hono/routes/logs.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport type { LogLevel } from '../../../utils/logger.js';\nimport { queryLogs, getLogFiles, getLogLevels, getLogStats, getLogModules, LOG_DIR } from '../../../utils/logger/log-store.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nexport function registerLogsRoutes(authenticated: Hono, _deps: AuthenticatedRouteDeps): void {\n\n // ========== Logs REST API (/api/logs) ==========\n\n // GET /api/logs - Query logs with filters\n authenticated.get('/api/logs', async (c) => {\n const query = c.req.query();\n const logs = await queryLogs({\n levels: query.level ? query.level.split(',') as LogLevel[] : undefined,\n from: query.from,\n to: query.to,\n q: query.q,\n module: query.module,\n limit: query.limit ? parseInt(query.limit) : 100,\n offset: query.offset ? parseInt(query.offset) : 0,\n });\n return c.json({ logs, count: logs.length });\n });\n\n // GET /api/logs/files - List log files\n authenticated.get('/api/logs/files', async (c) => {\n const files = getLogFiles();\n return c.json({ files });\n });\n\n // GET /api/logs/stats - Get log statistics\n authenticated.get('/api/logs/stats', async (c) => {\n const stats = getLogStats();\n return c.json(stats);\n });\n\n // GET /api/logs/levels - Get available log levels\n authenticated.get('/api/logs/levels', async (c) => {\n return c.json({ levels: getLogLevels() });\n });\n\n // GET /api/logs/modules - Get available modules\n authenticated.get('/api/logs/modules', async (c) => {\n const modules = await getLogModules();\n return c.json({ modules });\n });\n\n // GET /api/logs/dir - Get log directory path\n authenticated.get('/api/logs/dir', async (c) => {\n return c.json({ dir: LOG_DIR });\n });\n\n // GET /api/logs/health - Get log system health status\n authenticated.get('/api/logs/health', async (c) => {\n const { getLogDir, getLogStats, isLoggerShuttingDown } = await import('../../../utils/logger.js');\n const { getLogFiles } = await import('../../../utils/logger/log-store.js');\n \n const stats = getLogStats();\n const files = getLogFiles().slice(0, 5);\n const isShuttingDown = isLoggerShuttingDown();\n \n return c.json({\n status: isShuttingDown ? 'shutting_down' : 'healthy',\n config: {\n dir: getLogDir(),\n uptimeMs: stats.uptimeMs,\n },\n stats: {\n byLevel: stats.byLevel,\n errorsLast24h: stats.errorsLast24h,\n modulesTracked: stats.byModule ? Object.keys(stats.byModule).length : 0,\n },\n files: files.map(f => ({\n name: f.name,\n size: f.size,\n modified: f.modified,\n type: f.type,\n })),\n shuttingDown: isShuttingDown,\n });\n });\n\n // POST /api/logs/level - Set log level dynamically\n authenticated.post('/api/logs/level', async (c) => {\n const { setLogLevel, getLogLevel } = await import('../../../utils/logger.js');\n const body = await c.req.json().catch(() => ({}));\n const { level, duration } = body as { level?: string; duration?: string };\n \n if (!level) {\n return c.json({ error: 'level is required' }, 400);\n }\n \n const validLevels = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];\n if (!validLevels.includes(level)) {\n return c.json({ error: `Invalid level. Must be one of: ${validLevels.join(', ')}` }, 400);\n }\n \n const previousLevel = getLogLevel();\n setLogLevel(level as any);\n \n // Optional: auto-revert after duration\n let autoRevertAt: string | null = null;\n if (duration) {\n const durationMs = parseInt(duration) * 60000; // minutes to ms\n if (!isNaN(durationMs) && durationMs > 0) {\n autoRevertAt = new Date(Date.now() + durationMs).toISOString();\n setTimeout(() => {\n setLogLevel(previousLevel);\n console.log(`[Logger] Auto-reverted log level to ${previousLevel}`);\n }, durationMs);\n }\n }\n \n return c.json({\n previous: previousLevel,\n current: level,\n autoRevertAt,\n message: `Log level changed from ${previousLevel} to ${level}`,\n });\n });\n\n // GET /api/logs/level - Get current log level\n authenticated.get('/api/logs/level', async (c) => {\n const { getLogLevel } = await import('../../../utils/logger.js');\n return c.json({ level: getLogLevel() });\n });\n\n // ========== Real-time Log Streaming (SSE) ==========\n\n // GET /api/logs/stream - Stream logs in real-time via SSE\n authenticated.get('/api/logs/stream', async (c) => {\n const { createLogSSEHandler } = await import('../../../utils/logger/log-stream.js');\n return createLogSSEHandler()(c);\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"logs.js","names":[],"sources":["../../../../../src/gateway/hono/routes/logs.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport type { LogLevel } from '../../../utils/logger.js';\nimport {\n queryLogs,\n getLogFiles,\n getLogLevels,\n getFileLogStats,\n getLogModules,\n getLogErrorSummary,\n LOG_DIR,\n} from '../../../utils/logger/log-store.js';\nimport { createGatewayRouteLogger } from '../lib/route-logger.js';\n\nconst log = createGatewayRouteLogger('Logs');\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nexport function registerLogsRoutes(authenticated: Hono, _deps: AuthenticatedRouteDeps): void {\n\n // ========== Logs REST API (/api/logs) ==========\n\n // GET /api/logs - Query logs with filters\n authenticated.get('/api/logs', async (c) => {\n const query = c.req.query();\n const logs = await queryLogs({\n levels: query.level ? query.level.split(',') as LogLevel[] : undefined,\n from: query.from,\n to: query.to,\n q: query.q,\n module: query.module,\n requestId: query.requestId,\n sessionId: query.sessionId,\n limit: query.limit ? parseInt(query.limit) : 100,\n offset: query.offset ? parseInt(query.offset) : 0,\n });\n return c.json({ logs, count: logs.length });\n });\n\n // GET /api/logs/files - List log files\n authenticated.get('/api/logs/files', async (c) => {\n const files = getLogFiles();\n return c.json({ files });\n });\n\n // GET /api/logs/stats - Get log statistics (file sample + runtime counters)\n authenticated.get('/api/logs/stats', async (c) => {\n const { getRuntimeLogStats } = await import('../../../utils/logger.js');\n const [fileStats, runtimeStats] = await Promise.all([\n getFileLogStats(),\n Promise.resolve(getRuntimeLogStats()),\n ]);\n return c.json({\n byLevel: fileStats.byLevel,\n runtime: {\n byLevel: runtimeStats.byLevel,\n byModule: runtimeStats.byModule,\n errorsLast24h: runtimeStats.errorsLast24h,\n uptimeMs: runtimeStats.uptimeMs,\n },\n });\n });\n\n // GET /api/logs/errors/summary - Aggregate recent errors by type/phase/module\n authenticated.get('/api/logs/errors/summary', async (c) => {\n const query = c.req.query();\n const items = await getLogErrorSummary({\n from: query.from,\n to: query.to,\n limit: query.limit ? parseInt(query.limit, 10) : 20,\n });\n return c.json({ items });\n });\n\n // GET /api/logs/levels - Get available log levels\n authenticated.get('/api/logs/levels', async (c) => {\n return c.json({ levels: getLogLevels() });\n });\n\n // GET /api/logs/modules - Get available modules\n authenticated.get('/api/logs/modules', async (c) => {\n const modules = await getLogModules();\n return c.json({ modules });\n });\n\n // GET /api/logs/dir - Get log directory path\n authenticated.get('/api/logs/dir', async (c) => {\n return c.json({ dir: LOG_DIR });\n });\n\n // GET /api/logs/health - Get log system health status\n authenticated.get('/api/logs/health', async (c) => {\n const { getLogDir, getRuntimeLogStats, isLoggerShuttingDown } = await import('../../../utils/logger.js');\n const { getLogFiles } = await import('../../../utils/logger/log-store.js');\n \n const stats = getRuntimeLogStats();\n const files = getLogFiles().slice(0, 5);\n const isShuttingDown = isLoggerShuttingDown();\n \n return c.json({\n status: isShuttingDown ? 'shutting_down' : 'healthy',\n config: {\n dir: getLogDir(),\n uptimeMs: stats.uptimeMs,\n },\n stats: {\n byLevel: stats.byLevel,\n errorsLast24h: stats.errorsLast24h,\n modulesTracked: stats.byModule ? Object.keys(stats.byModule).length : 0,\n },\n files: files.map(f => ({\n name: f.name,\n size: f.size,\n modified: f.modified,\n type: f.type,\n })),\n shuttingDown: isShuttingDown,\n });\n });\n\n // POST /api/logs/level - Set log level dynamically\n authenticated.post('/api/logs/level', async (c) => {\n const { setLogLevel, getLogLevel } = await import('../../../utils/logger.js');\n const body = await c.req.json().catch(() => ({}));\n const { level, duration } = body as { level?: string; duration?: string };\n \n if (!level) {\n return c.json({ error: 'level is required' }, 400);\n }\n \n const validLevels = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];\n if (!validLevels.includes(level)) {\n return c.json({ error: `Invalid level. Must be one of: ${validLevels.join(', ')}` }, 400);\n }\n \n const previousLevel = getLogLevel();\n setLogLevel(level as LogLevel);\n \n // Optional: auto-revert after duration\n let autoRevertAt: string | null = null;\n if (duration) {\n const durationMs = parseInt(duration, 10) * 60000; // minutes to ms\n if (!isNaN(durationMs) && durationMs > 0) {\n autoRevertAt = new Date(Date.now() + durationMs).toISOString();\n setTimeout(() => {\n setLogLevel(previousLevel);\n log.info({ phase: 'gateway.logs.level', previousLevel, reverted: true }, `Log level auto-reverted to ${previousLevel}`);\n }, durationMs);\n }\n }\n \n log.info({ phase: 'gateway.logs.level', previousLevel, current: level, autoRevertAt }, `Log level changed to ${level}`);\n \n return c.json({\n previous: previousLevel,\n current: level,\n autoRevertAt,\n message: `Log level changed from ${previousLevel} to ${level}`,\n });\n });\n\n // GET /api/logs/level - Get current log level\n authenticated.get('/api/logs/level', async (c) => {\n const { getLogLevel } = await import('../../../utils/logger.js');\n return c.json({ level: getLogLevel() });\n });\n\n // ========== Real-time Log Streaming (SSE) ==========\n\n // GET /api/logs/stream - Stream logs in real-time via SSE\n authenticated.get('/api/logs/stream', async (c) => {\n const { createLogSSEHandler } = await import('../../../utils/logger/log-stream.js');\n return createLogSSEHandler()(c);\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Hono } from 'hono';
2
+ import type { AuthenticatedRouteDeps } from './deps.js';
3
+ export declare function registerMcpRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void;