@xopcai/xopc 0.0.82 → 0.0.84

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 (740) hide show
  1. package/README.md +3 -1
  2. package/README.zh-CN.md +3 -1
  3. package/dist/browser-ext/manifest.json +1 -1
  4. package/dist/extensions/feishu/src/outbound/media-load.js +2 -3
  5. package/dist/extensions/feishu/src/outbound/media-load.js.map +1 -1
  6. package/dist/extensions/feishu/src/schema/config-schema.d.ts +6 -6
  7. package/dist/extensions/telegram/src/config-schema.d.ts +6 -6
  8. package/dist/extensions/telegram/src/plugin.d.ts +1 -1
  9. package/dist/extensions/telegram/src/plugin.js +1 -1
  10. package/dist/extensions/telegram/src/routing-integration.js +2 -2
  11. package/dist/extensions/telegram/xopc.extension.json +1 -1
  12. package/dist/extensions/weixin/src/api/api.js +3 -3
  13. package/dist/extensions/weixin/src/auth/accounts.js +1 -1
  14. package/dist/extensions/weixin/src/cdn/upload.js +1 -1
  15. package/dist/extensions/weixin/src/config-schema.d.ts +3 -3
  16. package/dist/extensions/weixin/src/media/data-url.js +1 -1
  17. package/dist/extensions/weixin/src/messaging/debug-mode.js +1 -1
  18. package/dist/extensions/weixin/src/messaging/inbound.js +1 -1
  19. package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
  20. package/dist/extensions/weixin/src/plugin.js +1 -1
  21. package/dist/extensions/weixin/src/storage/sync-buf.js +1 -1
  22. package/dist/gateway/static/root/assets/agents-tR-nNP04.js +222 -0
  23. package/dist/gateway/static/root/assets/{apps-page-pJ27dsqn.js → apps-page-BDw6SP-d.js} +1 -1
  24. package/dist/gateway/static/root/assets/channels-settings-DEFd-jj1.js +1 -0
  25. package/dist/gateway/static/root/assets/{channels-status-swr-D1KYmOmi.js → channels-status-swr-DI5FHdGe.js} +1 -1
  26. package/dist/gateway/static/root/assets/{cron-api-Y2wfSJVI.js → cron-api-BSqY8LwW.js} +1 -1
  27. package/dist/gateway/static/root/assets/{cron-page-B97KU_RG.js → cron-page-D7lVDjcR.js} +1 -1
  28. package/dist/gateway/static/root/assets/{dist-CboA_Css.js → dist-CqNMNhJM.js} +1 -1
  29. package/dist/gateway/static/root/assets/{extension-debug-page-DN_zNmpo.js → extension-debug-page-gf2L0kY_.js} +1 -1
  30. package/dist/gateway/static/root/assets/{extension-page-BUXtOzv5.js → extension-page-CQo2Xsmg.js} +1 -1
  31. package/dist/gateway/static/root/assets/{extension-settings-page-C2dX4KCW.js → extension-settings-page-CZf0WoZg.js} +1 -1
  32. package/dist/gateway/static/root/assets/fetch-2iRFmd3n.js +3 -0
  33. package/dist/gateway/static/root/assets/{field-primitives-B9rOLqdm.js → field-primitives-DTtlp-l8.js} +1 -1
  34. package/dist/gateway/static/root/assets/{heartbeat-config-api-DvfiRVrc.js → heartbeat-config-api-B0drdQEJ.js} +1 -1
  35. package/dist/gateway/static/root/assets/{index-DQuaMye9.js → index-0Gt3TG4j.js} +94 -85
  36. package/dist/gateway/static/root/assets/index-BuFldCsB.css +1 -0
  37. package/dist/gateway/static/root/assets/{logs-page-BQuBpHcc.js → logs-page-DMuORLfC.js} +1 -1
  38. package/dist/gateway/static/root/assets/sessions-page-_UO8g6NN.js +1 -0
  39. package/dist/gateway/static/root/assets/{settings-form-section-2Yu-FASs.js → settings-form-section-DkmHkknc.js} +1 -1
  40. package/dist/gateway/static/root/assets/settings-page-Cz8FoW_A.js +3 -0
  41. package/dist/gateway/static/root/assets/skills-page-HrUOxF7H.js +2 -0
  42. package/dist/gateway/static/root/assets/{theme-store-DnwYutiX.js → theme-store-D01dJt95.js} +1 -1
  43. package/dist/gateway/static/root/assets/{utils-D2Gn2qod.js → utils-BFwcR6pL.js} +1 -1
  44. package/dist/gateway/static/root/assets/voice-api-key-field-JF8-aqc5.js +1 -0
  45. package/dist/gateway/static/root/index.html +4 -4
  46. package/dist/package.js +1 -1
  47. package/dist/src/agent/agent-instance-gateway.d.ts +50 -0
  48. package/dist/src/agent/agent-instance-gateway.js +1 -0
  49. package/dist/src/agent/agent-manager.d.ts +20 -14
  50. package/dist/src/agent/agent-manager.js +74 -186
  51. package/dist/src/agent/agent-manager.js.map +1 -1
  52. package/dist/src/agent/background-review/coordinator.d.ts +61 -0
  53. package/dist/src/agent/background-review/coordinator.js +120 -0
  54. package/dist/src/agent/background-review/coordinator.js.map +1 -0
  55. package/dist/src/agent/bootstrap/load-bootstrap-files.js +1 -1
  56. package/dist/src/agent/child-agent-factory.d.ts +14 -0
  57. package/dist/src/agent/child-agent-factory.js +2 -8
  58. package/dist/src/agent/child-agent-factory.js.map +1 -1
  59. package/dist/src/agent/context/workspace-seed.js +3 -3
  60. package/dist/src/agent/embedded/index.d.ts +1 -2
  61. package/dist/src/agent/embedded/index.js +2 -3
  62. package/dist/src/agent/embedded/run-for-session.d.ts +2 -2
  63. package/dist/src/agent/embedded/run-for-session.js.map +1 -1
  64. package/dist/src/agent/embedded/runs.d.ts +32 -0
  65. package/dist/src/agent/embedded/runs.js +79 -19
  66. package/dist/src/agent/embedded/runs.js.map +1 -1
  67. package/dist/src/agent/embedded/session-manager-cache.d.ts +14 -0
  68. package/dist/src/agent/embedded/session-manager-cache.js +32 -11
  69. package/dist/src/agent/embedded/session-manager-cache.js.map +1 -1
  70. package/dist/src/agent/embedded/session-runner.d.ts +37 -7
  71. package/dist/src/agent/embedded/session-runner.js +184 -153
  72. package/dist/src/agent/embedded/session-runner.js.map +1 -1
  73. package/dist/src/agent/embedded/session-tool-result-guard.d.ts +57 -9
  74. package/dist/src/agent/embedded/session-tool-result-guard.js +159 -67
  75. package/dist/src/agent/embedded/session-tool-result-guard.js.map +1 -1
  76. package/dist/src/agent/goals/goal-run-store.js +4 -4
  77. package/dist/src/agent/goals/persistent-goal-service.d.ts +84 -0
  78. package/dist/src/agent/goals/persistent-goal-service.js +139 -0
  79. package/dist/src/agent/goals/persistent-goal-service.js.map +1 -0
  80. package/dist/src/agent/goals/post-turn.js +2 -2
  81. package/dist/src/agent/goals/state.d.ts +1 -1
  82. package/dist/src/agent/goals/state.js.map +1 -1
  83. package/dist/src/agent/image/load-image-media.js +1 -1
  84. package/dist/src/agent/inbound/inbound-loop.d.ts +77 -0
  85. package/dist/src/agent/inbound/inbound-loop.js +226 -0
  86. package/dist/src/agent/inbound/inbound-loop.js.map +1 -0
  87. package/dist/src/agent/inbound/turn-dispatcher.d.ts +80 -0
  88. package/dist/src/agent/inbound/turn-dispatcher.js +138 -0
  89. package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -0
  90. package/dist/src/agent/ipc/bus.js +1 -1
  91. package/dist/src/agent/ipc/inbox.js +2 -2
  92. package/dist/src/agent/ipc/socket.js +1 -1
  93. package/dist/src/agent/lifecycle/handlers/compaction.d.ts +1 -1
  94. package/dist/src/agent/lifecycle/handlers/compaction.js.map +1 -1
  95. package/dist/src/agent/lifecycle/manager.d.ts +1 -1
  96. package/dist/src/agent/lifecycle/manager.js.map +1 -1
  97. package/dist/src/agent/lifecycle/types.d.ts +1 -1
  98. package/dist/src/agent/memory/builtin-memory-store.js +1 -1
  99. package/dist/src/agent/memory/dreaming/deep-promotion.js +1 -1
  100. package/dist/src/agent/memory/dreaming/events.js +1 -1
  101. package/dist/src/agent/memory/dreaming/last-run.js +1 -1
  102. package/dist/src/agent/memory/dreaming/light-sweep.js +1 -1
  103. package/dist/src/agent/memory/dreaming/preview.js +1 -1
  104. package/dist/src/agent/memory/dreaming/rem-patterns.js +1 -1
  105. package/dist/src/agent/memory/dreaming/short-term-store.js +1 -1
  106. package/dist/src/agent/memory/dreaming/utils.d.ts +12 -2
  107. package/dist/src/agent/memory/dreaming/utils.js +1 -1
  108. package/dist/src/agent/memory/dreaming/utils.js.map +1 -1
  109. package/dist/src/agent/memory/index.js +3 -3
  110. package/dist/src/agent/memory/plugin-discovery.js +1 -1
  111. package/dist/src/agent/memory/prefetch-coordinator.d.ts +37 -0
  112. package/dist/src/agent/memory/prefetch-coordinator.js +45 -0
  113. package/dist/src/agent/memory/prefetch-coordinator.js.map +1 -0
  114. package/dist/src/agent/messaging/command-handler.d.ts +5 -1
  115. package/dist/src/agent/messaging/command-handler.js +24 -96
  116. package/dist/src/agent/messaging/command-handler.js.map +1 -1
  117. package/dist/src/agent/messaging/index.d.ts +1 -0
  118. package/dist/src/agent/messaging/index.js +2 -1
  119. package/dist/src/agent/messaging/message-router.d.ts +1 -1
  120. package/dist/src/agent/messaging/message-router.js.map +1 -1
  121. package/dist/src/agent/messaging/outbound-coordinator.d.ts +82 -0
  122. package/dist/src/agent/messaging/outbound-coordinator.js +123 -0
  123. package/dist/src/agent/messaging/outbound-coordinator.js.map +1 -0
  124. package/dist/src/agent/models/manager.js +1 -1
  125. package/dist/src/agent/orchestration/agent-event-handler.d.ts +36 -33
  126. package/dist/src/agent/orchestration/agent-event-handler.js +212 -174
  127. package/dist/src/agent/orchestration/agent-event-handler.js.map +1 -1
  128. package/dist/src/agent/orchestration/agent-orchestrator.d.ts +4 -4
  129. package/dist/src/agent/orchestration/agent-orchestrator.js +4 -8
  130. package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
  131. package/dist/src/agent/orchestration/index.d.ts +1 -1
  132. package/dist/src/agent/orchestration/index.js +2 -2
  133. package/dist/src/agent/prompt/service-prompt-builder.js +4 -4
  134. package/dist/src/agent/reply/post-compaction-context.js +1 -1
  135. package/dist/src/agent/reply/workspace-boundary-read.js +1 -1
  136. package/dist/src/agent/sandbox/path-policy.js +1 -1
  137. package/dist/src/agent/service/async-queue.d.ts +20 -0
  138. package/dist/src/agent/service/async-queue.js +53 -0
  139. package/dist/src/agent/service/async-queue.js.map +1 -0
  140. package/dist/src/agent/service/build-direct-message-content.d.ts +2 -2
  141. package/dist/src/agent/service/build-direct-message-content.js.map +1 -1
  142. package/dist/src/agent/service/direct-turn-helpers.d.ts +70 -0
  143. package/dist/src/agent/service/direct-turn-helpers.js +90 -0
  144. package/dist/src/agent/service/direct-turn-helpers.js.map +1 -0
  145. package/dist/src/agent/service/process-direct-one-shot.d.ts +3 -3
  146. package/dist/src/agent/service/process-direct-one-shot.js +17 -34
  147. package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
  148. package/dist/src/agent/service/process-direct-streaming.d.ts +2 -2
  149. package/dist/src/agent/service/process-direct-streaming.js +122 -168
  150. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  151. package/dist/src/agent/service/webchat-tts.d.ts +2 -2
  152. package/dist/src/agent/service/webchat-tts.js +1 -1
  153. package/dist/src/agent/service/webchat-tts.js.map +1 -1
  154. package/dist/src/agent/service.d.ts +62 -167
  155. package/dist/src/agent/service.js +177 -786
  156. package/dist/src/agent/service.js.map +1 -1
  157. package/dist/src/agent/session/index.d.ts +4 -0
  158. package/dist/src/agent/session/index.js +5 -1
  159. package/dist/src/agent/session/session-config-service.d.ts +68 -0
  160. package/dist/src/agent/session/session-config-service.js +172 -0
  161. package/dist/src/agent/session/session-config-service.js.map +1 -0
  162. package/dist/src/agent/session/session-context.d.ts +27 -19
  163. package/dist/src/agent/session/session-context.js +39 -24
  164. package/dist/src/agent/session/session-context.js.map +1 -1
  165. package/dist/src/agent/session/session-hydrator.d.ts +42 -0
  166. package/dist/src/agent/session/session-hydrator.js +66 -0
  167. package/dist/src/agent/session/session-hydrator.js.map +1 -0
  168. package/dist/src/agent/session/session-inspector.d.ts +80 -0
  169. package/dist/src/agent/session/session-inspector.js +119 -0
  170. package/dist/src/agent/session/session-inspector.js.map +1 -0
  171. package/dist/src/agent/session/session-state-bag.d.ts +83 -0
  172. package/dist/src/agent/session/session-state-bag.js +192 -0
  173. package/dist/src/agent/session/session-state-bag.js.map +1 -0
  174. package/dist/src/agent/skills/config.js +1 -1
  175. package/dist/src/agent/skills/hub-hash.js +2 -2
  176. package/dist/src/agent/skills/hub-lock.js +1 -1
  177. package/dist/src/agent/skills/hub-pull.js +2 -2
  178. package/dist/src/agent/skills/index.d.ts +0 -2
  179. package/dist/src/agent/skills/index.js +3 -5
  180. package/dist/src/agent/skills/index.js.map +1 -1
  181. package/dist/src/agent/skills/managed-store.js +1 -1
  182. package/dist/src/agent/skills/marketplace/adapters/clawhub/adapter.js +11 -6
  183. package/dist/src/agent/skills/marketplace/adapters/clawhub/adapter.js.map +1 -1
  184. package/dist/src/agent/skills/marketplace/adapters/skillhub/adapter.js +35 -7
  185. package/dist/src/agent/skills/marketplace/adapters/skillhub/adapter.js.map +1 -1
  186. package/dist/src/agent/skills/scanner.js +1 -1
  187. package/dist/src/agent/skills/skill-manage-ops.js +2 -2
  188. package/dist/src/agent/skills/skill-manager.js +1 -1
  189. package/dist/src/agent/tools/browser/tool/browser-use-tool.d.ts +7 -0
  190. package/dist/src/agent/tools/browser/tool/browser-use-tool.js +37 -0
  191. package/dist/src/agent/tools/browser/tool/browser-use-tool.js.map +1 -1
  192. package/dist/src/agent/tools/delegate-tool.d.ts +7 -0
  193. package/dist/src/agent/tools/delegate-tool.js +2 -1
  194. package/dist/src/agent/tools/delegate-tool.js.map +1 -1
  195. package/dist/src/agent/tools/dreaming-tool.js +1 -1
  196. package/dist/src/agent/tools/executor.d.ts +34 -15
  197. package/dist/src/agent/tools/executor.js +44 -79
  198. package/dist/src/agent/tools/executor.js.map +1 -1
  199. package/dist/src/agent/tools/factory.d.ts +6 -0
  200. package/dist/src/agent/tools/factory.js +63 -4
  201. package/dist/src/agent/tools/factory.js.map +1 -1
  202. package/dist/src/agent/tools/image-generate-tool.js +1 -1
  203. package/dist/src/agent/tools/send-media.js +1 -1
  204. package/dist/src/agent/tools/skill-manage-tool.js +1 -1
  205. package/dist/src/agent/tools/skills-tools.js +1 -1
  206. package/dist/src/agent/tools/tts-tool.js +1 -1
  207. package/dist/src/agent/tools/write.js +1 -1
  208. package/dist/src/agent/workspace-runtime/registry.d.ts +48 -0
  209. package/dist/src/agent/workspace-runtime/registry.js +59 -0
  210. package/dist/src/agent/workspace-runtime/registry.js.map +1 -0
  211. package/dist/src/auth/credentials.js +3 -3
  212. package/dist/src/auth/profiles/store.js +1 -1
  213. package/dist/src/auth/sync-provider-auth.js +1 -1
  214. package/dist/src/browser/cdp-local-launcher.js +4 -3
  215. package/dist/src/browser/cdp-local-launcher.js.map +1 -1
  216. package/dist/src/browser/index.d.ts +1 -0
  217. package/dist/src/browser/index.js +2 -1
  218. package/dist/src/browser/manager.js +3 -2
  219. package/dist/src/browser/manager.js.map +1 -1
  220. package/dist/src/browser/providers/browser-ext-install.js +4 -4
  221. package/dist/src/browser/providers/browser-use.js +2 -1
  222. package/dist/src/browser/providers/browser-use.js.map +1 -1
  223. package/dist/src/browser/providers/browserbase.js +2 -1
  224. package/dist/src/browser/providers/browserbase.js.map +1 -1
  225. package/dist/src/browser/providers/cloakbrowser.js +7 -6
  226. package/dist/src/browser/providers/cloakbrowser.js.map +1 -1
  227. package/dist/src/browser/providers/playwright-doctor.d.ts +2 -0
  228. package/dist/src/browser/providers/playwright-doctor.js +7 -3
  229. package/dist/src/browser/providers/playwright-doctor.js.map +1 -1
  230. package/dist/src/browser/readiness.d.ts +33 -0
  231. package/dist/src/browser/readiness.js +138 -0
  232. package/dist/src/browser/readiness.js.map +1 -0
  233. package/dist/src/browser/stealth.js +2 -2
  234. package/dist/src/channels/attachments/inbound-persist.js +1 -1
  235. package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
  236. package/dist/src/channels/channel-domain.d.ts +1 -1
  237. package/dist/src/channels/config-helpers.d.ts +1 -1
  238. package/dist/src/channels/config-helpers.js.map +1 -1
  239. package/dist/src/channels/heartbeat-scheduler.d.ts +40 -0
  240. package/dist/src/channels/heartbeat-scheduler.js +94 -0
  241. package/dist/src/channels/heartbeat-scheduler.js.map +1 -0
  242. package/dist/src/channels/lifecycle-supervisor.d.ts +81 -0
  243. package/dist/src/channels/lifecycle-supervisor.js +263 -0
  244. package/dist/src/channels/lifecycle-supervisor.js.map +1 -0
  245. package/dist/src/channels/manager.d.ts +34 -68
  246. package/dist/src/channels/manager.js +107 -477
  247. package/dist/src/channels/manager.js.map +1 -1
  248. package/dist/src/channels/outbound/deliver.d.ts +1 -1
  249. package/dist/src/channels/outbound/deliver.js.map +1 -1
  250. package/dist/src/channels/outbound/persist-store.js +1 -1
  251. package/dist/src/channels/outbound-sender.d.ts +51 -0
  252. package/dist/src/channels/outbound-sender.js +125 -0
  253. package/dist/src/channels/outbound-sender.js.map +1 -0
  254. package/dist/src/channels/pairing/allow-from-file.js +1 -1
  255. package/dist/src/channels/pairing/pairing-service.d.ts +3 -10
  256. package/dist/src/channels/pairing/pairing-service.js.map +1 -1
  257. package/dist/src/channels/pairing/pairing-store.js +2 -2
  258. package/dist/src/channels/pairing/pairing-types.d.ts +15 -0
  259. package/dist/src/channels/pairing/pairing-types.js +1 -0
  260. package/dist/src/channels/plugin-registry.d.ts +22 -0
  261. package/dist/src/channels/plugin-registry.js +44 -0
  262. package/dist/src/channels/plugin-registry.js.map +1 -0
  263. package/dist/src/channels/plugin-types.d.ts +1 -1
  264. package/dist/src/channels/plugins/types.adapters.d.ts +2 -2
  265. package/dist/src/channels/security-helpers.d.ts +1 -1
  266. package/dist/src/channels/security-helpers.js.map +1 -1
  267. package/dist/src/channels/setup-wizard.d.ts +1 -1
  268. package/dist/src/chat-commands/builtins/config.js +2 -2
  269. package/dist/src/chat-commands/context.js +1 -1
  270. package/dist/src/cli/command-catalog.js +110 -8
  271. package/dist/src/cli/command-catalog.js.map +1 -1
  272. package/dist/src/cli/command-loaders.js +2 -0
  273. package/dist/src/cli/command-loaders.js.map +1 -1
  274. package/dist/src/cli/command-manifest.js +9 -1
  275. package/dist/src/cli/command-manifest.js.map +1 -1
  276. package/dist/src/cli/commands/agent/stream-renderer.js +1 -1
  277. package/dist/src/cli/commands/agent/stream-renderer.js.map +1 -1
  278. package/dist/src/cli/commands/agent.js +4 -4
  279. package/dist/src/cli/commands/agent.js.map +1 -1
  280. package/dist/src/cli/commands/browser-cli-helpers.js +2 -1
  281. package/dist/src/cli/commands/browser-cli-helpers.js.map +1 -1
  282. package/dist/src/cli/commands/config.js +70 -19
  283. package/dist/src/cli/commands/config.js.map +1 -1
  284. package/dist/src/cli/commands/cron-cli.d.ts +2 -0
  285. package/dist/src/cli/commands/cron-cli.js +15 -0
  286. package/dist/src/cli/commands/cron-cli.js.map +1 -0
  287. package/dist/src/cli/commands/cron.d.ts +4 -1
  288. package/dist/src/cli/commands/cron.js +76 -41
  289. package/dist/src/cli/commands/cron.js.map +1 -1
  290. package/dist/src/cli/commands/doctor/checks/channel-config.js +1 -1
  291. package/dist/src/cli/commands/doctor/checks/channel-config.js.map +1 -1
  292. package/dist/src/cli/commands/doctor/checks/config-health.js +2 -2
  293. package/dist/src/cli/commands/doctor/checks/config-health.js.map +1 -1
  294. package/dist/src/cli/commands/doctor/checks/cron-health.js +1 -1
  295. package/dist/src/cli/commands/doctor/checks/cron-health.js.map +1 -1
  296. package/dist/src/cli/commands/doctor/checks/gateway-health.js +2 -2
  297. package/dist/src/cli/commands/doctor/checks/gateway-health.js.map +1 -1
  298. package/dist/src/cli/commands/doctor/checks/gateway-service.js +2 -2
  299. package/dist/src/cli/commands/doctor/checks/gateway-service.js.map +1 -1
  300. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  301. package/dist/src/cli/commands/doctor/checks/session-integrity.js +1 -1
  302. package/dist/src/cli/commands/doctor/checks/state-integrity.js +2 -2
  303. package/dist/src/cli/commands/doctor/checks/state-integrity.js.map +1 -1
  304. package/dist/src/cli/commands/doctor/checks/workspace-status.js +4 -4
  305. package/dist/src/cli/commands/doctor/checks/workspace-status.js.map +1 -1
  306. package/dist/src/cli/commands/extension-dev.js +2 -2
  307. package/dist/src/cli/commands/extension-dev.js.map +1 -1
  308. package/dist/src/cli/commands/extension-marketplace.js +2 -2
  309. package/dist/src/cli/commands/extension-marketplace.js.map +1 -1
  310. package/dist/src/cli/commands/extension-pack.js +1 -1
  311. package/dist/src/cli/commands/gateway/call.js +1 -1
  312. package/dist/src/cli/commands/gateway/call.js.map +1 -1
  313. package/dist/src/cli/commands/gateway/health.js +1 -1
  314. package/dist/src/cli/commands/gateway/health.js.map +1 -1
  315. package/dist/src/cli/commands/gateway/index.d.ts +1 -1
  316. package/dist/src/cli/commands/gateway/index.js +2 -2
  317. package/dist/src/cli/commands/gateway/lifecycle-core.d.ts +31 -12
  318. package/dist/src/cli/commands/gateway/lifecycle-core.js +167 -116
  319. package/dist/src/cli/commands/gateway/lifecycle-core.js.map +1 -1
  320. package/dist/src/cli/commands/gateway/lifecycle.d.ts +11 -0
  321. package/dist/src/cli/commands/gateway/lifecycle.js +102 -0
  322. package/dist/src/cli/commands/gateway/lifecycle.js.map +1 -0
  323. package/dist/src/cli/commands/gateway/logs.js +1 -1
  324. package/dist/src/cli/commands/gateway/logs.js.map +1 -1
  325. package/dist/src/cli/commands/gateway/probe.js +1 -1
  326. package/dist/src/cli/commands/gateway/probe.js.map +1 -1
  327. package/dist/src/cli/commands/gateway/restart-health.d.ts +12 -0
  328. package/dist/src/cli/commands/gateway/restart-health.js +45 -1
  329. package/dist/src/cli/commands/gateway/restart-health.js.map +1 -1
  330. package/dist/src/cli/commands/gateway/restart.js +3 -3
  331. package/dist/src/cli/commands/gateway/restart.js.map +1 -1
  332. package/dist/src/cli/commands/gateway/run-foreground.d.ts +0 -1
  333. package/dist/src/cli/commands/gateway/run-foreground.js +0 -35
  334. package/dist/src/cli/commands/gateway/run-foreground.js.map +1 -1
  335. package/dist/src/cli/commands/gateway/service.d.ts +4 -0
  336. package/dist/src/cli/commands/gateway/service.js +18 -3
  337. package/dist/src/cli/commands/gateway/service.js.map +1 -1
  338. package/dist/src/cli/commands/gateway/shared.d.ts +3 -0
  339. package/dist/src/cli/commands/gateway/shared.js +54 -0
  340. package/dist/src/cli/commands/gateway/shared.js.map +1 -0
  341. package/dist/src/cli/commands/gateway/status.js +1 -1
  342. package/dist/src/cli/commands/gateway/status.js.map +1 -1
  343. package/dist/src/cli/commands/gateway/stop.js +2 -2
  344. package/dist/src/cli/commands/gateway/stop.js.map +1 -1
  345. package/dist/src/cli/commands/gateway/subcommands.js +1 -4
  346. package/dist/src/cli/commands/gateway/subcommands.js.map +1 -1
  347. package/dist/src/cli/commands/gateway/token.js +1 -1
  348. package/dist/src/cli/commands/gateway/token.js.map +1 -1
  349. package/dist/src/cli/commands/gateway.js +5 -5
  350. package/dist/src/cli/commands/gateway.js.map +1 -1
  351. package/dist/src/cli/commands/image.js +2 -2
  352. package/dist/src/cli/commands/image.js.map +1 -1
  353. package/dist/src/cli/commands/init.js +31 -4
  354. package/dist/src/cli/commands/init.js.map +1 -1
  355. package/dist/src/cli/commands/models.d.ts +4 -1
  356. package/dist/src/cli/commands/models.js +87 -75
  357. package/dist/src/cli/commands/models.js.map +1 -1
  358. package/dist/src/cli/commands/onboard/gateway.d.ts +0 -8
  359. package/dist/src/cli/commands/onboard/gateway.js +48 -49
  360. package/dist/src/cli/commands/onboard/gateway.js.map +1 -1
  361. package/dist/src/cli/commands/onboard.js +11 -64
  362. package/dist/src/cli/commands/onboard.js.map +1 -1
  363. package/dist/src/cli/commands/profile.d.ts +3 -5
  364. package/dist/src/cli/commands/profile.js +31 -31
  365. package/dist/src/cli/commands/profile.js.map +1 -1
  366. package/dist/src/cli/commands/session/utils.js +1 -1
  367. package/dist/src/cli/commands/session/utils.js.map +1 -1
  368. package/dist/src/cli/commands/setup.js +6 -1
  369. package/dist/src/cli/commands/setup.js.map +1 -1
  370. package/dist/src/cli/commands/skills.js +1 -1
  371. package/dist/src/cli/commands/tailscale.js +1 -1
  372. package/dist/src/cli/commands/tailscale.js.map +1 -1
  373. package/dist/src/cli/context.d.ts +20 -0
  374. package/dist/src/cli/context.js +23 -0
  375. package/dist/src/cli/context.js.map +1 -0
  376. package/dist/src/cli/extension-cli-register.js +3 -3
  377. package/dist/src/cli/gateway-run-argv.js +16 -9
  378. package/dist/src/cli/gateway-run-argv.js.map +1 -1
  379. package/dist/src/cli/gateway-run-fast-path.js +1 -1
  380. package/dist/src/cli/gateway-run-fast-path.js.map +1 -1
  381. package/dist/src/cli/index.d.ts +1 -7
  382. package/dist/src/cli/index.js +4 -6
  383. package/dist/src/cli/index.js.map +1 -1
  384. package/dist/src/cli/utils/init-workspace-core.js +2 -2
  385. package/dist/src/config/commands.flags.d.ts +3 -0
  386. package/dist/src/config/commands.flags.js +11 -0
  387. package/dist/src/config/commands.flags.js.map +1 -0
  388. package/dist/src/config/index.d.ts +1 -0
  389. package/dist/src/config/index.js +6 -5
  390. package/dist/src/config/index.js.map +1 -1
  391. package/dist/src/config/loader.js +2 -2
  392. package/dist/src/config/models-json.js +2 -2
  393. package/dist/src/config/profile.js +2 -2
  394. package/dist/src/config/schema.d.ts +11 -4
  395. package/dist/src/config/schema.js +13 -12
  396. package/dist/src/config/schema.js.map +1 -1
  397. package/dist/src/config/workspace-path-helpers.d.ts +15 -0
  398. package/dist/src/config/workspace-path-helpers.js +14 -0
  399. package/dist/src/config/workspace-path-helpers.js.map +1 -0
  400. package/dist/src/cron/executor.js +4 -4
  401. package/dist/src/cron/executor.js.map +1 -1
  402. package/dist/src/cron/persistence.js +1 -1
  403. package/dist/src/cron/run-log-store.js +1 -1
  404. package/dist/src/daemon/index.d.ts +0 -1
  405. package/dist/src/daemon/index.js +1 -2
  406. package/dist/src/daemon/install-plan.js +3 -2
  407. package/dist/src/daemon/install-plan.js.map +1 -1
  408. package/dist/src/daemon/launchd.js +2 -2
  409. package/dist/src/daemon/systemd.js +2 -2
  410. package/dist/src/daemon/types.d.ts +0 -6
  411. package/dist/src/extensions/api.d.ts +1 -1
  412. package/dist/src/extensions/api.js +2 -2
  413. package/dist/src/extensions/api.js.map +1 -1
  414. package/dist/src/extensions/bundle-mcp.js +1 -1
  415. package/dist/src/extensions/discover-extensions.js +1 -1
  416. package/dist/src/extensions/extension-registry-impl.d.ts +51 -0
  417. package/dist/src/extensions/extension-registry-impl.js +117 -0
  418. package/dist/src/extensions/extension-registry-impl.js.map +1 -0
  419. package/dist/src/extensions/health.js +1 -1
  420. package/dist/src/extensions/index.js +3 -2
  421. package/dist/src/extensions/loader.d.ts +3 -43
  422. package/dist/src/extensions/loader.js +3 -110
  423. package/dist/src/extensions/loader.js.map +1 -1
  424. package/dist/src/extensions/lockfile.js +2 -2
  425. package/dist/src/extensions/sdk/index.js +2 -1
  426. package/dist/src/extensions/sdk/index.js.map +1 -1
  427. package/dist/src/extensions/types/events.d.ts +7 -1
  428. package/dist/src/gateway/agents-admin.js +2 -2
  429. package/dist/src/gateway/file-path-classifier.js +2 -2
  430. package/dist/src/gateway/heartbeat/service.js +1 -1
  431. package/dist/src/gateway/heartbeat/service.js.map +1 -1
  432. package/dist/src/gateway/hono/app.js +5 -53
  433. package/dist/src/gateway/hono/app.js.map +1 -1
  434. package/dist/src/gateway/hono/lib/extension-store.js +1 -1
  435. package/dist/src/gateway/hono/lib/static-ui.js +2 -2
  436. package/dist/src/gateway/hono/middleware/auth.d.ts +5 -14
  437. package/dist/src/gateway/hono/middleware/auth.js +89 -126
  438. package/dist/src/gateway/hono/middleware/auth.js.map +1 -1
  439. package/dist/src/gateway/hono/middleware/logger.js +1 -1
  440. package/dist/src/gateway/hono/middleware/logger.js.map +1 -1
  441. package/dist/src/gateway/hono/middleware/strict-rate-limit.d.ts +14 -0
  442. package/dist/src/gateway/hono/middleware/strict-rate-limit.js +62 -0
  443. package/dist/src/gateway/hono/middleware/strict-rate-limit.js.map +1 -0
  444. package/dist/src/gateway/hono/oauth.js +1 -1
  445. package/dist/src/gateway/hono/routes/auth-registry-extensions.js +4 -4
  446. package/dist/src/gateway/hono/routes/auth-registry-extensions.js.map +1 -1
  447. package/dist/src/gateway/hono/routes/browser.d.ts +20 -0
  448. package/dist/src/gateway/hono/routes/browser.js +626 -0
  449. package/dist/src/gateway/hono/routes/browser.js.map +1 -0
  450. package/dist/src/gateway/hono/routes/commands-skills.js +13 -13
  451. package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
  452. package/dist/src/gateway/hono/routes/config-patch/agents.d.ts +18 -0
  453. package/dist/src/gateway/hono/routes/config-patch/agents.js +418 -0
  454. package/dist/src/gateway/hono/routes/config-patch/agents.js.map +1 -0
  455. package/dist/src/gateway/hono/routes/config-patch/channels.d.ts +12 -0
  456. package/dist/src/gateway/hono/routes/config-patch/channels.js +186 -0
  457. package/dist/src/gateway/hono/routes/config-patch/channels.js.map +1 -0
  458. package/dist/src/gateway/hono/routes/config-patch/gateway.d.ts +18 -0
  459. package/dist/src/gateway/hono/routes/config-patch/gateway.js +264 -0
  460. package/dist/src/gateway/hono/routes/config-patch/gateway.js.map +1 -0
  461. package/dist/src/gateway/hono/routes/config-patch/index.d.ts +9 -0
  462. package/dist/src/gateway/hono/routes/config-patch/index.js +6 -0
  463. package/dist/src/gateway/hono/routes/config-patch/misc.d.ts +23 -0
  464. package/dist/src/gateway/hono/routes/config-patch/misc.js +139 -0
  465. package/dist/src/gateway/hono/routes/config-patch/misc.js.map +1 -0
  466. package/dist/src/gateway/hono/routes/config-patch/result.d.ts +18 -0
  467. package/dist/src/gateway/hono/routes/config-patch/result.js +13 -0
  468. package/dist/src/gateway/hono/routes/config-patch/result.js.map +1 -0
  469. package/dist/src/gateway/hono/routes/config.js +20 -1764
  470. package/dist/src/gateway/hono/routes/config.js.map +1 -1
  471. package/dist/src/gateway/hono/routes/dreaming.js +2 -3
  472. package/dist/src/gateway/hono/routes/dreaming.js.map +1 -1
  473. package/dist/src/gateway/hono/routes/host-fs.js +1 -1
  474. package/dist/src/gateway/hono/routes/lazy-bundles.js +10 -5
  475. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  476. package/dist/src/gateway/hono/routes/mcp.js +1 -2
  477. package/dist/src/gateway/hono/routes/mcp.js.map +1 -1
  478. package/dist/src/gateway/hono/routes/models.js +1 -1
  479. package/dist/src/gateway/hono/routes/sessions.js +32 -32
  480. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  481. package/dist/src/gateway/hono/routes/shares.js +4 -4
  482. package/dist/src/gateway/hono/routes/shares.js.map +1 -1
  483. package/dist/src/gateway/hono/routes/tunnel.js +1 -1
  484. package/dist/src/gateway/hono/routes/tunnel.js.map +1 -1
  485. package/dist/src/gateway/hono/routes/workspace.js +6 -7
  486. package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
  487. package/dist/src/gateway/hono/sse.js +2 -2
  488. package/dist/src/gateway/index.d.ts +1 -1
  489. package/dist/src/gateway/index.js +4 -2
  490. package/dist/src/gateway/lock.js +3 -3
  491. package/dist/src/gateway/rate-limit/auth-policy.d.ts +34 -0
  492. package/dist/src/gateway/rate-limit/auth-policy.js +49 -0
  493. package/dist/src/gateway/rate-limit/auth-policy.js.map +1 -0
  494. package/dist/src/gateway/rate-limit/buckets.d.ts +63 -0
  495. package/dist/src/gateway/rate-limit/buckets.js +143 -0
  496. package/dist/src/gateway/rate-limit/buckets.js.map +1 -0
  497. package/dist/src/gateway/rate-limit/env-flags.d.ts +13 -0
  498. package/dist/src/gateway/rate-limit/env-flags.js +16 -0
  499. package/dist/src/gateway/rate-limit/env-flags.js.map +1 -0
  500. package/dist/src/gateway/rate-limit/index.d.ts +3 -0
  501. package/dist/src/gateway/rate-limit/index.js +4 -0
  502. package/dist/src/gateway/run-loop.d.ts +1 -1
  503. package/dist/src/gateway/run-loop.js +24 -4
  504. package/dist/src/gateway/run-loop.js.map +1 -1
  505. package/dist/src/gateway/runtime-config.js +2 -1
  506. package/dist/src/gateway/runtime-config.js.map +1 -1
  507. package/dist/src/gateway/security/audit.js +2 -1
  508. package/dist/src/gateway/security/audit.js.map +1 -1
  509. package/dist/src/gateway/security/index.d.ts +0 -1
  510. package/dist/src/gateway/security/index.js +1 -2
  511. package/dist/src/gateway/security/loopback.d.ts +13 -0
  512. package/dist/src/gateway/security/loopback.js +45 -0
  513. package/dist/src/gateway/security/loopback.js.map +1 -0
  514. package/dist/src/gateway/service/agent-runner.d.ts +108 -0
  515. package/dist/src/gateway/service/agent-runner.js +184 -0
  516. package/dist/src/gateway/service/agent-runner.js.map +1 -0
  517. package/dist/src/gateway/service/config-coordinator.d.ts +119 -0
  518. package/dist/src/gateway/service/config-coordinator.js +351 -0
  519. package/dist/src/gateway/service/config-coordinator.js.map +1 -0
  520. package/dist/src/gateway/service/marketplace-service.d.ts +85 -0
  521. package/dist/src/gateway/service/marketplace-service.js +239 -0
  522. package/dist/src/gateway/service/marketplace-service.js.map +1 -0
  523. package/dist/src/gateway/service/run-gateway-agent.js +5 -5
  524. package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
  525. package/dist/src/gateway/service/sessions-api.d.ts +125 -0
  526. package/dist/src/gateway/service/sessions-api.js +135 -0
  527. package/dist/src/gateway/service/sessions-api.js.map +1 -0
  528. package/dist/src/gateway/service.d.ts +30 -360
  529. package/dist/src/gateway/service.js +122 -904
  530. package/dist/src/gateway/service.js.map +1 -1
  531. package/dist/src/gateway/workspace-fs-file-list.js +1 -1
  532. package/dist/src/gateway/workspace-heartbeat-path.js +1 -2
  533. package/dist/src/gateway/workspace-heartbeat-path.js.map +1 -1
  534. package/dist/src/infra/gateway-process-argv.d.ts +4 -0
  535. package/dist/src/infra/gateway-process-argv.js +26 -0
  536. package/dist/src/infra/gateway-process-argv.js.map +1 -0
  537. package/dist/src/infra/gateway-processes.d.ts +5 -0
  538. package/dist/src/infra/gateway-processes.js +65 -0
  539. package/dist/src/infra/gateway-processes.js.map +1 -0
  540. package/dist/src/infra/rate-limit/failure-limiter.d.ts +50 -0
  541. package/dist/src/infra/rate-limit/failure-limiter.js +100 -0
  542. package/dist/src/infra/rate-limit/failure-limiter.js.map +1 -0
  543. package/dist/src/infra/rate-limit/index.d.ts +5 -0
  544. package/dist/src/infra/rate-limit/index.js +3 -0
  545. package/dist/src/infra/rate-limit/keyed-store.d.ts +34 -0
  546. package/dist/src/infra/rate-limit/keyed-store.js +44 -0
  547. package/dist/src/infra/rate-limit/keyed-store.js.map +1 -0
  548. package/dist/src/infra/rate-limit/rate-limiter.d.ts +39 -0
  549. package/dist/src/infra/rate-limit/rate-limiter.js +65 -0
  550. package/dist/src/infra/rate-limit/rate-limiter.js.map +1 -0
  551. package/dist/src/infra/restart.d.ts +21 -0
  552. package/dist/src/infra/restart.js +122 -0
  553. package/dist/src/infra/restart.js.map +1 -0
  554. package/dist/src/infra/update-check.js +1 -1
  555. package/dist/src/infra/update-lock.js +3 -3
  556. package/dist/src/infra/update-runner.js +1 -1
  557. package/dist/src/infra/update-startup.js +2 -2
  558. package/dist/src/infra/write-file-atomic.js +2 -2
  559. package/dist/src/mcp/channel-bridge.d.ts +0 -6
  560. package/dist/src/mcp/channel-bridge.js +1 -5
  561. package/dist/src/mcp/channel-bridge.js.map +1 -1
  562. package/dist/src/media-shared/http/ssrf-guard.js +1 -1
  563. package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
  564. package/dist/src/providers/index.js +2 -2
  565. package/dist/src/providers/model-registry.js +1 -1
  566. package/dist/src/session/config-store.js +2 -2
  567. package/dist/src/session/parity/jsonl-transcript-io.js +2 -2
  568. package/dist/src/session/parity/sessions-json-file-read.d.ts +2 -1
  569. package/dist/src/session/parity/sessions-json-file-read.js.map +1 -1
  570. package/dist/src/session/parity/sessions-json-file.js +1 -1
  571. package/dist/src/session/parity/transcript-file-lock.js +2 -2
  572. package/dist/src/session/parity/transcript-paths.js +1 -1
  573. package/dist/src/session/search-index-cache.js +1 -1
  574. package/dist/src/session/search-index.js +1 -1
  575. package/dist/src/session/session-title.js +1 -1
  576. package/dist/src/session/store.js +5 -5
  577. package/dist/src/share/share-rate-limit.d.ts +10 -2
  578. package/dist/src/share/share-rate-limit.js +33 -42
  579. package/dist/src/share/share-rate-limit.js.map +1 -1
  580. package/dist/src/share/share-store.js +3 -3
  581. package/dist/src/tui/backends/embedded-backend.js +16 -12
  582. package/dist/src/tui/backends/embedded-backend.js.map +1 -1
  583. package/dist/src/tui/clipboard-image.js +2 -2
  584. package/dist/src/tui/extension-host/load-extensions.js +1 -1
  585. package/dist/src/tui/format-tui-hotkeys.js +1 -1
  586. package/dist/src/tui/theme-manager.js +1 -1
  587. package/dist/src/tui/tui-keybindings-file.js +1 -1
  588. package/dist/src/tui/tui-scoped-models.js +1 -1
  589. package/dist/src/tui/tui-settings.js +1 -1
  590. package/dist/src/tui/tui-skills-autocomplete.js +1 -1
  591. package/dist/src/tui/tui.js +1 -2
  592. package/dist/src/tui/tui.js.map +1 -1
  593. package/dist/src/tui/xopc-tui-keybindings.d.ts +0 -1
  594. package/dist/src/tui/xopc-tui-keybindings.js +1 -2
  595. package/dist/src/tui/xopc-tui-keybindings.js.map +1 -1
  596. package/dist/src/tunnel/frpc-binary.js +2 -2
  597. package/dist/src/tunnel/frpc-config.js +1 -1
  598. package/dist/src/tunnel/frpc-extract.js +1 -1
  599. package/dist/src/tunnel/pairing-rate-limit.d.ts +10 -2
  600. package/dist/src/tunnel/pairing-rate-limit.js +19 -15
  601. package/dist/src/tunnel/pairing-rate-limit.js.map +1 -1
  602. package/dist/src/tunnel/tunnel-rate-limit.d.ts +6 -3
  603. package/dist/src/tunnel/tunnel-rate-limit.js +11 -22
  604. package/dist/src/tunnel/tunnel-rate-limit.js.map +1 -1
  605. package/dist/src/tunnel/tunnel-state.js +1 -1
  606. package/dist/src/utils/logger/audit.js +1 -1
  607. package/dist/src/utils/logger/log-store.js +1 -1
  608. package/dist/src/utils/logger/rotation.js +1 -1
  609. package/dist/src/utils/logger/stats.d.ts +1 -1
  610. package/dist/src/voice/tts/audio.js +1 -1
  611. package/dist/src/voice/tts/factory.js +1 -1
  612. package/dist/src/voice/tts/index.js +2 -2
  613. package/dist/src/voice/tts/merge-config.js +1 -1
  614. package/dist/src/voice/tts/providers/edge-speech.js +1 -1
  615. package/dist/src/voice/tts/service.js +1 -1
  616. package/dist/src/voice/tts/service.js.map +1 -1
  617. package/dist/src/voice/tts/speak-core.js +1 -1
  618. package/package.json +10 -5
  619. package/dist/gateway/static/root/assets/agents-Cqh1ts38.js +0 -222
  620. package/dist/gateway/static/root/assets/channels-settings-wTiWStg9.js +0 -1
  621. package/dist/gateway/static/root/assets/fetch-BAAh_kXG.js +0 -3
  622. package/dist/gateway/static/root/assets/index-C8yHX-AA.css +0 -1
  623. package/dist/gateway/static/root/assets/sessions-page-BeiFm0Ms.js +0 -1
  624. package/dist/gateway/static/root/assets/settings-page-RPAz_Wg_.js +0 -3
  625. package/dist/gateway/static/root/assets/skills-page-Wu4aNWDx.js +0 -2
  626. package/dist/gateway/static/root/assets/voice-api-key-field-BxIGhhEL.js +0 -1
  627. package/dist/src/agent/embedded/session-raw-append-message.d.ts +0 -11
  628. package/dist/src/agent/embedded/session-raw-append-message.js +0 -15
  629. package/dist/src/agent/embedded/session-raw-append-message.js.map +0 -1
  630. package/dist/src/agent/embedded/session-tool-result-guard-wrapper.d.ts +0 -15
  631. package/dist/src/agent/embedded/session-tool-result-guard-wrapper.js +0 -24
  632. package/dist/src/agent/embedded/session-tool-result-guard-wrapper.js.map +0 -1
  633. package/dist/src/agent/embedded/session-tool-result-state.d.ts +0 -17
  634. package/dist/src/agent/embedded/session-tool-result-state.js +0 -26
  635. package/dist/src/agent/embedded/session-tool-result-state.js.map +0 -1
  636. package/dist/src/daemon/launchd-restart-handoff.d.ts +0 -25
  637. package/dist/src/daemon/launchd-restart-handoff.js +0 -132
  638. package/dist/src/daemon/launchd-restart-handoff.js.map +0 -1
  639. package/dist/src/gateway/auth-rate-limit.d.ts +0 -71
  640. package/dist/src/gateway/auth-rate-limit.js +0 -192
  641. package/dist/src/gateway/auth-rate-limit.js.map +0 -1
  642. package/dist/src/gateway/restart-handler.d.ts +0 -14
  643. package/dist/src/gateway/restart-handler.js +0 -64
  644. package/dist/src/gateway/restart-handler.js.map +0 -1
  645. package/dist/src/gateway/security/flood-guard.d.ts +0 -28
  646. package/dist/src/gateway/security/flood-guard.js +0 -42
  647. package/dist/src/gateway/security/flood-guard.js.map +0 -1
  648. package/dist/src/infra/rate-limit.d.ts +0 -38
  649. package/dist/src/infra/rate-limit.js +0 -60
  650. package/dist/src/infra/rate-limit.js.map +0 -1
  651. package/dist/src/infra/restart-intent.d.ts +0 -13
  652. package/dist/src/infra/restart-intent.js +0 -40
  653. package/dist/src/infra/restart-intent.js.map +0 -1
  654. package/dist/src/infra/restart-sentinel.d.ts +0 -23
  655. package/dist/src/infra/restart-sentinel.js +0 -75
  656. package/dist/src/infra/restart-sentinel.js.map +0 -1
  657. package/skills/creative/canvas-design/LICENSE.txt +0 -202
  658. package/skills/creative/canvas-design/SKILL-zh.md +0 -130
  659. package/skills/creative/canvas-design/SKILL.md +0 -130
  660. package/skills/creative/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +0 -93
  661. package/skills/creative/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
  662. package/skills/creative/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
  663. package/skills/creative/canvas-design/canvas-fonts/BigShoulders-OFL.txt +0 -93
  664. package/skills/creative/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
  665. package/skills/creative/canvas-design/canvas-fonts/Boldonse-OFL.txt +0 -93
  666. package/skills/creative/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
  667. package/skills/creative/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
  668. package/skills/creative/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +0 -93
  669. package/skills/creative/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
  670. package/skills/creative/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
  671. package/skills/creative/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
  672. package/skills/creative/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +0 -93
  673. package/skills/creative/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
  674. package/skills/creative/canvas-design/canvas-fonts/DMMono-OFL.txt +0 -93
  675. package/skills/creative/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
  676. package/skills/creative/canvas-design/canvas-fonts/EricaOne-OFL.txt +0 -94
  677. package/skills/creative/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
  678. package/skills/creative/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
  679. package/skills/creative/canvas-design/canvas-fonts/GeistMono-OFL.txt +0 -93
  680. package/skills/creative/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
  681. package/skills/creative/canvas-design/canvas-fonts/Gloock-OFL.txt +0 -93
  682. package/skills/creative/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
  683. package/skills/creative/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
  684. package/skills/creative/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +0 -93
  685. package/skills/creative/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
  686. package/skills/creative/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
  687. package/skills/creative/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
  688. package/skills/creative/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
  689. package/skills/creative/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
  690. package/skills/creative/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
  691. package/skills/creative/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
  692. package/skills/creative/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
  693. package/skills/creative/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +0 -93
  694. package/skills/creative/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
  695. package/skills/creative/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
  696. package/skills/creative/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
  697. package/skills/creative/canvas-design/canvas-fonts/Italiana-OFL.txt +0 -93
  698. package/skills/creative/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
  699. package/skills/creative/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
  700. package/skills/creative/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +0 -93
  701. package/skills/creative/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
  702. package/skills/creative/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
  703. package/skills/creative/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
  704. package/skills/creative/canvas-design/canvas-fonts/Jura-OFL.txt +0 -93
  705. package/skills/creative/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +0 -93
  706. package/skills/creative/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
  707. package/skills/creative/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
  708. package/skills/creative/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
  709. package/skills/creative/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
  710. package/skills/creative/canvas-design/canvas-fonts/Lora-OFL.txt +0 -93
  711. package/skills/creative/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
  712. package/skills/creative/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
  713. package/skills/creative/canvas-design/canvas-fonts/NationalPark-OFL.txt +0 -93
  714. package/skills/creative/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
  715. package/skills/creative/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +0 -93
  716. package/skills/creative/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
  717. package/skills/creative/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
  718. package/skills/creative/canvas-design/canvas-fonts/Outfit-OFL.txt +0 -93
  719. package/skills/creative/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
  720. package/skills/creative/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
  721. package/skills/creative/canvas-design/canvas-fonts/PixelifySans-OFL.txt +0 -93
  722. package/skills/creative/canvas-design/canvas-fonts/PoiretOne-OFL.txt +0 -93
  723. package/skills/creative/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
  724. package/skills/creative/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
  725. package/skills/creative/canvas-design/canvas-fonts/RedHatMono-OFL.txt +0 -93
  726. package/skills/creative/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
  727. package/skills/creative/canvas-design/canvas-fonts/Silkscreen-OFL.txt +0 -93
  728. package/skills/creative/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
  729. package/skills/creative/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
  730. package/skills/creative/canvas-design/canvas-fonts/SmoochSans-OFL.txt +0 -93
  731. package/skills/creative/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
  732. package/skills/creative/canvas-design/canvas-fonts/Tektur-OFL.txt +0 -93
  733. package/skills/creative/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
  734. package/skills/creative/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
  735. package/skills/creative/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
  736. package/skills/creative/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
  737. package/skills/creative/canvas-design/canvas-fonts/WorkSans-OFL.txt +0 -93
  738. package/skills/creative/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
  739. package/skills/creative/canvas-design/canvas-fonts/YoungSerif-OFL.txt +0 -93
  740. package/skills/creative/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"tunnel.js","names":[],"sources":["../../../../../src/gateway/hono/routes/tunnel.ts"],"sourcesContent":["import type { Hono, MiddlewareHandler } from 'hono';\n\nimport type { Config } from '../../../config/schema.js';\nimport { resolveGatewayEffectiveHost } from '../../../config/gateway-bind.js';\nimport { extractToken } from '../../auth.js';\nimport {\n assertTunnelMayStart,\n getTunnelConsentState,\n TUNNEL_CONSENT_REQUIRED_CODE,\n TunnelConsentError,\n} from '../../../tunnel/consent.js';\nimport { hashGatewayToken } from '../../../tunnel/tunnel-service.js';\nimport { configureTunnelFromGatewayConfig } from '../../../tunnel/gateway-lifecycle.js';\nimport {\n getTunnelRegistrationSecretMeta,\n readTunnelRegistrationSecretFromConfigOnly,\n resolveTunnelBrokerUrl,\n} from '../../../tunnel/env.js';\nimport { getTunnelService } from '../../../tunnel/index.js';\nimport { createPairingSecret, exchangePairingSecretOnce, getCachedPairingExchange } from '../../../tunnel/pairing.js';\nimport { buildMobilePairContext } from '../../../tunnel/pair-context.js';\nimport { applyLanPairingGatewayPatch } from '../../../tunnel/enable-lan-pairing.js';\nimport {\n buildMobileConnectUrlOrder,\n resolveMobilePairLanUrl,\n validateMobilePairBaseUrl,\n} from '../../../tunnel/pair-url.js';\nimport { consumePairingExchangeFailLimit } from '../../../tunnel/pairing-rate-limit.js';\nimport { loadTunnelState } from '../../../tunnel/tunnel-state.js';\nimport { logTunnelAudit } from '../../../tunnel/tunnel-audit.js';\nimport {\n applyTunnelConsentToConfig,\n setTunnelEnabledInConfig,\n} from '../../../tunnel/tunnel-config.js';\nimport { consumeTunnelMutationLimit } from '../../../tunnel/tunnel-rate-limit.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\nimport type { GatewayService } from '../../service.js';\nimport { getClientIpFromHeaders } from '../../auth-rate-limit.js';\n\nasync function configureTunnelFromService(\n deps: AuthenticatedRouteDeps,\n opts?: { force?: boolean },\n): Promise<void> {\n await configureTunnelFromGatewayConfig(deps.service.currentConfig, opts);\n}\n\nfunction enrichTunnelStatus(config: Config, status: ReturnType<ReturnType<typeof getTunnelService>['getStatus']>) {\n const consent = getTunnelConsentState(config);\n const brokerUrl = resolveTunnelBrokerUrl(config.tunnel?.brokerUrl);\n const registrationSecret = getTunnelRegistrationSecretMeta(config, process.env, brokerUrl);\n return {\n ...status,\n consentRequired: consent.consentRequired,\n consent: {\n currentVersion: consent.currentVersion,\n acceptedVersion: consent.acceptedVersion,\n acceptedAt: consent.acceptedAt,\n valid: consent.valid,\n },\n canAutoStart: consent.canAutoStart,\n registrationSecret,\n };\n}\n\nfunction requireGatewayToken(c: { req: { header: (name: string) => string | undefined } }): string | null {\n return (\n extractToken({\n authorization: c.req.header('authorization') ?? undefined,\n }) ?? null\n );\n}\n\nfunction createTunnelMutationRateLimitMiddleware(): MiddlewareHandler {\n return async (c, next) => {\n const token = requireGatewayToken(c);\n if (!token) {\n return c.json({ error: 'Gateway token required' }, 401);\n }\n const result = consumeTunnelMutationLimit(token);\n if (!result.allowed) {\n c.header('Retry-After', String(Math.ceil(result.retryAfterMs / 1000)));\n return c.json(\n {\n error: 'Too many tunnel operations. Try again later.',\n code: 'TUNNEL_RATE_LIMITED',\n retryAfterMs: result.retryAfterMs,\n },\n 429,\n );\n }\n await next();\n };\n}\n\nexport function registerTunnelPublicRoutes(app: Hono, service: GatewayService): void {\n app.get('/api/tunnel/pair/ping', async (c) => {\n const config = service.currentConfig as Config;\n const tunnel = getTunnelService();\n const status = tunnel.getStatus();\n const context = buildMobilePairContext({\n config,\n tunnelPublicUrl: status.publicUrl,\n tunnelConnected: status.state === 'connected',\n });\n return c.json({\n ok: true,\n service: 'xopc-gateway',\n mobilePairing: true,\n port: context.port,\n bindMode: context.bindMode,\n listenHost: context.listenHost,\n pairingReady: context.pairingReady,\n blockReason: context.blockReason ?? null,\n tunnelConnected: status.state === 'connected',\n connectUrls: context.connectUrls,\n });\n });\n\n app.post('/api/tunnel/pair/validate-url', async (c) => {\n let body: { baseUrl?: unknown };\n try {\n body = (await c.req.json()) as { baseUrl?: unknown };\n } catch {\n return c.json({ error: 'Invalid JSON body' }, 400);\n }\n const baseUrl = typeof body.baseUrl === 'string' ? body.baseUrl : '';\n const result = validateMobilePairBaseUrl(baseUrl);\n if (result.ok === false) {\n return c.json({\n ok: false,\n code: result.code,\n message: result.message,\n });\n }\n return c.json({\n ok: true,\n url: result.url,\n loopback: false,\n probePath: '/api/tunnel/pair/ping',\n });\n });\n\n app.post('/api/tunnel/exchange-token', async (c) => {\n const clientIp =\n getClientIpFromHeaders({\n get: (name: string) => c.req.header(name) ?? undefined,\n }) ?? 'unknown';\n\n let body: { pairingSecret?: unknown };\n try {\n body = (await c.req.json()) as { pairingSecret?: unknown };\n } catch {\n return c.json({ error: 'Invalid JSON body' }, 400);\n }\n\n const pairingSecret = typeof body.pairingSecret === 'string' ? body.pairingSecret.trim() : '';\n if (!pairingSecret) {\n return c.json({ error: 'pairingSecret required' }, 400);\n }\n\n const cached = getCachedPairingExchange(pairingSecret);\n if (cached) {\n logTunnelAudit(\n 'tunnel.exchange_token',\n { ok: true, clientIp, phase: 'pairing_exchange', replay: true },\n 'Pairing secret replayed (duplicate mobile exchange)',\n );\n return c.json(cached);\n }\n\n const token = service.getAuthToken();\n if (!token) {\n return c.json({ error: 'Gateway token not configured' }, 500);\n }\n\n const persisted = loadTunnelState();\n const config = service.currentConfig as Config;\n const publicUrl = persisted?.publicUrl?.trim() || null;\n const lanUrl = resolveMobilePairLanUrl(config);\n const connectUrls = buildMobileConnectUrlOrder({ baseUrl: publicUrl, lanUrl });\n\n const payload = await exchangePairingSecretOnce(pairingSecret, () => ({\n token,\n baseUrl: publicUrl,\n lanUrl,\n connectUrls,\n }));\n\n if (!payload) {\n const limited = consumePairingExchangeFailLimit(clientIp);\n if (!limited.allowed) {\n c.header('Retry-After', String(Math.ceil(limited.retryAfterMs / 1000)));\n }\n logTunnelAudit(\n 'tunnel.exchange_token',\n { ok: false, clientIp, phase: 'pairing_exchange' },\n 'Pairing exchange denied: invalid or expired secret',\n );\n return c.json({ error: 'Invalid or expired pairing secret', code: 'PAIRING_INVALID' }, 401);\n }\n\n logTunnelAudit(\n 'tunnel.exchange_token',\n { ok: true, clientIp, subdomain: persisted?.subdomain ?? null, phase: 'pairing_exchange' },\n 'Pairing secret exchanged for gateway token',\n );\n return c.json(payload);\n });\n}\n\nexport function registerTunnelRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { strictRateLimitMiddleware } = deps;\n const tunnel = getTunnelService();\n const tunnelMutationLimit = createTunnelMutationRateLimitMiddleware();\n\n authenticated.get('/api/tunnel/pair/context', async (c) => {\n await configureTunnelFromService(deps);\n const config = deps.service.currentConfig as Config;\n const status = tunnel.getStatus();\n const context = buildMobilePairContext({\n config,\n tunnelPublicUrl: status.publicUrl,\n tunnelConnected: status.state === 'connected',\n });\n return c.json(context);\n });\n\n authenticated.post('/api/tunnel/pair/enable-lan', tunnelMutationLimit, async (c) => {\n const token = requireGatewayToken(c);\n if (!token) return c.json({ error: 'Gateway token required' }, 401);\n\n const config = deps.service.currentConfig as Config;\n const patchResult = applyLanPairingGatewayPatch(config);\n if (patchResult.ok === false) {\n return c.json({ ok: false, error: { message: patchResult.message, code: 'LAN_PAIRING_CONFIG' } }, 400);\n }\n\n if (patchResult.changed) {\n const saveResult = await deps.service.saveConfig(config);\n if (!saveResult.saved) {\n return c.json(\n { ok: false, error: { message: saveResult.error ?? 'Failed to save config', code: 'SAVE_FAILED' } },\n 500,\n );\n }\n logTunnelAudit(\n 'tunnel.enable_lan_pairing',\n { gatewayTokenHash: hashGatewayToken(token).slice(0, 12) },\n 'Gateway bind switched to LAN for mobile pairing',\n );\n }\n\n const status = tunnel.getStatus();\n let context = buildMobilePairContext({\n config: deps.service.currentConfig as Config,\n tunnelPublicUrl: status.publicUrl,\n tunnelConnected: status.state === 'connected',\n });\n\n if (patchResult.changed) {\n context = {\n ...context,\n pairingReady: false,\n blockReason: 'GATEWAY_LOOPBACK_ONLY',\n };\n }\n\n return c.json({\n ok: true,\n requiresRestart: patchResult.changed,\n context,\n });\n });\n\n authenticated.post('/api/tunnel/pair', tunnelMutationLimit, async (c) => {\n await configureTunnelFromService(deps);\n const token = requireGatewayToken(c);\n if (!token) return c.json({ error: 'Gateway token required' }, 401);\n\n const { secret, expiresAt } = createPairingSecret();\n logTunnelAudit(\n 'tunnel.pair',\n {\n expiresAt: expiresAt.toISOString(),\n gatewayTokenHash: hashGatewayToken(token).slice(0, 12),\n },\n 'Mobile pairing session created',\n );\n return c.json({ pairingSecret: secret, expiresAt: expiresAt.toISOString() });\n });\n\n authenticated.get('/api/tunnel/status', async (c) => {\n await configureTunnelFromService(deps);\n const config = deps.service.currentConfig as Config;\n return c.json(enrichTunnelStatus(config, tunnel.getStatus()));\n });\n\n authenticated.post('/api/tunnel/consent', tunnelMutationLimit, async (c) => {\n const token = requireGatewayToken(c);\n if (!token) return c.json({ error: 'Gateway token required' }, 401);\n\n const config = deps.service.currentConfig as Config;\n applyTunnelConsentToConfig(config);\n const result = await deps.service.saveConfig(config);\n if (!result.saved) {\n return c.json({ ok: false, error: result.error ?? 'Failed to save config' }, 500);\n }\n const consent = getTunnelConsentState(config);\n logTunnelAudit(\n 'tunnel.consent',\n {\n consentVersion: consent.currentVersion,\n gatewayTokenHash: hashGatewayToken(token).slice(0, 12),\n },\n 'Remote access security consent recorded',\n );\n return c.json({\n ok: true,\n consent: {\n currentVersion: consent.currentVersion,\n acceptedVersion: consent.acceptedVersion,\n acceptedAt: consent.acceptedAt,\n valid: consent.valid,\n },\n });\n });\n\n authenticated.post('/api/tunnel/start', tunnelMutationLimit, async (c) => {\n await configureTunnelFromService(deps, { force: true });\n const config = deps.service.currentConfig as Config;\n const token = requireGatewayToken(c);\n if (!token) return c.json({ error: 'Gateway token required' }, 401);\n\n try {\n assertTunnelMayStart(config);\n } catch (err) {\n if (err instanceof TunnelConsentError) {\n logTunnelAudit(\n 'tunnel.start_denied',\n { reason: TUNNEL_CONSENT_REQUIRED_CODE, gatewayTokenHash: hashGatewayToken(token).slice(0, 12) },\n 'Tunnel start denied: consent required',\n );\n return c.json({ error: err.message, code: TUNNEL_CONSENT_REQUIRED_CODE }, 403);\n }\n throw err;\n }\n\n const gateway = config.gateway;\n const port = gateway.port ?? 18790;\n try {\n const qr = await tunnel.start(port, token);\n setTunnelEnabledInConfig(config, true);\n await deps.service.saveConfig(config);\n const status = tunnel.getStatus();\n return c.json({\n publicUrl: qr.publicUrl,\n subdomain: status.subdomain,\n qrPayload: qr.qrPayload,\n lanUrl: qr.lanUrl,\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return c.json({ error: message }, 500);\n }\n });\n\n authenticated.post('/api/tunnel/stop', tunnelMutationLimit, async (c) => {\n await configureTunnelFromService(deps);\n const config = deps.service.currentConfig as Config;\n let release = false;\n try {\n const body = (await c.req.json().catch(() => ({}))) as { release?: unknown };\n release = body.release === true;\n } catch {\n release = false;\n }\n const { released } = await tunnel.stop({ release });\n setTunnelEnabledInConfig(config, false);\n await deps.service.saveConfig(config);\n return c.json({ ok: true, released });\n });\n\n authenticated.get('/api/tunnel/qr', async (c) => {\n await configureTunnelFromService(deps);\n const gateway = deps.service.currentConfig.gateway;\n const port = gateway.port ?? 18790;\n const host = resolveGatewayEffectiveHost(deps.service.currentConfig);\n const token = requireGatewayToken(c);\n if (!token) return c.json({ error: 'Gateway token required' }, 401);\n const qr = await tunnel.buildQr(port, host);\n return c.json(qr);\n });\n\n authenticated.get('/api/tunnel/transport-status', async (c) => {\n await configureTunnelFromService(deps);\n return c.json({\n transport: { tls: 'broker_terminated' as const },\n });\n });\n\n /**\n * POST /api/tunnel/reveal-registration-secret — plaintext only when stored in config file.\n */\n authenticated.post('/api/tunnel/reveal-registration-secret', strictRateLimitMiddleware, async (c) => {\n const config = deps.service.currentConfig as Config;\n const registrationSecret = readTunnelRegistrationSecretFromConfigOnly(config);\n return c.json({\n ok: true,\n payload: {\n registrationSecret,\n source: registrationSecret ? ('config' as const) : ('none' as const),\n },\n });\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"tunnel.js","names":[],"sources":["../../../../../src/gateway/hono/routes/tunnel.ts"],"sourcesContent":["import type { Hono, MiddlewareHandler } from 'hono';\n\nimport type { Config } from '../../../config/schema.js';\nimport { resolveGatewayEffectiveHost } from '../../../config/gateway-bind.js';\nimport { extractToken } from '../../auth.js';\nimport {\n assertTunnelMayStart,\n getTunnelConsentState,\n TUNNEL_CONSENT_REQUIRED_CODE,\n TunnelConsentError,\n} from '../../../tunnel/consent.js';\nimport { hashGatewayToken } from '../../../tunnel/tunnel-service.js';\nimport { configureTunnelFromGatewayConfig } from '../../../tunnel/gateway-lifecycle.js';\nimport {\n getTunnelRegistrationSecretMeta,\n readTunnelRegistrationSecretFromConfigOnly,\n resolveTunnelBrokerUrl,\n} from '../../../tunnel/env.js';\nimport { getTunnelService } from '../../../tunnel/index.js';\nimport { createPairingSecret, exchangePairingSecretOnce, getCachedPairingExchange } from '../../../tunnel/pairing.js';\nimport { buildMobilePairContext } from '../../../tunnel/pair-context.js';\nimport { applyLanPairingGatewayPatch } from '../../../tunnel/enable-lan-pairing.js';\nimport {\n buildMobileConnectUrlOrder,\n resolveMobilePairLanUrl,\n validateMobilePairBaseUrl,\n} from '../../../tunnel/pair-url.js';\nimport { consumePairingExchangeFailLimit } from '../../../tunnel/pairing-rate-limit.js';\nimport { loadTunnelState } from '../../../tunnel/tunnel-state.js';\nimport { logTunnelAudit } from '../../../tunnel/tunnel-audit.js';\nimport {\n applyTunnelConsentToConfig,\n setTunnelEnabledInConfig,\n} from '../../../tunnel/tunnel-config.js';\nimport { consumeTunnelMutationLimit } from '../../../tunnel/tunnel-rate-limit.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\nimport type { GatewayService } from '../../service.js';\nimport { getClientIpFromHeaders } from '../../security/loopback.js';\n\nasync function configureTunnelFromService(\n deps: AuthenticatedRouteDeps,\n opts?: { force?: boolean },\n): Promise<void> {\n await configureTunnelFromGatewayConfig(deps.service.currentConfig, opts);\n}\n\nfunction enrichTunnelStatus(config: Config, status: ReturnType<ReturnType<typeof getTunnelService>['getStatus']>) {\n const consent = getTunnelConsentState(config);\n const brokerUrl = resolveTunnelBrokerUrl(config.tunnel?.brokerUrl);\n const registrationSecret = getTunnelRegistrationSecretMeta(config, process.env, brokerUrl);\n return {\n ...status,\n consentRequired: consent.consentRequired,\n consent: {\n currentVersion: consent.currentVersion,\n acceptedVersion: consent.acceptedVersion,\n acceptedAt: consent.acceptedAt,\n valid: consent.valid,\n },\n canAutoStart: consent.canAutoStart,\n registrationSecret,\n };\n}\n\nfunction requireGatewayToken(c: { req: { header: (name: string) => string | undefined } }): string | null {\n return (\n extractToken({\n authorization: c.req.header('authorization') ?? undefined,\n }) ?? null\n );\n}\n\nfunction createTunnelMutationRateLimitMiddleware(): MiddlewareHandler {\n return async (c, next) => {\n const token = requireGatewayToken(c);\n if (!token) {\n return c.json({ error: 'Gateway token required' }, 401);\n }\n const result = consumeTunnelMutationLimit(token);\n if (!result.allowed) {\n c.header('Retry-After', String(Math.ceil(result.retryAfterMs / 1000)));\n return c.json(\n {\n error: 'Too many tunnel operations. Try again later.',\n code: 'TUNNEL_RATE_LIMITED',\n retryAfterMs: result.retryAfterMs,\n },\n 429,\n );\n }\n await next();\n };\n}\n\nexport function registerTunnelPublicRoutes(app: Hono, service: GatewayService): void {\n app.get('/api/tunnel/pair/ping', async (c) => {\n const config = service.currentConfig as Config;\n const tunnel = getTunnelService();\n const status = tunnel.getStatus();\n const context = buildMobilePairContext({\n config,\n tunnelPublicUrl: status.publicUrl,\n tunnelConnected: status.state === 'connected',\n });\n return c.json({\n ok: true,\n service: 'xopc-gateway',\n mobilePairing: true,\n port: context.port,\n bindMode: context.bindMode,\n listenHost: context.listenHost,\n pairingReady: context.pairingReady,\n blockReason: context.blockReason ?? null,\n tunnelConnected: status.state === 'connected',\n connectUrls: context.connectUrls,\n });\n });\n\n app.post('/api/tunnel/pair/validate-url', async (c) => {\n let body: { baseUrl?: unknown };\n try {\n body = (await c.req.json()) as { baseUrl?: unknown };\n } catch {\n return c.json({ error: 'Invalid JSON body' }, 400);\n }\n const baseUrl = typeof body.baseUrl === 'string' ? body.baseUrl : '';\n const result = validateMobilePairBaseUrl(baseUrl);\n if (result.ok === false) {\n return c.json({\n ok: false,\n code: result.code,\n message: result.message,\n });\n }\n return c.json({\n ok: true,\n url: result.url,\n loopback: false,\n probePath: '/api/tunnel/pair/ping',\n });\n });\n\n app.post('/api/tunnel/exchange-token', async (c) => {\n const clientIp =\n getClientIpFromHeaders({\n get: (name: string) => c.req.header(name) ?? undefined,\n }) ?? 'unknown';\n\n let body: { pairingSecret?: unknown };\n try {\n body = (await c.req.json()) as { pairingSecret?: unknown };\n } catch {\n return c.json({ error: 'Invalid JSON body' }, 400);\n }\n\n const pairingSecret = typeof body.pairingSecret === 'string' ? body.pairingSecret.trim() : '';\n if (!pairingSecret) {\n return c.json({ error: 'pairingSecret required' }, 400);\n }\n\n const cached = getCachedPairingExchange(pairingSecret);\n if (cached) {\n logTunnelAudit(\n 'tunnel.exchange_token',\n { ok: true, clientIp, phase: 'pairing_exchange', replay: true },\n 'Pairing secret replayed (duplicate mobile exchange)',\n );\n return c.json(cached);\n }\n\n const token = service.getAuthToken();\n if (!token) {\n return c.json({ error: 'Gateway token not configured' }, 500);\n }\n\n const persisted = loadTunnelState();\n const config = service.currentConfig as Config;\n const publicUrl = persisted?.publicUrl?.trim() || null;\n const lanUrl = resolveMobilePairLanUrl(config);\n const connectUrls = buildMobileConnectUrlOrder({ baseUrl: publicUrl, lanUrl });\n\n const payload = await exchangePairingSecretOnce(pairingSecret, () => ({\n token,\n baseUrl: publicUrl,\n lanUrl,\n connectUrls,\n }));\n\n if (!payload) {\n const limited = consumePairingExchangeFailLimit(clientIp);\n if (!limited.allowed) {\n c.header('Retry-After', String(Math.ceil(limited.retryAfterMs / 1000)));\n }\n logTunnelAudit(\n 'tunnel.exchange_token',\n { ok: false, clientIp, phase: 'pairing_exchange' },\n 'Pairing exchange denied: invalid or expired secret',\n );\n return c.json({ error: 'Invalid or expired pairing secret', code: 'PAIRING_INVALID' }, 401);\n }\n\n logTunnelAudit(\n 'tunnel.exchange_token',\n { ok: true, clientIp, subdomain: persisted?.subdomain ?? null, phase: 'pairing_exchange' },\n 'Pairing secret exchanged for gateway token',\n );\n return c.json(payload);\n });\n}\n\nexport function registerTunnelRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { strictRateLimitMiddleware } = deps;\n const tunnel = getTunnelService();\n const tunnelMutationLimit = createTunnelMutationRateLimitMiddleware();\n\n authenticated.get('/api/tunnel/pair/context', async (c) => {\n await configureTunnelFromService(deps);\n const config = deps.service.currentConfig as Config;\n const status = tunnel.getStatus();\n const context = buildMobilePairContext({\n config,\n tunnelPublicUrl: status.publicUrl,\n tunnelConnected: status.state === 'connected',\n });\n return c.json(context);\n });\n\n authenticated.post('/api/tunnel/pair/enable-lan', tunnelMutationLimit, async (c) => {\n const token = requireGatewayToken(c);\n if (!token) return c.json({ error: 'Gateway token required' }, 401);\n\n const config = deps.service.currentConfig as Config;\n const patchResult = applyLanPairingGatewayPatch(config);\n if (patchResult.ok === false) {\n return c.json({ ok: false, error: { message: patchResult.message, code: 'LAN_PAIRING_CONFIG' } }, 400);\n }\n\n if (patchResult.changed) {\n const saveResult = await deps.service.saveConfig(config);\n if (!saveResult.saved) {\n return c.json(\n { ok: false, error: { message: saveResult.error ?? 'Failed to save config', code: 'SAVE_FAILED' } },\n 500,\n );\n }\n logTunnelAudit(\n 'tunnel.enable_lan_pairing',\n { gatewayTokenHash: hashGatewayToken(token).slice(0, 12) },\n 'Gateway bind switched to LAN for mobile pairing',\n );\n }\n\n const status = tunnel.getStatus();\n let context = buildMobilePairContext({\n config: deps.service.currentConfig as Config,\n tunnelPublicUrl: status.publicUrl,\n tunnelConnected: status.state === 'connected',\n });\n\n if (patchResult.changed) {\n context = {\n ...context,\n pairingReady: false,\n blockReason: 'GATEWAY_LOOPBACK_ONLY',\n };\n }\n\n return c.json({\n ok: true,\n requiresRestart: patchResult.changed,\n context,\n });\n });\n\n authenticated.post('/api/tunnel/pair', tunnelMutationLimit, async (c) => {\n await configureTunnelFromService(deps);\n const token = requireGatewayToken(c);\n if (!token) return c.json({ error: 'Gateway token required' }, 401);\n\n const { secret, expiresAt } = createPairingSecret();\n logTunnelAudit(\n 'tunnel.pair',\n {\n expiresAt: expiresAt.toISOString(),\n gatewayTokenHash: hashGatewayToken(token).slice(0, 12),\n },\n 'Mobile pairing session created',\n );\n return c.json({ pairingSecret: secret, expiresAt: expiresAt.toISOString() });\n });\n\n authenticated.get('/api/tunnel/status', async (c) => {\n await configureTunnelFromService(deps);\n const config = deps.service.currentConfig as Config;\n return c.json(enrichTunnelStatus(config, tunnel.getStatus()));\n });\n\n authenticated.post('/api/tunnel/consent', tunnelMutationLimit, async (c) => {\n const token = requireGatewayToken(c);\n if (!token) return c.json({ error: 'Gateway token required' }, 401);\n\n const config = deps.service.currentConfig as Config;\n applyTunnelConsentToConfig(config);\n const result = await deps.service.saveConfig(config);\n if (!result.saved) {\n return c.json({ ok: false, error: result.error ?? 'Failed to save config' }, 500);\n }\n const consent = getTunnelConsentState(config);\n logTunnelAudit(\n 'tunnel.consent',\n {\n consentVersion: consent.currentVersion,\n gatewayTokenHash: hashGatewayToken(token).slice(0, 12),\n },\n 'Remote access security consent recorded',\n );\n return c.json({\n ok: true,\n consent: {\n currentVersion: consent.currentVersion,\n acceptedVersion: consent.acceptedVersion,\n acceptedAt: consent.acceptedAt,\n valid: consent.valid,\n },\n });\n });\n\n authenticated.post('/api/tunnel/start', tunnelMutationLimit, async (c) => {\n await configureTunnelFromService(deps, { force: true });\n const config = deps.service.currentConfig as Config;\n const token = requireGatewayToken(c);\n if (!token) return c.json({ error: 'Gateway token required' }, 401);\n\n try {\n assertTunnelMayStart(config);\n } catch (err) {\n if (err instanceof TunnelConsentError) {\n logTunnelAudit(\n 'tunnel.start_denied',\n { reason: TUNNEL_CONSENT_REQUIRED_CODE, gatewayTokenHash: hashGatewayToken(token).slice(0, 12) },\n 'Tunnel start denied: consent required',\n );\n return c.json({ error: err.message, code: TUNNEL_CONSENT_REQUIRED_CODE }, 403);\n }\n throw err;\n }\n\n const gateway = config.gateway;\n const port = gateway.port ?? 18790;\n try {\n const qr = await tunnel.start(port, token);\n setTunnelEnabledInConfig(config, true);\n await deps.service.saveConfig(config);\n const status = tunnel.getStatus();\n return c.json({\n publicUrl: qr.publicUrl,\n subdomain: status.subdomain,\n qrPayload: qr.qrPayload,\n lanUrl: qr.lanUrl,\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return c.json({ error: message }, 500);\n }\n });\n\n authenticated.post('/api/tunnel/stop', tunnelMutationLimit, async (c) => {\n await configureTunnelFromService(deps);\n const config = deps.service.currentConfig as Config;\n let release = false;\n try {\n const body = (await c.req.json().catch(() => ({}))) as { release?: unknown };\n release = body.release === true;\n } catch {\n release = false;\n }\n const { released } = await tunnel.stop({ release });\n setTunnelEnabledInConfig(config, false);\n await deps.service.saveConfig(config);\n return c.json({ ok: true, released });\n });\n\n authenticated.get('/api/tunnel/qr', async (c) => {\n await configureTunnelFromService(deps);\n const gateway = deps.service.currentConfig.gateway;\n const port = gateway.port ?? 18790;\n const host = resolveGatewayEffectiveHost(deps.service.currentConfig);\n const token = requireGatewayToken(c);\n if (!token) return c.json({ error: 'Gateway token required' }, 401);\n const qr = await tunnel.buildQr(port, host);\n return c.json(qr);\n });\n\n authenticated.get('/api/tunnel/transport-status', async (c) => {\n await configureTunnelFromService(deps);\n return c.json({\n transport: { tls: 'broker_terminated' as const },\n });\n });\n\n /**\n * POST /api/tunnel/reveal-registration-secret — plaintext only when stored in config file.\n */\n authenticated.post('/api/tunnel/reveal-registration-secret', strictRateLimitMiddleware, async (c) => {\n const config = deps.service.currentConfig as Config;\n const registrationSecret = readTunnelRegistrationSecretFromConfigOnly(config);\n return c.json({\n ok: true,\n payload: {\n registrationSecret,\n source: registrationSecret ? ('config' as const) : ('none' as const),\n },\n });\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAuCA,eAAe,2BACb,MACA,MACe;AACf,OAAM,iCAAiC,KAAK,QAAQ,eAAe,KAAK;;AAG1E,SAAS,mBAAmB,QAAgB,QAAsE;CAChH,MAAM,UAAU,sBAAsB,OAAO;CAC7C,MAAM,YAAY,uBAAuB,OAAO,QAAQ,UAAU;CAClE,MAAM,qBAAqB,gCAAgC,QAAQ,QAAQ,KAAK,UAAU;AAC1F,QAAO;EACL,GAAG;EACH,iBAAiB,QAAQ;EACzB,SAAS;GACP,gBAAgB,QAAQ;GACxB,iBAAiB,QAAQ;GACzB,YAAY,QAAQ;GACpB,OAAO,QAAQ;GAChB;EACD,cAAc,QAAQ;EACtB;EACD;;AAGH,SAAS,oBAAoB,GAA6E;AACxG,QACE,aAAa,EACX,eAAe,EAAE,IAAI,OAAO,gBAAgB,IAAI,KAAA,GACjD,CAAC,IAAI;;AAIV,SAAS,0CAA6D;AACpE,QAAO,OAAO,GAAG,SAAS;EACxB,MAAM,QAAQ,oBAAoB,EAAE;AACpC,MAAI,CAAC,MACH,QAAO,EAAE,KAAK,EAAE,OAAO,0BAA0B,EAAE,IAAI;EAEzD,MAAM,SAAS,2BAA2B,MAAM;AAChD,MAAI,CAAC,OAAO,SAAS;AACnB,KAAE,OAAO,eAAe,OAAO,KAAK,KAAK,OAAO,eAAe,IAAK,CAAC,CAAC;AACtE,UAAO,EAAE,KACP;IACE,OAAO;IACP,MAAM;IACN,cAAc,OAAO;IACtB,EACD,IACD;;AAEH,QAAM,MAAM;;;AAIhB,SAAgB,2BAA2B,KAAW,SAA+B;AACnF,KAAI,IAAI,yBAAyB,OAAO,MAAM;EAC5C,MAAM,SAAS,QAAQ;EAEvB,MAAM,SADS,kBACM,CAAC,WAAW;EACjC,MAAM,UAAU,uBAAuB;GACrC;GACA,iBAAiB,OAAO;GACxB,iBAAiB,OAAO,UAAU;GACnC,CAAC;AACF,SAAO,EAAE,KAAK;GACZ,IAAI;GACJ,SAAS;GACT,eAAe;GACf,MAAM,QAAQ;GACd,UAAU,QAAQ;GAClB,YAAY,QAAQ;GACpB,cAAc,QAAQ;GACtB,aAAa,QAAQ,eAAe;GACpC,iBAAiB,OAAO,UAAU;GAClC,aAAa,QAAQ;GACtB,CAAC;GACF;AAEF,KAAI,KAAK,iCAAiC,OAAO,MAAM;EACrD,IAAI;AACJ,MAAI;AACF,UAAQ,MAAM,EAAE,IAAI,MAAM;UACpB;AACN,UAAO,EAAE,KAAK,EAAE,OAAO,qBAAqB,EAAE,IAAI;;EAGpD,MAAM,SAAS,0BADC,OAAO,KAAK,YAAY,WAAW,KAAK,UAAU,GACjB;AACjD,MAAI,OAAO,OAAO,MAChB,QAAO,EAAE,KAAK;GACZ,IAAI;GACJ,MAAM,OAAO;GACb,SAAS,OAAO;GACjB,CAAC;AAEJ,SAAO,EAAE,KAAK;GACZ,IAAI;GACJ,KAAK,OAAO;GACZ,UAAU;GACV,WAAW;GACZ,CAAC;GACF;AAEF,KAAI,KAAK,8BAA8B,OAAO,MAAM;EAClD,MAAM,WACJ,uBAAuB,EACrB,MAAM,SAAiB,EAAE,IAAI,OAAO,KAAK,IAAI,KAAA,GAC9C,CAAC,IAAI;EAER,IAAI;AACJ,MAAI;AACF,UAAQ,MAAM,EAAE,IAAI,MAAM;UACpB;AACN,UAAO,EAAE,KAAK,EAAE,OAAO,qBAAqB,EAAE,IAAI;;EAGpD,MAAM,gBAAgB,OAAO,KAAK,kBAAkB,WAAW,KAAK,cAAc,MAAM,GAAG;AAC3F,MAAI,CAAC,cACH,QAAO,EAAE,KAAK,EAAE,OAAO,0BAA0B,EAAE,IAAI;EAGzD,MAAM,SAAS,yBAAyB,cAAc;AACtD,MAAI,QAAQ;AACV,kBACE,yBACA;IAAE,IAAI;IAAM;IAAU,OAAO;IAAoB,QAAQ;IAAM,EAC/D,sDACD;AACD,UAAO,EAAE,KAAK,OAAO;;EAGvB,MAAM,QAAQ,QAAQ,cAAc;AACpC,MAAI,CAAC,MACH,QAAO,EAAE,KAAK,EAAE,OAAO,gCAAgC,EAAE,IAAI;EAG/D,MAAM,YAAY,iBAAiB;EACnC,MAAM,SAAS,QAAQ;EACvB,MAAM,YAAY,WAAW,WAAW,MAAM,IAAI;EAClD,MAAM,SAAS,wBAAwB,OAAO;EAC9C,MAAM,cAAc,2BAA2B;GAAE,SAAS;GAAW;GAAQ,CAAC;EAE9E,MAAM,UAAU,MAAM,0BAA0B,sBAAsB;GACpE;GACA,SAAS;GACT;GACA;GACD,EAAE;AAEH,MAAI,CAAC,SAAS;GACZ,MAAM,UAAU,gCAAgC,SAAS;AACzD,OAAI,CAAC,QAAQ,QACX,GAAE,OAAO,eAAe,OAAO,KAAK,KAAK,QAAQ,eAAe,IAAK,CAAC,CAAC;AAEzE,kBACE,yBACA;IAAE,IAAI;IAAO;IAAU,OAAO;IAAoB,EAClD,qDACD;AACD,UAAO,EAAE,KAAK;IAAE,OAAO;IAAqC,MAAM;IAAmB,EAAE,IAAI;;AAG7F,iBACE,yBACA;GAAE,IAAI;GAAM;GAAU,WAAW,WAAW,aAAa;GAAM,OAAO;GAAoB,EAC1F,6CACD;AACD,SAAO,EAAE,KAAK,QAAQ;GACtB;;AAGJ,SAAgB,qBAAqB,eAAqB,MAAoC;CAC5F,MAAM,EAAE,8BAA8B;CACtC,MAAM,SAAS,kBAAkB;CACjC,MAAM,sBAAsB,yCAAyC;AAErE,eAAc,IAAI,4BAA4B,OAAO,MAAM;AACzD,QAAM,2BAA2B,KAAK;EACtC,MAAM,SAAS,KAAK,QAAQ;EAC5B,MAAM,SAAS,OAAO,WAAW;EACjC,MAAM,UAAU,uBAAuB;GACrC;GACA,iBAAiB,OAAO;GACxB,iBAAiB,OAAO,UAAU;GACnC,CAAC;AACF,SAAO,EAAE,KAAK,QAAQ;GACtB;AAEF,eAAc,KAAK,+BAA+B,qBAAqB,OAAO,MAAM;EAClF,MAAM,QAAQ,oBAAoB,EAAE;AACpC,MAAI,CAAC,MAAO,QAAO,EAAE,KAAK,EAAE,OAAO,0BAA0B,EAAE,IAAI;EAEnE,MAAM,SAAS,KAAK,QAAQ;EAC5B,MAAM,cAAc,4BAA4B,OAAO;AACvD,MAAI,YAAY,OAAO,MACrB,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;IAAE,SAAS,YAAY;IAAS,MAAM;IAAsB;GAAE,EAAE,IAAI;AAGxG,MAAI,YAAY,SAAS;GACvB,MAAM,aAAa,MAAM,KAAK,QAAQ,WAAW,OAAO;AACxD,OAAI,CAAC,WAAW,MACd,QAAO,EAAE,KACP;IAAE,IAAI;IAAO,OAAO;KAAE,SAAS,WAAW,SAAS;KAAyB,MAAM;KAAe;IAAE,EACnG,IACD;AAEH,kBACE,6BACA,EAAE,kBAAkB,iBAAiB,MAAM,CAAC,MAAM,GAAG,GAAG,EAAE,EAC1D,kDACD;;EAGH,MAAM,SAAS,OAAO,WAAW;EACjC,IAAI,UAAU,uBAAuB;GACnC,QAAQ,KAAK,QAAQ;GACrB,iBAAiB,OAAO;GACxB,iBAAiB,OAAO,UAAU;GACnC,CAAC;AAEF,MAAI,YAAY,QACd,WAAU;GACR,GAAG;GACH,cAAc;GACd,aAAa;GACd;AAGH,SAAO,EAAE,KAAK;GACZ,IAAI;GACJ,iBAAiB,YAAY;GAC7B;GACD,CAAC;GACF;AAEF,eAAc,KAAK,oBAAoB,qBAAqB,OAAO,MAAM;AACvE,QAAM,2BAA2B,KAAK;EACtC,MAAM,QAAQ,oBAAoB,EAAE;AACpC,MAAI,CAAC,MAAO,QAAO,EAAE,KAAK,EAAE,OAAO,0BAA0B,EAAE,IAAI;EAEnE,MAAM,EAAE,QAAQ,cAAc,qBAAqB;AACnD,iBACE,eACA;GACE,WAAW,UAAU,aAAa;GAClC,kBAAkB,iBAAiB,MAAM,CAAC,MAAM,GAAG,GAAG;GACvD,EACD,iCACD;AACD,SAAO,EAAE,KAAK;GAAE,eAAe;GAAQ,WAAW,UAAU,aAAa;GAAE,CAAC;GAC5E;AAEF,eAAc,IAAI,sBAAsB,OAAO,MAAM;AACnD,QAAM,2BAA2B,KAAK;EACtC,MAAM,SAAS,KAAK,QAAQ;AAC5B,SAAO,EAAE,KAAK,mBAAmB,QAAQ,OAAO,WAAW,CAAC,CAAC;GAC7D;AAEF,eAAc,KAAK,uBAAuB,qBAAqB,OAAO,MAAM;EAC1E,MAAM,QAAQ,oBAAoB,EAAE;AACpC,MAAI,CAAC,MAAO,QAAO,EAAE,KAAK,EAAE,OAAO,0BAA0B,EAAE,IAAI;EAEnE,MAAM,SAAS,KAAK,QAAQ;AAC5B,6BAA2B,OAAO;EAClC,MAAM,SAAS,MAAM,KAAK,QAAQ,WAAW,OAAO;AACpD,MAAI,CAAC,OAAO,MACV,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,OAAO,SAAS;GAAyB,EAAE,IAAI;EAEnF,MAAM,UAAU,sBAAsB,OAAO;AAC7C,iBACE,kBACA;GACE,gBAAgB,QAAQ;GACxB,kBAAkB,iBAAiB,MAAM,CAAC,MAAM,GAAG,GAAG;GACvD,EACD,0CACD;AACD,SAAO,EAAE,KAAK;GACZ,IAAI;GACJ,SAAS;IACP,gBAAgB,QAAQ;IACxB,iBAAiB,QAAQ;IACzB,YAAY,QAAQ;IACpB,OAAO,QAAQ;IAChB;GACF,CAAC;GACF;AAEF,eAAc,KAAK,qBAAqB,qBAAqB,OAAO,MAAM;AACxE,QAAM,2BAA2B,MAAM,EAAE,OAAO,MAAM,CAAC;EACvD,MAAM,SAAS,KAAK,QAAQ;EAC5B,MAAM,QAAQ,oBAAoB,EAAE;AACpC,MAAI,CAAC,MAAO,QAAO,EAAE,KAAK,EAAE,OAAO,0BAA0B,EAAE,IAAI;AAEnE,MAAI;AACF,wBAAqB,OAAO;WACrB,KAAK;AACZ,OAAI,eAAe,oBAAoB;AACrC,mBACE,uBACA;KAAE,QAAQ;KAA8B,kBAAkB,iBAAiB,MAAM,CAAC,MAAM,GAAG,GAAG;KAAE,EAChG,wCACD;AACD,WAAO,EAAE,KAAK;KAAE,OAAO,IAAI;KAAS,MAAM;KAA8B,EAAE,IAAI;;AAEhF,SAAM;;EAIR,MAAM,OADU,OAAO,QACF,QAAQ;AAC7B,MAAI;GACF,MAAM,KAAK,MAAM,OAAO,MAAM,MAAM,MAAM;AAC1C,4BAAyB,QAAQ,KAAK;AACtC,SAAM,KAAK,QAAQ,WAAW,OAAO;GACrC,MAAM,SAAS,OAAO,WAAW;AACjC,UAAO,EAAE,KAAK;IACZ,WAAW,GAAG;IACd,WAAW,OAAO;IAClB,WAAW,GAAG;IACd,QAAQ,GAAG;IACZ,CAAC;WACK,KAAK;GACZ,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAChE,UAAO,EAAE,KAAK,EAAE,OAAO,SAAS,EAAE,IAAI;;GAExC;AAEF,eAAc,KAAK,oBAAoB,qBAAqB,OAAO,MAAM;AACvE,QAAM,2BAA2B,KAAK;EACtC,MAAM,SAAS,KAAK,QAAQ;EAC5B,IAAI,UAAU;AACd,MAAI;AAEF,cAAU,MADU,EAAE,IAAI,MAAM,CAAC,aAAa,EAAE,EAAE,EACnC,YAAY;UACrB;AACN,aAAU;;EAEZ,MAAM,EAAE,aAAa,MAAM,OAAO,KAAK,EAAE,SAAS,CAAC;AACnD,2BAAyB,QAAQ,MAAM;AACvC,QAAM,KAAK,QAAQ,WAAW,OAAO;AACrC,SAAO,EAAE,KAAK;GAAE,IAAI;GAAM;GAAU,CAAC;GACrC;AAEF,eAAc,IAAI,kBAAkB,OAAO,MAAM;AAC/C,QAAM,2BAA2B,KAAK;EAEtC,MAAM,OADU,KAAK,QAAQ,cAAc,QACtB,QAAQ;EAC7B,MAAM,OAAO,4BAA4B,KAAK,QAAQ,cAAc;AAEpE,MAAI,CADU,oBAAoB,EACxB,CAAE,QAAO,EAAE,KAAK,EAAE,OAAO,0BAA0B,EAAE,IAAI;EACnE,MAAM,KAAK,MAAM,OAAO,QAAQ,MAAM,KAAK;AAC3C,SAAO,EAAE,KAAK,GAAG;GACjB;AAEF,eAAc,IAAI,gCAAgC,OAAO,MAAM;AAC7D,QAAM,2BAA2B,KAAK;AACtC,SAAO,EAAE,KAAK,EACZ,WAAW,EAAE,KAAK,qBAA8B,EACjD,CAAC;GACF;;;;AAKF,eAAc,KAAK,0CAA0C,2BAA2B,OAAO,MAAM;EACnG,MAAM,SAAS,KAAK,QAAQ;EAC5B,MAAM,qBAAqB,2CAA2C,OAAO;AAC7E,SAAO,EAAE,KAAK;GACZ,IAAI;GACJ,SAAS;IACP;IACA,QAAQ,qBAAsB,WAAsB;IACrD;GACF,CAAC;GACF"}
@@ -1,10 +1,10 @@
1
- import { init_agent_scope, listAgentEntries, normalizeAgentId, resolveAgentHomeDir, resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../../agent/agent-scope.js";
2
- import { getWorkspacePath, init_schema } from "../../../config/schema.js";
3
- import { extractProfileAgentId } from "../../../config/agent-profile.js";
4
1
  import { createLogger } from "../../../utils/logger/index.js";
5
2
  import { init_logger } from "../../../utils/logger.js";
3
+ import { init_agent_scope, listAgentEntries, normalizeAgentId, resolveAgentHomeDir, resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../../agent/agent-scope.js";
4
+ import { extractProfileAgentId } from "../../../config/agent-profile.js";
6
5
  import { validateWritePath } from "../../../agent/sandbox/path-policy.js";
7
6
  import { resolveSafeInboundFilePath } from "../../../channels/attachments/inbound-persist.js";
7
+ import { getWorkspacePath } from "../../../config/workspace-path-helpers.js";
8
8
  import { isPathUnderWorkspace, resolveWorkspaceSafePath, toWorkspaceRelativePosix } from "../../workspace-editor-path.js";
9
9
  import { resolveSafeTtsFilePath } from "../../../channels/attachments/outbound-tts-persist.js";
10
10
  import { buildFilePathClassifierContext, classifyFileLocation, displayNameForPath, fileRefSessionKeysMatch, resolveFileReferenceCandidate } from "../../file-path-classifier.js";
@@ -12,12 +12,11 @@ import { fileReferenceRegistry } from "../../file-reference-registry.js";
12
12
  import { resolveHeartbeatMdPath } from "../../workspace-heartbeat-path.js";
13
13
  import { listWorkspaceRelativeFilesFsFallback } from "../../workspace-fs-file-list.js";
14
14
  import { runRipgrepInDirectory, runRipgrepListFiles } from "../../workspace-ripgrep.js";
15
- import { basename, dirname, join, resolve } from "node:path";
16
- import { constants } from "node:fs";
17
15
  import { randomUUID } from "node:crypto";
16
+ import { constants } from "node:fs";
18
17
  import { copyFile, link, mkdir, readFile, readdir, rename, stat, unlink, writeFile } from "node:fs/promises";
18
+ import { basename, dirname, join, resolve } from "node:path";
19
19
  //#region src/gateway/hono/routes/workspace.ts
20
- init_schema();
21
20
  init_agent_scope();
22
21
  init_logger();
23
22
  const log = createLogger("HonoApp");
@@ -123,7 +122,7 @@ async function resolveEditorWorkspaceRootAsync(service, cfg, sessionKeyRaw, agen
123
122
  if (sk) try {
124
123
  return {
125
124
  ok: true,
126
- root: await service.getEffectiveWorkspacePathForSession(sk)
125
+ root: await service.sessions.getEffectiveWorkspacePath(sk)
127
126
  };
128
127
  } catch (err) {
129
128
  const em = err instanceof Error ? err.message : String(err);
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.js","names":["fsConstants"],"sources":["../../../../../src/gateway/hono/routes/workspace.ts"],"sourcesContent":["import type { Hono } from 'hono';\nimport { randomUUID } from 'node:crypto';\nimport { constants as fsConstants } from 'node:fs';\nimport { copyFile, link, mkdir, readdir, readFile, rename, stat, unlink, writeFile } from 'node:fs/promises';\nimport { basename, dirname, join, resolve } from 'node:path';\n\nimport { extractProfileAgentId } from '../../../config/agent-profile.js';\nimport { type Config, getWorkspacePath } from '../../../config/schema.js';\nimport { validateWritePath } from '../../../agent/sandbox/path-policy.js';\nimport { resolveSafeInboundFilePath } from '../../../channels/attachments/inbound-persist.js';\nimport { resolveSafeTtsFilePath } from '../../../channels/attachments/outbound-tts-persist.js';\nimport {\n listAgentEntries,\n normalizeAgentId,\n resolveAgentHomeDir,\n resolveAgentWorkspaceDir,\n resolveDefaultAgentId,\n} from '../../../agent/agent-scope.js';\nimport { createLogger } from '../../../utils/logger.js';\nimport { resolveHeartbeatMdPath } from '../../workspace-heartbeat-path.js';\nimport {\n isPathUnderWorkspace,\n resolveWorkspaceSafePath,\n toWorkspaceRelativePosix,\n} from '../../workspace-editor-path.js';\nimport { listWorkspaceRelativeFilesFsFallback } from '../../workspace-fs-file-list.js';\nimport { runRipgrepInDirectory, runRipgrepListFiles } from '../../workspace-ripgrep.js';\nimport {\n buildFilePathClassifierContext,\n classifyFileLocation,\n displayNameForPath,\n fileRefSessionKeysMatch,\n resolveFileReferenceCandidate,\n} from '../../file-path-classifier.js';\nimport {\n fileReferenceRegistry,\n type FileReferenceCapability,\n type FileReferenceLocationKind,\n type FileReferenceScope,\n} from '../../file-reference-registry.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\nimport type { GatewayService } from '../../service.js';\n\nconst log = createLogger('HonoApp');\n\n/** Agent home for persisted `inbound/` and `tts/` attachments (matches `persistOutboundTtsAudio` / `prepareInboundAttachments`). */\nfunction resolvePersistedAttachmentAgentHome(cfg: Config, sessionKeyRaw: string | undefined): string {\n const sk = typeof sessionKeyRaw === 'string' ? sessionKeyRaw.trim() : '';\n const agentId = sk ? extractProfileAgentId(sk, cfg) : resolveDefaultAgentId(cfg);\n return resolveAgentHomeDir(cfg, agentId);\n}\n\nconst FILE_SEARCH_MAX_LIMIT = 50;\n\n/** Subsequence fuzzy match: all query chars appear in order in `candidate` (case-insensitive). */\nfunction fuzzySubsequenceScore(query: string, candidate: string): number | null {\n const q = query.toLowerCase();\n const c = candidate.toLowerCase();\n if (q.length === 0) return 0;\n let qi = 0;\n for (let ci = 0; ci < c.length && qi < q.length; ci++) {\n if (c[ci] === q[qi]) qi++;\n }\n if (qi < q.length) return null;\n const base = c.split('/').pop() ?? c;\n let score = 10;\n if (c.startsWith(q)) score += 40;\n if (base.startsWith(q)) score += 35;\n else if (base.includes(q)) score += 20;\n else if (c.includes(q)) score += 10;\n score -= c.length * 0.0001;\n return score;\n}\n\nasync function fuzzySearchWorkspaceFiles(\n workspaceRoot: string,\n query: string,\n limit: number,\n): Promise<Array<{ name: string; path: string; isDirectory: boolean }>> {\n let files = await runRipgrepListFiles(workspaceRoot);\n if (files.length === 0) {\n files = await listWorkspaceRelativeFilesFsFallback(workspaceRoot, 120_000);\n if (files.length > 0) {\n log.debug(\n { workspaceRoot, fileCount: files.length },\n 'workspace files/search: file list from fs walk (ripgrep unavailable or returned empty)',\n );\n }\n }\n const q = query.trim();\n const capped = Math.min(Math.max(limit, 1), FILE_SEARCH_MAX_LIMIT);\n\n type Row = { name: string; path: string; isDirectory: boolean; score: number };\n const rows: Row[] = [];\n\n if (!q) {\n const sorted = [...files].sort((a, b) => a.localeCompare(b));\n for (const rel of sorted.slice(0, capped)) {\n const name = rel.split('/').pop() ?? rel;\n rows.push({ name, path: rel, isDirectory: false, score: 0 });\n }\n return rows;\n }\n\n for (const rel of files) {\n const name = rel.split('/').pop() ?? rel;\n const scorePath = fuzzySubsequenceScore(q, rel);\n const scoreName = fuzzySubsequenceScore(q, name);\n const score = Math.max(scorePath ?? -Infinity, scoreName ?? -Infinity);\n if (score === -Infinity) continue;\n rows.push({ name, path: rel, isDirectory: false, score });\n }\n\n rows.sort((a, b) => b.score - a.score || a.path.localeCompare(b.path));\n return rows.slice(0, capped).map(({ name, path, isDirectory }) => ({ name, path, isDirectory }));\n}\n\nfunction isKnownEditorAgentId(cfg: Config, id: string): boolean {\n const n = normalizeAgentId(id);\n if (n === resolveDefaultAgentId(cfg)) return true;\n return listAgentEntries(cfg).some((e) => normalizeAgentId(e.id) === n);\n}\n\nfunction resolveEditorWorkspaceRoot(\n cfg: Config,\n agentIdRaw: string | undefined,\n): { ok: true; root: string } | { ok: false; message: string } {\n const trimmed = typeof agentIdRaw === 'string' ? agentIdRaw.trim() : '';\n if (!trimmed) {\n const root = getWorkspacePath(cfg);\n if (!root) return { ok: false, message: 'Workspace not configured' };\n return { ok: true, root };\n }\n const id = normalizeAgentId(trimmed);\n if (!isKnownEditorAgentId(cfg, id)) {\n return { ok: false, message: 'Unknown agent' };\n }\n return { ok: true, root: resolveAgentWorkspaceDir(cfg, id) };\n}\n\n/** Prefer `sessionKey` (per-session workspace override) over `agentId`. */\nasync function resolveEditorWorkspaceRootAsync(\n service: GatewayService,\n cfg: Config,\n sessionKeyRaw: string | undefined,\n agentIdRaw: string | undefined,\n): Promise<{ ok: true; root: string } | { ok: false; message: string }> {\n const sk = typeof sessionKeyRaw === 'string' ? sessionKeyRaw.trim() : '';\n if (sk) {\n try {\n const root = await service.getEffectiveWorkspacePathForSession(sk);\n return { ok: true, root };\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n log.warn({ err, sessionKey: sk }, 'Session workspace root resolution failed');\n return { ok: false, message: em || 'Session workspace resolution failed' };\n }\n }\n return resolveEditorWorkspaceRoot(cfg, agentIdRaw);\n}\n\ninterface ResolvedWorkspaceImportConfig {\n targetDir: string;\n maxBytes: number;\n allowOverwrite: boolean;\n}\n\nfunction resolveWorkspaceImportConfig(cfg: Config): ResolvedWorkspaceImportConfig {\n const raw = cfg.workspace?.import;\n return {\n targetDir: raw?.targetDir?.trim() || 'imports',\n maxBytes: raw?.maxBytes ?? 104_857_600,\n allowOverwrite: raw?.allowOverwrite ?? true,\n };\n}\n\n/** Strip path separators, NULs and control chars from a basename so it stays in the destination dir. */\nfunction sanitizeImportBasename(name: string): string {\n return name\n .replace(/[\\\\/]/g, '')\n .replace(/[\\x00-\\x1f\\x7f]/g, '')\n .trim();\n}\n\n/**\n * Race-safe target picker for the `rename`-on-conflict strategy. Uses `link(tmp, target)`\n * which atomically fails with EEXIST when the candidate is taken; on success the tmp\n * file is left in place for the caller to unlink. Returns the linked target path.\n */\nasync function pickAvailableTargetWithLink(\n tmpAbs: string,\n initialDestAbs: string,\n maxAttempts = 1000,\n): Promise<{ ok: true; path: string; attempts: number } | { ok: false; attempts: number }> {\n const dir = dirname(initialDestAbs);\n const original = basename(initialDestAbs);\n const dotIdx = original.lastIndexOf('.');\n const stem = dotIdx > 0 ? original.slice(0, dotIdx) : original;\n const ext = dotIdx > 0 ? original.slice(dotIdx) : '';\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n const candidate = attempt === 1 ? initialDestAbs : join(dir, `${stem}-${attempt}${ext}`);\n try {\n await link(tmpAbs, candidate);\n return { ok: true, path: candidate, attempts: attempt };\n } catch (err) {\n const code = (err as NodeJS.ErrnoException)?.code;\n if (code !== 'EEXIST') {\n throw err;\n }\n }\n }\n return { ok: false, attempts: maxAttempts };\n}\n\nfunction fileReferenceCapabilities(\n scope: FileReferenceScope,\n isDirectory: boolean,\n locationKind?: FileReferenceLocationKind,\n): FileReferenceCapability[] {\n if (scope === 'workspace') {\n return isDirectory\n ? ['openExternal', 'revealInFolder', 'copyPath']\n : ['preview', 'edit', 'openExternal', 'revealInFolder', 'copyPath'];\n }\n if (scope === 'external' || scope === 'agent-profile' || scope === 'session-artifact') {\n const base: FileReferenceCapability[] = ['openExternal', 'revealInFolder', 'copyPath'];\n // v1: importToWorkspace for files only; exclude xopc-config to prevent copying\n // app config into the workspace (semantically wrong).\n if (!isDirectory && locationKind !== 'xopc-config') {\n base.push('importToWorkspace');\n }\n return base;\n }\n if (scope === 'missing') return ['copyPath'];\n return [];\n}\n\nfunction isFileReferenceAction(action: unknown): action is 'openExternal' | 'revealInFolder' {\n return action === 'openExternal' || action === 'revealInFolder';\n}\n\nexport function registerWorkspaceRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service } = deps;\n\n authenticated.get('/api/workspace/inbound-file', async (c) => {\n const rel = c.req.query('rel');\n if (!rel || typeof rel !== 'string') {\n return c.json({ ok: false, error: { message: 'Missing rel' } }, 400);\n }\n const cfg = service.currentConfig;\n const agentHome = resolvePersistedAttachmentAgentHome(cfg, c.req.query('sessionKey'));\n const abs = resolveSafeInboundFilePath({ agentHome }, rel);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Forbidden' } }, 403);\n }\n try {\n const buf = await readFile(abs);\n const ext = rel.split('.').pop()?.toLowerCase() ?? '';\n const mimeByExt: Record<string, string> = {\n pdf: 'application/pdf',\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n webp: 'image/webp',\n gif: 'image/gif',\n md: 'text/markdown',\n txt: 'text/plain',\n json: 'application/json',\n html: 'text/html',\n css: 'text/css',\n js: 'text/javascript',\n ts: 'text/typescript',\n webm: 'audio/webm',\n ogg: 'audio/ogg',\n opus: 'audio/ogg',\n mp3: 'audio/mpeg',\n wav: 'audio/wav',\n m4a: 'audio/mp4',\n };\n const contentType = mimeByExt[ext] || 'application/octet-stream';\n return new Response(buf, {\n headers: {\n 'Content-Type': contentType,\n 'Cache-Control': 'private, max-age=3600',\n },\n });\n } catch {\n return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n }\n });\n\n authenticated.get('/api/workspace/tts-file', async (c) => {\n const rel = c.req.query('rel');\n if (!rel || typeof rel !== 'string') {\n return c.json({ ok: false, error: { message: 'Missing rel' } }, 400);\n }\n const cfg = service.currentConfig;\n const agentHome = resolvePersistedAttachmentAgentHome(cfg, c.req.query('sessionKey'));\n const abs = resolveSafeTtsFilePath({ agentHome }, rel);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Forbidden' } }, 403);\n }\n try {\n const buf = await readFile(abs);\n const ext = rel.split('.').pop()?.toLowerCase() ?? '';\n const mimeByExt: Record<string, string> = {\n ogg: 'audio/ogg',\n opus: 'audio/ogg',\n mp3: 'audio/mpeg',\n wav: 'audio/wav',\n m4a: 'audio/mp4',\n };\n const contentType = mimeByExt[ext] || 'application/octet-stream';\n return new Response(buf, {\n headers: {\n 'Content-Type': contentType,\n 'Cache-Control': 'private, max-age=3600',\n },\n });\n } catch {\n return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n }\n });\n\n authenticated.get('/api/workspace/heartbeat-md', async (c) => {\n const abs = resolveHeartbeatMdPath(service.currentConfig);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Workspace not configured' } }, 400);\n }\n try {\n const content = await readFile(abs, 'utf-8');\n return c.json({ ok: true, payload: { content: content, file: 'HEARTBEAT.md' } });\n } catch {\n return c.json({ ok: true, payload: { content: '', file: 'HEARTBEAT.md' } });\n }\n });\n\n authenticated.put('/api/workspace/heartbeat-md', async (c) => {\n const abs = resolveHeartbeatMdPath(service.currentConfig);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Workspace not configured' } }, 400);\n }\n let body: unknown;\n try {\n body = await c.req.json();\n } catch {\n return c.json({ ok: false, error: { message: 'Invalid JSON' } }, 400);\n }\n const content =\n typeof body === 'object' &&\n body !== null &&\n 'content' in body &&\n typeof (body as { content: unknown }).content === 'string'\n ? (body as { content: string }).content\n : '';\n try {\n await writeFile(abs, content, 'utf-8');\n return c.json({ ok: true, payload: { file: 'HEARTBEAT.md' } });\n } catch (err) {\n log.error({ err, path: abs }, 'Failed to write HEARTBEAT.md');\n return c.json({ ok: false, error: { message: 'Write failed' } }, 500);\n }\n });\n\n authenticated.get('/api/workspace/editor/list', async (c) => {\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n const dirRel = typeof c.req.query('dir') === 'string' ? c.req.query('dir')! : '';\n const absDir = resolveWorkspaceSafePath(workspaceRoot, dirRel);\n if (!absDir) {\n return c.json({ ok: false, error: { message: 'Invalid path' } }, 400);\n }\n let st: Awaited<ReturnType<typeof stat>>;\n try {\n st = await stat(absDir);\n } catch {\n return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n }\n if (!st.isDirectory()) {\n return c.json({ ok: false, error: { message: 'Not a directory' } }, 400);\n }\n const dirents = await readdir(absDir, { withFileTypes: true });\n const entries: { name: string; path: string; absolutePath: string; isDirectory: boolean }[] = [];\n for (const entry of dirents) {\n if (entry.name.startsWith('.')) continue;\n const fullPath = join(absDir, entry.name);\n if (entry.isDirectory()) {\n entries.push({\n name: entry.name,\n path: toWorkspaceRelativePosix(workspaceRoot, fullPath),\n absolutePath: fullPath,\n isDirectory: true,\n });\n } else {\n entries.push({\n name: entry.name,\n path: toWorkspaceRelativePosix(workspaceRoot, fullPath),\n absolutePath: fullPath,\n isDirectory: false,\n });\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({ ok: true, payload: { entries } });\n });\n\n authenticated.get('/api/workspace/editor/read', async (c) => {\n const pathRel = typeof c.req.query('path') === 'string' ? c.req.query('path')! : '';\n if (!pathRel.trim()) {\n return c.json({ ok: false, error: { message: 'Missing path' } }, 400);\n }\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n const abs = resolveWorkspaceSafePath(workspaceRoot, pathRel);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Invalid path' } }, 400);\n }\n let st: Awaited<ReturnType<typeof stat>>;\n try {\n st = await stat(abs);\n } catch {\n return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n }\n if (!st.isFile()) {\n return c.json({ ok: false, error: { message: 'Not a file' } }, 400);\n }\n try {\n const content = await readFile(abs, 'utf-8');\n return c.json({\n ok: true,\n payload: {\n content,\n path: toWorkspaceRelativePosix(workspaceRoot, abs),\n absolutePath: abs,\n mtimeMs: st.mtimeMs,\n },\n });\n } catch {\n return c.json({ ok: false, error: { message: 'Read failed' } }, 500);\n }\n });\n\n /** Read file as raw bytes and return base64 (for PDF/images in workspace preview — avoids UTF-8 corruption). */\n authenticated.get('/api/workspace/editor/read-base64', async (c) => {\n const pathRel = typeof c.req.query('path') === 'string' ? c.req.query('path')! : '';\n if (!pathRel.trim()) {\n return c.json({ ok: false, error: { message: 'Missing path' } }, 400);\n }\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n const abs = resolveWorkspaceSafePath(workspaceRoot, pathRel);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Invalid path' } }, 400);\n }\n let st: Awaited<ReturnType<typeof stat>>;\n try {\n st = await stat(abs);\n } catch {\n return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n }\n if (!st.isFile()) {\n return c.json({ ok: false, error: { message: 'Not a file' } }, 400);\n }\n try {\n const buf = await readFile(abs);\n return c.json({\n ok: true,\n payload: {\n contentBase64: buf.toString('base64'),\n path: toWorkspaceRelativePosix(workspaceRoot, abs),\n /** Host absolute path — Electron can open with the default app (shell.openPath). */\n absolutePath: abs,\n mtimeMs: st.mtimeMs,\n },\n });\n } catch {\n return c.json({ ok: false, error: { message: 'Read failed' } }, 500);\n }\n });\n\n /** Map an absolute host path to a workspace-relative path (if under this session’s workspace). */\n authenticated.get('/api/workspace/editor/resolve-path', async (c) => {\n const raw = c.req.query('absolutePath');\n if (!raw || typeof raw !== 'string' || !raw.trim()) {\n return c.json({ ok: false, error: { message: 'Missing absolutePath' } }, 400);\n }\n const absolutePath = raw.trim();\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n const normalized = resolve(absolutePath);\n if (!isPathUnderWorkspace(workspaceRoot, normalized)) {\n return c.json({ ok: false, error: { message: 'Path not under workspace' } }, 403);\n }\n const rel = toWorkspaceRelativePosix(workspaceRoot, normalized);\n return c.json({ ok: true, payload: { workspaceRelativePath: rel } });\n });\n\n authenticated.get('/api/workspace/editor/resolve-reference', async (c) => {\n const rawPath = typeof c.req.query('path') === 'string' ? c.req.query('path')!.trim() : '';\n if (!rawPath) {\n return c.json({ ok: false, error: { code: 'INVALID_PATH', message: 'Missing path' } }, 400);\n }\n\n const sessionKey = typeof c.req.query('sessionKey') === 'string' ? c.req.query('sessionKey')!.trim() : '';\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n sessionKey,\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { code: 'WORKSPACE_RESOLUTION_FAILED', message: ws.message } }, 400);\n }\n\n const workspaceRoot = ws.root;\n const classifierCtx = { ...buildFilePathClassifierContext(service.currentConfig, sessionKey), workspaceRoot };\n const displayName = displayNameForPath(rawPath);\n const { candidate, invalid } = await resolveFileReferenceCandidate(rawPath, workspaceRoot, classifierCtx);\n\n if (!candidate || invalid) {\n return c.json({\n ok: true,\n payload: {\n inputPath: rawPath,\n displayName,\n scope: 'invalid' satisfies FileReferenceScope,\n exists: false,\n capabilities: [] as FileReferenceCapability[],\n errorCode: 'INVALID_PATH',\n },\n });\n }\n\n let st: Awaited<ReturnType<typeof stat>> | null = null;\n try {\n st = await stat(candidate);\n } catch {\n st = null;\n }\n\n if (!st) {\n // Always include the resolved candidate so the UI's \"Copy path\" yields\n // something actionable (\"I looked here, no file\"). Without this, bare\n // workspace-relative mentions fall back to the `rel:<path>` UI sentinel.\n return c.json({\n ok: true,\n payload: {\n inputPath: rawPath,\n displayName,\n scope: 'missing' satisfies FileReferenceScope,\n exists: false,\n absolutePath: candidate,\n capabilities: fileReferenceCapabilities('missing', false),\n errorCode: 'FILE_NOT_FOUND',\n },\n });\n }\n\n const classified = classifyFileLocation(candidate, classifierCtx);\n const { scope, locationKind, manageRoute } = classified;\n const inWorkspace = scope === 'workspace';\n const isDirectory = st.isDirectory();\n const capabilities = fileReferenceCapabilities(scope, isDirectory, locationKind);\n const ref = fileReferenceRegistry.register({\n absolutePath: candidate,\n sessionKey: sessionKey || undefined,\n scope,\n locationKind,\n capabilities,\n });\n\n return c.json({\n ok: true,\n payload: {\n fileRefId: ref.id,\n inputPath: rawPath,\n displayName,\n scope,\n locationKind,\n manageRoute,\n exists: true,\n isDirectory,\n absolutePath: candidate,\n workspaceRelativePath: inWorkspace ? toWorkspaceRelativePosix(workspaceRoot, candidate) : undefined,\n capabilities,\n mtimeMs: st.mtimeMs,\n },\n });\n });\n\n authenticated.post('/api/workspace/file-ref/:id/resolve-action', async (c) => {\n const id = c.req.param('id')?.trim() ?? '';\n if (!id) {\n return c.json({ ok: false, error: { code: 'INVALID_FILE_REF', message: 'Missing file reference' } }, 400);\n }\n\n const ref = fileReferenceRegistry.resolve(id);\n if (!ref) {\n return c.json({ ok: false, error: { code: 'FILE_REF_EXPIRED', message: 'File reference expired' } }, 404);\n }\n\n const sessionKey = typeof c.req.query('sessionKey') === 'string' ? c.req.query('sessionKey')!.trim() : '';\n if (!fileRefSessionKeysMatch(ref.sessionKey, sessionKey)) {\n return c.json({ ok: false, error: { code: 'FILE_REF_FORBIDDEN', message: 'File reference forbidden' } }, 403);\n }\n\n const body = (await c.req.json().catch(() => ({}))) as { action?: unknown };\n const action = body.action;\n if (!isFileReferenceAction(action)) {\n return c.json({ ok: false, error: { code: 'INVALID_ACTION', message: 'Invalid action' } }, 400);\n }\n if (!ref.capabilities.includes(action)) {\n return c.json({ ok: false, error: { code: 'ACTION_NOT_ALLOWED', message: 'Action not allowed' } }, 403);\n }\n\n let st: Awaited<ReturnType<typeof stat>>;\n try {\n st = await stat(ref.absolutePath);\n } catch {\n return c.json({ ok: false, error: { code: 'FILE_NOT_FOUND', message: 'File not found' } }, 404);\n }\n\n return c.json({\n ok: true,\n payload: {\n absolutePath: ref.absolutePath,\n isDirectory: st.isDirectory(),\n },\n });\n });\n\n authenticated.post('/api/workspace/import-file-ref/:id', async (c) => {\n const id = c.req.param('id')?.trim() ?? '';\n if (!id) {\n return c.json({ ok: false, error: { code: 'INVALID_FILE_REF', message: 'Missing file reference' } }, 400);\n }\n\n const ref = fileReferenceRegistry.resolve(id);\n if (!ref) {\n return c.json({ ok: false, error: { code: 'FILE_REF_EXPIRED', message: 'File reference expired' } }, 404);\n }\n\n const sessionKey = typeof c.req.query('sessionKey') === 'string' ? c.req.query('sessionKey')!.trim() : '';\n if (!fileRefSessionKeysMatch(ref.sessionKey, sessionKey)) {\n return c.json({ ok: false, error: { code: 'FILE_REF_FORBIDDEN', message: 'File reference forbidden' } }, 403);\n }\n\n if (!ref.capabilities.includes('importToWorkspace')) {\n return c.json({ ok: false, error: { code: 'IMPORT_NOT_ALLOWED', message: 'Import not allowed for this file' } }, 403);\n }\n\n let sourceStat: Awaited<ReturnType<typeof stat>>;\n try {\n sourceStat = await stat(ref.absolutePath);\n } catch {\n fileReferenceRegistry.expireById(id);\n return c.json({ ok: false, error: { code: 'SOURCE_NOT_FOUND', message: 'Source file no longer exists' } }, 404);\n }\n if (!sourceStat.isFile()) {\n return c.json({ ok: false, error: { code: 'SOURCE_NOT_FILE', message: 'Source is not a regular file' } }, 400);\n }\n\n const importCfg = resolveWorkspaceImportConfig(service.currentConfig);\n if (sourceStat.size > importCfg.maxBytes) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'SOURCE_TOO_LARGE',\n message: `Source exceeds maximum import size (${importCfg.maxBytes} bytes)`,\n },\n },\n 413,\n );\n }\n\n const ws = await resolveEditorWorkspaceRootAsync(service, service.currentConfig, sessionKey, undefined);\n if (ws.ok === false) {\n return c.json({ ok: false, error: { code: 'WORKSPACE_RESOLUTION_FAILED', message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n\n let body: { destination?: unknown; onConflict?: unknown };\n try {\n body = (await c.req.json().catch(() => ({}))) as typeof body;\n } catch {\n body = {};\n }\n const requestedDestRaw = typeof body.destination === 'string' ? body.destination.trim() : '';\n const onConflictRaw = typeof body.onConflict === 'string' ? body.onConflict : 'rename';\n if (onConflictRaw !== 'rename' && onConflictRaw !== 'overwrite' && onConflictRaw !== 'error') {\n return c.json({ ok: false, error: { code: 'INVALID_CONFLICT_MODE', message: 'Invalid onConflict value' } }, 400);\n }\n const onConflict = onConflictRaw as 'rename' | 'overwrite' | 'error';\n if (onConflict === 'overwrite' && !importCfg.allowOverwrite) {\n return c.json({ ok: false, error: { code: 'OVERWRITE_DISABLED', message: 'Overwrite is disabled by config' } }, 403);\n }\n\n const sourceBasename = sanitizeImportBasename(basename(ref.absolutePath)) || 'imported-file';\n let requestedRel: string;\n if (!requestedDestRaw) {\n requestedRel = `${importCfg.targetDir}/${sourceBasename}`;\n } else {\n const trimmedDest = requestedDestRaw.replace(/\\\\/g, '/');\n // Path ending with `/` is treated as a directory; append source basename.\n requestedRel = trimmedDest.endsWith('/') ? `${trimmedDest}${sourceBasename}` : trimmedDest;\n }\n\n let initialDestAbs = resolveWorkspaceSafePath(workspaceRoot, requestedRel);\n if (!initialDestAbs) {\n return c.json({ ok: false, error: { code: 'INVALID_DESTINATION', message: 'Invalid destination path' } }, 400);\n }\n\n // Sandbox: blocks `.xopc/xopc.json`, `.env*`, etc.; canonical symlink resolution included.\n const writePolicy = validateWritePath(initialDestAbs, workspaceRoot);\n if (!writePolicy.allowed) {\n return c.json({ ok: false, error: { code: 'DESTINATION_BLOCKED', message: writePolicy.reason ?? 'Destination blocked' } }, 403);\n }\n\n if (resolve(ref.absolutePath) === resolve(initialDestAbs)) {\n return c.json({ ok: false, error: { code: 'SAME_LOCATION', message: 'Destination is the same as source' } }, 400);\n }\n\n const destDir = dirname(initialDestAbs);\n try {\n await mkdir(destDir, { recursive: true });\n } catch (err) {\n log.warn({ err, destDir }, 'Failed to create import destination directory');\n return c.json({ ok: false, error: { code: 'IMPORT_FAILED', message: 'Failed to prepare destination' } }, 500);\n }\n\n // Stage source into a hidden tmp file inside the destination directory so we\n // can atomically `link` (rename strategy) or `rename` (overwrite) to land it.\n const tmpName = `.${basename(initialDestAbs)}.import-${randomUUID()}.tmp`;\n const tmpAbs = join(destDir, tmpName);\n\n const started = Date.now();\n let renamed = false;\n let overwrote = false;\n let finalDestAbs = initialDestAbs;\n\n try {\n try {\n await copyFile(ref.absolutePath, tmpAbs, fsConstants.COPYFILE_FICLONE);\n } catch (err) {\n await unlink(tmpAbs).catch(() => {});\n log.warn({ err, source: ref.absolutePath, tmpAbs }, 'Failed to copy source into staging tmp');\n return c.json({ ok: false, error: { code: 'IMPORT_FAILED', message: 'Failed to copy source file' } }, 500);\n }\n\n if (onConflict === 'overwrite') {\n // Snapshot pre-rename existence for telemetry; the actual overwrite is unconditional.\n overwrote = await stat(initialDestAbs).then(() => true).catch(() => false);\n try {\n await rename(tmpAbs, initialDestAbs);\n } catch (err) {\n await unlink(tmpAbs).catch(() => {});\n log.warn({ err, target: initialDestAbs }, 'Atomic rename failed');\n return c.json({ ok: false, error: { code: 'IMPORT_FAILED', message: 'Failed to finalize import' } }, 500);\n }\n } else if (onConflict === 'error') {\n const exists = await stat(initialDestAbs).then(() => true).catch(() => false);\n if (exists) {\n await unlink(tmpAbs).catch(() => {});\n return c.json({ ok: false, error: { code: 'DESTINATION_EXISTS', message: 'Destination already exists' } }, 409);\n }\n try {\n await link(tmpAbs, initialDestAbs);\n await unlink(tmpAbs).catch(() => {});\n } catch (err) {\n await unlink(tmpAbs).catch(() => {});\n log.warn({ err, target: initialDestAbs }, 'Hard link to destination failed');\n return c.json({ ok: false, error: { code: 'IMPORT_FAILED', message: 'Failed to finalize import' } }, 500);\n }\n } else {\n // rename: race-safe loop using O_EXCL semantics of `link`.\n const picked = await pickAvailableTargetWithLink(tmpAbs, initialDestAbs);\n if (!picked.ok) {\n await unlink(tmpAbs).catch(() => {});\n log.warn({ target: initialDestAbs, attempts: picked.attempts }, 'Failed to find free import target name');\n return c.json({ ok: false, error: { code: 'IMPORT_FAILED', message: 'No free destination name available' } }, 500);\n }\n await unlink(tmpAbs).catch(() => {});\n finalDestAbs = picked.path;\n renamed = picked.path !== initialDestAbs;\n }\n } catch (err) {\n await unlink(tmpAbs).catch(() => {});\n log.error({ err }, 'Import file unexpected failure');\n return c.json({ ok: false, error: { code: 'IMPORT_FAILED', message: 'Import failed' } }, 500);\n }\n\n let finalMtime: number;\n try {\n finalMtime = (await stat(finalDestAbs)).mtimeMs;\n } catch {\n finalMtime = Date.now();\n }\n\n const workspaceRel = toWorkspaceRelativePosix(workspaceRoot, finalDestAbs);\n\n fileReferenceRegistry.expireById(id);\n const newRef = fileReferenceRegistry.register({\n absolutePath: finalDestAbs,\n sessionKey: sessionKey || undefined,\n scope: 'workspace',\n capabilities: fileReferenceCapabilities('workspace', false),\n });\n\n const sourceScope = ref.scope;\n const sourceLocationKind = ref.locationKind;\n\n service.emit('workspace.file-imported', {\n sessionKey: sessionKey || undefined,\n workspaceRelativePath: workspaceRel,\n absolutePath: finalDestAbs,\n bytes: sourceStat.size,\n sourceScope,\n sourceLocationKind,\n });\n\n log.info(\n {\n sessionKey,\n fileRefId: id,\n sourceAbsolutePath: ref.absolutePath,\n sourceScope,\n sourceLocationKind,\n destWorkspaceRelativePath: workspaceRel,\n bytes: sourceStat.size,\n renamed,\n overwrote,\n durationMs: Date.now() - started,\n },\n 'Workspace file import succeeded',\n );\n\n return c.json({\n ok: true,\n payload: {\n workspaceRelativePath: workspaceRel,\n absolutePath: finalDestAbs,\n bytesCopied: sourceStat.size,\n sourceAbsolutePath: ref.absolutePath,\n sourceScope,\n sourceLocationKind,\n renamed,\n overwrote,\n mtimeMs: finalMtime,\n newFileRefId: newRef.id,\n },\n });\n });\n\n /**\n * Serve a workspace file as raw bytes (e.g. <img> after auth fetch + blob URL).\n * Path is workspace-relative; scope via sessionKey / agentId like other editor routes.\n */\n authenticated.get('/api/workspace/editor/raw', async (c) => {\n const pathRel = typeof c.req.query('path') === 'string' ? c.req.query('path')! : '';\n if (!pathRel.trim()) {\n return c.json({ ok: false, error: { message: 'Missing path' } }, 400);\n }\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n const abs = resolveWorkspaceSafePath(workspaceRoot, pathRel);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Invalid path' } }, 400);\n }\n let st: Awaited<ReturnType<typeof stat>>;\n try {\n st = await stat(abs);\n } catch {\n return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n }\n if (!st.isFile()) {\n return c.json({ ok: false, error: { message: 'Not a file' } }, 400);\n }\n const ext = pathRel.split('.').pop()?.toLowerCase() ?? '';\n const mimeByExt: Record<string, string> = {\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n webp: 'image/webp',\n bmp: 'image/bmp',\n svg: 'image/svg+xml',\n pdf: 'application/pdf',\n docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n txt: 'text/plain',\n md: 'text/markdown',\n json: 'application/json',\n html: 'text/html',\n css: 'text/css',\n js: 'text/javascript',\n ts: 'text/typescript',\n mp3: 'audio/mpeg',\n wav: 'audio/wav',\n ogg: 'audio/ogg',\n webm: 'video/webm',\n mp4: 'video/mp4',\n mov: 'video/quicktime',\n };\n const contentType = mimeByExt[ext] || 'application/octet-stream';\n try {\n const buf = await readFile(abs);\n return new Response(buf, {\n headers: {\n 'Content-Type': contentType,\n 'Cache-Control': 'private, max-age=3600',\n },\n });\n } catch {\n return c.json({ ok: false, error: { message: 'Read failed' } }, 500);\n }\n });\n\n authenticated.put('/api/workspace/editor/write', async (c) => {\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n let body: unknown;\n try {\n body = await c.req.json();\n } catch {\n return c.json({ ok: false, error: { message: 'Invalid JSON' } }, 400);\n }\n const pathRel =\n typeof body === 'object' &&\n body !== null &&\n 'path' in body &&\n typeof (body as { path: unknown }).path === 'string'\n ? (body as { path: string }).path\n : '';\n const content =\n typeof body === 'object' &&\n body !== null &&\n 'content' in body &&\n typeof (body as { content: unknown }).content === 'string'\n ? (body as { content: string }).content\n : '';\n if (!pathRel.trim()) {\n return c.json({ ok: false, error: { message: 'Missing path' } }, 400);\n }\n const abs = resolveWorkspaceSafePath(workspaceRoot, pathRel);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Invalid path' } }, 400);\n }\n let st: Awaited<ReturnType<typeof stat>> | undefined;\n try {\n st = await stat(abs);\n } catch {\n st = undefined;\n }\n if (st && !st.isFile()) {\n return c.json({ ok: false, error: { message: 'Not a file' } }, 400);\n }\n try {\n await writeFile(abs, content, 'utf-8');\n let mtimeMs: number;\n try {\n mtimeMs = (await stat(abs)).mtimeMs;\n } catch {\n mtimeMs = Date.now();\n }\n return c.json({\n ok: true,\n payload: { path: toWorkspaceRelativePosix(workspaceRoot, abs), mtimeMs },\n });\n } catch (err) {\n log.error({ err, path: abs }, 'workspace editor write failed');\n return c.json({ ok: false, error: { message: 'Write failed' } }, 500);\n }\n });\n\n authenticated.get('/api/workspace/editor/search', async (c) => {\n const q = typeof c.req.query('q') === 'string' ? c.req.query('q')!.trim() : '';\n const dirRel = typeof c.req.query('dir') === 'string' ? c.req.query('dir')! : '';\n if (!q) {\n return c.json({\n ok: true,\n payload: { results: [] as { filePath: string; lineNumber: number; lineContent: string; matchStart: number; matchEnd: number }[] },\n });\n }\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n const absDir = resolveWorkspaceSafePath(workspaceRoot, dirRel);\n if (!absDir) {\n return c.json({ ok: false, error: { message: 'Invalid path' } }, 400);\n }\n let st: Awaited<ReturnType<typeof stat>>;\n try {\n st = await stat(absDir);\n } catch {\n return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n }\n if (!st.isDirectory()) {\n return c.json({ ok: false, error: { message: 'Not a directory' } }, 400);\n }\n const raw = await runRipgrepInDirectory(q, absDir);\n const results = raw\n .filter((r) => isPathUnderWorkspace(workspaceRoot, r.filePath))\n .map((r) => ({\n ...r,\n filePath: toWorkspaceRelativePosix(workspaceRoot, resolve(r.filePath)),\n }));\n return c.json({ ok: true, payload: { results } });\n });\n\n /** Fuzzy filename / path search over the session workspace (ripgrep `--files` + subsequence scoring). */\n authenticated.get('/api/workspace/editor/files/search', async (c) => {\n const q = typeof c.req.query('q') === 'string' ? c.req.query('q')!.trim() : '';\n const limitRaw = c.req.query('limit');\n const limit = Math.min(\n Math.max(parseInt(typeof limitRaw === 'string' ? limitRaw : '15', 10) || 15, 1),\n FILE_SEARCH_MAX_LIMIT,\n );\n\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n\n const entries = await fuzzySearchWorkspaceFiles(ws.root, q, limit);\n return c.json({ ok: true, payload: { entries } });\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;aAO0E;kBAUnC;aACiB;AAyBxD,MAAM,MAAM,aAAa,UAAU;;AAGnC,SAAS,oCAAoC,KAAa,eAA2C;CACnG,MAAM,KAAK,OAAO,kBAAkB,WAAW,cAAc,MAAM,GAAG;AAEtE,QAAO,oBAAoB,KADX,KAAK,sBAAsB,IAAI,IAAI,GAAG,sBAAsB,IAAI,CACxC;;AAG1C,MAAM,wBAAwB;;AAG9B,SAAS,sBAAsB,OAAe,WAAkC;CAC9E,MAAM,IAAI,MAAM,aAAa;CAC7B,MAAM,IAAI,UAAU,aAAa;AACjC,KAAI,EAAE,WAAW,EAAG,QAAO;CAC3B,IAAI,KAAK;AACT,MAAK,IAAI,KAAK,GAAG,KAAK,EAAE,UAAU,KAAK,EAAE,QAAQ,KAC/C,KAAI,EAAE,QAAQ,EAAE,IAAK;AAEvB,KAAI,KAAK,EAAE,OAAQ,QAAO;CAC1B,MAAM,OAAO,EAAE,MAAM,IAAI,CAAC,KAAK,IAAI;CACnC,IAAI,QAAQ;AACZ,KAAI,EAAE,WAAW,EAAE,CAAE,UAAS;AAC9B,KAAI,KAAK,WAAW,EAAE,CAAE,UAAS;UACxB,KAAK,SAAS,EAAE,CAAE,UAAS;UAC3B,EAAE,SAAS,EAAE,CAAE,UAAS;AACjC,UAAS,EAAE,SAAS;AACpB,QAAO;;AAGT,eAAe,0BACb,eACA,OACA,OACsE;CACtE,IAAI,QAAQ,MAAM,oBAAoB,cAAc;AACpD,KAAI,MAAM,WAAW,GAAG;AACtB,UAAQ,MAAM,qCAAqC,eAAe,KAAQ;AAC1E,MAAI,MAAM,SAAS,EACjB,KAAI,MACF;GAAE;GAAe,WAAW,MAAM;GAAQ,EAC1C,yFACD;;CAGL,MAAM,IAAI,MAAM,MAAM;CACtB,MAAM,SAAS,KAAK,IAAI,KAAK,IAAI,OAAO,EAAE,EAAE,sBAAsB;CAGlE,MAAM,OAAc,EAAE;AAEtB,KAAI,CAAC,GAAG;EACN,MAAM,SAAS,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,cAAc,EAAE,CAAC;AAC5D,OAAK,MAAM,OAAO,OAAO,MAAM,GAAG,OAAO,EAAE;GACzC,MAAM,OAAO,IAAI,MAAM,IAAI,CAAC,KAAK,IAAI;AACrC,QAAK,KAAK;IAAE;IAAM,MAAM;IAAK,aAAa;IAAO,OAAO;IAAG,CAAC;;AAE9D,SAAO;;AAGT,MAAK,MAAM,OAAO,OAAO;EACvB,MAAM,OAAO,IAAI,MAAM,IAAI,CAAC,KAAK,IAAI;EACrC,MAAM,YAAY,sBAAsB,GAAG,IAAI;EAC/C,MAAM,YAAY,sBAAsB,GAAG,KAAK;EAChD,MAAM,QAAQ,KAAK,IAAI,aAAa,WAAW,aAAa,UAAU;AACtE,MAAI,UAAU,UAAW;AACzB,OAAK,KAAK;GAAE;GAAM,MAAM;GAAK,aAAa;GAAO;GAAO,CAAC;;AAG3D,MAAK,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;AACtE,QAAO,KAAK,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,MAAM,MAAM,mBAAmB;EAAE;EAAM;EAAM;EAAa,EAAE;;AAGlG,SAAS,qBAAqB,KAAa,IAAqB;CAC9D,MAAM,IAAI,iBAAiB,GAAG;AAC9B,KAAI,MAAM,sBAAsB,IAAI,CAAE,QAAO;AAC7C,QAAO,iBAAiB,IAAI,CAAC,MAAM,MAAM,iBAAiB,EAAE,GAAG,KAAK,EAAE;;AAGxE,SAAS,2BACP,KACA,YAC6D;CAC7D,MAAM,UAAU,OAAO,eAAe,WAAW,WAAW,MAAM,GAAG;AACrE,KAAI,CAAC,SAAS;EACZ,MAAM,OAAO,iBAAiB,IAAI;AAClC,MAAI,CAAC,KAAM,QAAO;GAAE,IAAI;GAAO,SAAS;GAA4B;AACpE,SAAO;GAAE,IAAI;GAAM;GAAM;;CAE3B,MAAM,KAAK,iBAAiB,QAAQ;AACpC,KAAI,CAAC,qBAAqB,KAAK,GAAG,CAChC,QAAO;EAAE,IAAI;EAAO,SAAS;EAAiB;AAEhD,QAAO;EAAE,IAAI;EAAM,MAAM,yBAAyB,KAAK,GAAG;EAAE;;;AAI9D,eAAe,gCACb,SACA,KACA,eACA,YACsE;CACtE,MAAM,KAAK,OAAO,kBAAkB,WAAW,cAAc,MAAM,GAAG;AACtE,KAAI,GACF,KAAI;AAEF,SAAO;GAAE,IAAI;GAAM,MAAA,MADA,QAAQ,oCAAoC,GAAG;GACzC;UAClB,KAAK;EACZ,MAAM,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC3D,MAAI,KAAK;GAAE;GAAK,YAAY;GAAI,EAAE,2CAA2C;AAC7E,SAAO;GAAE,IAAI;GAAO,SAAS,MAAM;GAAuC;;AAG9E,QAAO,2BAA2B,KAAK,WAAW;;AASpD,SAAS,6BAA6B,KAA4C;CAChF,MAAM,MAAM,IAAI,WAAW;AAC3B,QAAO;EACL,WAAW,KAAK,WAAW,MAAM,IAAI;EACrC,UAAU,KAAK,YAAY;EAC3B,gBAAgB,KAAK,kBAAkB;EACxC;;;AAIH,SAAS,uBAAuB,MAAsB;AACpD,QAAO,KACJ,QAAQ,UAAU,GAAG,CACrB,QAAQ,oBAAoB,GAAG,CAC/B,MAAM;;;;;;;AAQX,eAAe,4BACb,QACA,gBACA,cAAc,KAC2E;CACzF,MAAM,MAAM,QAAQ,eAAe;CACnC,MAAM,WAAW,SAAS,eAAe;CACzC,MAAM,SAAS,SAAS,YAAY,IAAI;CACxC,MAAM,OAAO,SAAS,IAAI,SAAS,MAAM,GAAG,OAAO,GAAG;CACtD,MAAM,MAAM,SAAS,IAAI,SAAS,MAAM,OAAO,GAAG;AAClD,MAAK,IAAI,UAAU,GAAG,WAAW,aAAa,WAAW;EACvD,MAAM,YAAY,YAAY,IAAI,iBAAiB,KAAK,KAAK,GAAG,KAAK,GAAG,UAAU,MAAM;AACxF,MAAI;AACF,SAAM,KAAK,QAAQ,UAAU;AAC7B,UAAO;IAAE,IAAI;IAAM,MAAM;IAAW,UAAU;IAAS;WAChD,KAAK;AAEZ,OADc,KAA+B,SAChC,SACX,OAAM;;;AAIZ,QAAO;EAAE,IAAI;EAAO,UAAU;EAAa;;AAG7C,SAAS,0BACP,OACA,aACA,cAC2B;AAC3B,KAAI,UAAU,YACZ,QAAO,cACH;EAAC;EAAgB;EAAkB;EAAW,GAC9C;EAAC;EAAW;EAAQ;EAAgB;EAAkB;EAAW;AAEvE,KAAI,UAAU,cAAc,UAAU,mBAAmB,UAAU,oBAAoB;EACrF,MAAM,OAAkC;GAAC;GAAgB;GAAkB;GAAW;AAGtF,MAAI,CAAC,eAAe,iBAAiB,cACnC,MAAK,KAAK,oBAAoB;AAEhC,SAAO;;AAET,KAAI,UAAU,UAAW,QAAO,CAAC,WAAW;AAC5C,QAAO,EAAE;;AAGX,SAAS,sBAAsB,QAA8D;AAC3F,QAAO,WAAW,kBAAkB,WAAW;;AAGjD,SAAgB,wBAAwB,eAAqB,MAAoC;CAC/F,MAAM,EAAE,YAAY;AAEpB,eAAc,IAAI,+BAA+B,OAAO,MAAM;EAC5D,MAAM,MAAM,EAAE,IAAI,MAAM,MAAM;AAC9B,MAAI,CAAC,OAAO,OAAO,QAAQ,SACzB,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,eAAe;GAAE,EAAE,IAAI;EAEtE,MAAM,MAAM,QAAQ;EAEpB,MAAM,MAAM,2BAA2B,EAAE,WADvB,oCAAoC,KAAK,EAAE,IAAI,MAAM,aAAa,CAClC,EAAE,EAAE,IAAI;AAC1D,MAAI,CAAC,IACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,aAAa;GAAE,EAAE,IAAI;AAEpE,MAAI;GACF,MAAM,MAAM,MAAM,SAAS,IAAI;GAuB/B,MAAM,cAAc;IApBlB,KAAK;IACL,KAAK;IACL,KAAK;IACL,MAAM;IACN,MAAM;IACN,KAAK;IACL,IAAI;IACJ,KAAK;IACL,MAAM;IACN,MAAM;IACN,KAAK;IACL,IAAI;IACJ,IAAI;IACJ,MAAM;IACN,KAAK;IACL,MAAM;IACN,KAAK;IACL,KAAK;IACL,KAAK;IAEsB,CAtBjB,IAAI,MAAM,IAAI,CAAC,KAAK,EAAE,aAAa,IAAI,OAsBb;AACtC,UAAO,IAAI,SAAS,KAAK,EACvB,SAAS;IACP,gBAAgB;IAChB,iBAAiB;IAClB,EACF,CAAC;UACI;AACN,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO,EAAE,SAAS,aAAa;IAAE,EAAE,IAAI;;GAEpE;AAEF,eAAc,IAAI,2BAA2B,OAAO,MAAM;EACxD,MAAM,MAAM,EAAE,IAAI,MAAM,MAAM;AAC9B,MAAI,CAAC,OAAO,OAAO,QAAQ,SACzB,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,eAAe;GAAE,EAAE,IAAI;EAEtE,MAAM,MAAM,QAAQ;EAEpB,MAAM,MAAM,uBAAuB,EAAE,WADnB,oCAAoC,KAAK,EAAE,IAAI,MAAM,aAAa,CACtC,EAAE,EAAE,IAAI;AACtD,MAAI,CAAC,IACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,aAAa;GAAE,EAAE,IAAI;AAEpE,MAAI;GACF,MAAM,MAAM,MAAM,SAAS,IAAI;GAS/B,MAAM,cAAc;IANlB,KAAK;IACL,MAAM;IACN,KAAK;IACL,KAAK;IACL,KAAK;IAEsB,CARjB,IAAI,MAAM,IAAI,CAAC,KAAK,EAAE,aAAa,IAAI,OAQb;AACtC,UAAO,IAAI,SAAS,KAAK,EACvB,SAAS;IACP,gBAAgB;IAChB,iBAAiB;IAClB,EACF,CAAC;UACI;AACN,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO,EAAE,SAAS,aAAa;IAAE,EAAE,IAAI;;GAEpE;AAEF,eAAc,IAAI,+BAA+B,OAAO,MAAM;EAC5D,MAAM,MAAM,uBAAuB,QAAQ,cAAc;AACzD,MAAI,CAAC,IACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,4BAA4B;GAAE,EAAE,IAAI;AAEnF,MAAI;GACF,MAAM,UAAU,MAAM,SAAS,KAAK,QAAQ;AAC5C,UAAO,EAAE,KAAK;IAAE,IAAI;IAAM,SAAS;KAAW;KAAS,MAAM;KAAgB;IAAE,CAAC;UAC1E;AACN,UAAO,EAAE,KAAK;IAAE,IAAI;IAAM,SAAS;KAAE,SAAS;KAAI,MAAM;KAAgB;IAAE,CAAC;;GAE7E;AAEF,eAAc,IAAI,+BAA+B,OAAO,MAAM;EAC5D,MAAM,MAAM,uBAAuB,QAAQ,cAAc;AACzD,MAAI,CAAC,IACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,4BAA4B;GAAE,EAAE,IAAI;EAEnF,IAAI;AACJ,MAAI;AACF,UAAO,MAAM,EAAE,IAAI,MAAM;UACnB;AACN,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO,EAAE,SAAS,gBAAgB;IAAE,EAAE,IAAI;;EAEvE,MAAM,UACJ,OAAO,SAAS,YAChB,SAAS,QACT,aAAa,QACb,OAAQ,KAA8B,YAAY,WAC7C,KAA6B,UAC9B;AACN,MAAI;AACF,SAAM,UAAU,KAAK,SAAS,QAAQ;AACtC,UAAO,EAAE,KAAK;IAAE,IAAI;IAAM,SAAS,EAAE,MAAM,gBAAgB;IAAE,CAAC;WACvD,KAAK;AACZ,OAAI,MAAM;IAAE;IAAK,MAAM;IAAK,EAAE,+BAA+B;AAC7D,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO,EAAE,SAAS,gBAAgB;IAAE,EAAE,IAAI;;GAEvE;AAEF,eAAc,IAAI,8BAA8B,OAAO,MAAM;EAC3D,MAAM,KAAK,MAAM,gCACf,SACA,QAAQ,eACR,EAAE,IAAI,MAAM,aAAa,EACzB,EAAE,IAAI,MAAM,UAAU,CACvB;AACD,MAAI,GAAG,OAAO,MACZ,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,GAAG,SAAS;GAAE,EAAE,IAAI;EAEnE,MAAM,gBAAgB,GAAG;EAEzB,MAAM,SAAS,yBAAyB,eADzB,OAAO,EAAE,IAAI,MAAM,MAAM,KAAK,WAAW,EAAE,IAAI,MAAM,MAAM,GAAI,GAChB;AAC9D,MAAI,CAAC,OACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,gBAAgB;GAAE,EAAE,IAAI;EAEvE,IAAI;AACJ,MAAI;AACF,QAAK,MAAM,KAAK,OAAO;UACjB;AACN,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO,EAAE,SAAS,aAAa;IAAE,EAAE,IAAI;;AAEpE,MAAI,CAAC,GAAG,aAAa,CACnB,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,mBAAmB;GAAE,EAAE,IAAI;EAE1E,MAAM,UAAU,MAAM,QAAQ,QAAQ,EAAE,eAAe,MAAM,CAAC;EAC9D,MAAM,UAAwF,EAAE;AAChG,OAAK,MAAM,SAAS,SAAS;AAC3B,OAAI,MAAM,KAAK,WAAW,IAAI,CAAE;GAChC,MAAM,WAAW,KAAK,QAAQ,MAAM,KAAK;AACzC,OAAI,MAAM,aAAa,CACrB,SAAQ,KAAK;IACX,MAAM,MAAM;IACZ,MAAM,yBAAyB,eAAe,SAAS;IACvD,cAAc;IACd,aAAa;IACd,CAAC;OAEF,SAAQ,KAAK;IACX,MAAM,MAAM;IACZ,MAAM,yBAAyB,eAAe,SAAS;IACvD,cAAc;IACd,aAAa;IACd,CAAC;;AAGN,UAAQ,MAAM,GAAG,MAAM;AACrB,OAAI,EAAE,gBAAgB,EAAE,YAAa,QAAO,EAAE,cAAc,KAAK;AACjE,UAAO,EAAE,KAAK,cAAc,EAAE,KAAK;IACnC;AACF,SAAO,EAAE,KAAK;GAAE,IAAI;GAAM,SAAS,EAAE,SAAS;GAAE,CAAC;GACjD;AAEF,eAAc,IAAI,8BAA8B,OAAO,MAAM;EAC3D,MAAM,UAAU,OAAO,EAAE,IAAI,MAAM,OAAO,KAAK,WAAW,EAAE,IAAI,MAAM,OAAO,GAAI;AACjF,MAAI,CAAC,QAAQ,MAAM,CACjB,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,gBAAgB;GAAE,EAAE,IAAI;EAEvE,MAAM,KAAK,MAAM,gCACf,SACA,QAAQ,eACR,EAAE,IAAI,MAAM,aAAa,EACzB,EAAE,IAAI,MAAM,UAAU,CACvB;AACD,MAAI,GAAG,OAAO,MACZ,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,GAAG,SAAS;GAAE,EAAE,IAAI;EAEnE,MAAM,gBAAgB,GAAG;EACzB,MAAM,MAAM,yBAAyB,eAAe,QAAQ;AAC5D,MAAI,CAAC,IACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,gBAAgB;GAAE,EAAE,IAAI;EAEvE,IAAI;AACJ,MAAI;AACF,QAAK,MAAM,KAAK,IAAI;UACd;AACN,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO,EAAE,SAAS,aAAa;IAAE,EAAE,IAAI;;AAEpE,MAAI,CAAC,GAAG,QAAQ,CACd,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,cAAc;GAAE,EAAE,IAAI;AAErE,MAAI;GACF,MAAM,UAAU,MAAM,SAAS,KAAK,QAAQ;AAC5C,UAAO,EAAE,KAAK;IACZ,IAAI;IACJ,SAAS;KACP;KACA,MAAM,yBAAyB,eAAe,IAAI;KAClD,cAAc;KACd,SAAS,GAAG;KACb;IACF,CAAC;UACI;AACN,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO,EAAE,SAAS,eAAe;IAAE,EAAE,IAAI;;GAEtE;;AAGF,eAAc,IAAI,qCAAqC,OAAO,MAAM;EAClE,MAAM,UAAU,OAAO,EAAE,IAAI,MAAM,OAAO,KAAK,WAAW,EAAE,IAAI,MAAM,OAAO,GAAI;AACjF,MAAI,CAAC,QAAQ,MAAM,CACjB,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,gBAAgB;GAAE,EAAE,IAAI;EAEvE,MAAM,KAAK,MAAM,gCACf,SACA,QAAQ,eACR,EAAE,IAAI,MAAM,aAAa,EACzB,EAAE,IAAI,MAAM,UAAU,CACvB;AACD,MAAI,GAAG,OAAO,MACZ,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,GAAG,SAAS;GAAE,EAAE,IAAI;EAEnE,MAAM,gBAAgB,GAAG;EACzB,MAAM,MAAM,yBAAyB,eAAe,QAAQ;AAC5D,MAAI,CAAC,IACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,gBAAgB;GAAE,EAAE,IAAI;EAEvE,IAAI;AACJ,MAAI;AACF,QAAK,MAAM,KAAK,IAAI;UACd;AACN,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO,EAAE,SAAS,aAAa;IAAE,EAAE,IAAI;;AAEpE,MAAI,CAAC,GAAG,QAAQ,CACd,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,cAAc;GAAE,EAAE,IAAI;AAErE,MAAI;GACF,MAAM,MAAM,MAAM,SAAS,IAAI;AAC/B,UAAO,EAAE,KAAK;IACZ,IAAI;IACJ,SAAS;KACP,eAAe,IAAI,SAAS,SAAS;KACrC,MAAM,yBAAyB,eAAe,IAAI;;KAElD,cAAc;KACd,SAAS,GAAG;KACb;IACF,CAAC;UACI;AACN,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO,EAAE,SAAS,eAAe;IAAE,EAAE,IAAI;;GAEtE;;AAGF,eAAc,IAAI,sCAAsC,OAAO,MAAM;EACnE,MAAM,MAAM,EAAE,IAAI,MAAM,eAAe;AACvC,MAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,CAAC,IAAI,MAAM,CAChD,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,wBAAwB;GAAE,EAAE,IAAI;EAE/E,MAAM,eAAe,IAAI,MAAM;EAC/B,MAAM,KAAK,MAAM,gCACf,SACA,QAAQ,eACR,EAAE,IAAI,MAAM,aAAa,EACzB,EAAE,IAAI,MAAM,UAAU,CACvB;AACD,MAAI,GAAG,OAAO,MACZ,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,GAAG,SAAS;GAAE,EAAE,IAAI;EAEnE,MAAM,gBAAgB,GAAG;EACzB,MAAM,aAAa,QAAQ,aAAa;AACxC,MAAI,CAAC,qBAAqB,eAAe,WAAW,CAClD,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,4BAA4B;GAAE,EAAE,IAAI;EAEnF,MAAM,MAAM,yBAAyB,eAAe,WAAW;AAC/D,SAAO,EAAE,KAAK;GAAE,IAAI;GAAM,SAAS,EAAE,uBAAuB,KAAK;GAAE,CAAC;GACpE;AAEF,eAAc,IAAI,2CAA2C,OAAO,MAAM;EACxE,MAAM,UAAU,OAAO,EAAE,IAAI,MAAM,OAAO,KAAK,WAAW,EAAE,IAAI,MAAM,OAAO,CAAE,MAAM,GAAG;AACxF,MAAI,CAAC,QACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAgB,SAAS;IAAgB;GAAE,EAAE,IAAI;EAG7F,MAAM,aAAa,OAAO,EAAE,IAAI,MAAM,aAAa,KAAK,WAAW,EAAE,IAAI,MAAM,aAAa,CAAE,MAAM,GAAG;EACvG,MAAM,KAAK,MAAM,gCACf,SACA,QAAQ,eACR,YACA,EAAE,IAAI,MAAM,UAAU,CACvB;AACD,MAAI,GAAG,OAAO,MACZ,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAA+B,SAAS,GAAG;IAAS;GAAE,EAAE,IAAI;EAGxG,MAAM,gBAAgB,GAAG;EACzB,MAAM,gBAAgB;GAAE,GAAG,+BAA+B,QAAQ,eAAe,WAAW;GAAE;GAAe;EAC7G,MAAM,cAAc,mBAAmB,QAAQ;EAC/C,MAAM,EAAE,WAAW,YAAY,MAAM,8BAA8B,SAAS,eAAe,cAAc;AAEzG,MAAI,CAAC,aAAa,QAChB,QAAO,EAAE,KAAK;GACZ,IAAI;GACJ,SAAS;IACP,WAAW;IACX;IACA,OAAO;IACP,QAAQ;IACR,cAAc,EAAE;IAChB,WAAW;IACZ;GACF,CAAC;EAGJ,IAAI,KAA8C;AAClD,MAAI;AACF,QAAK,MAAM,KAAK,UAAU;UACpB;AACN,QAAK;;AAGP,MAAI,CAAC,GAIH,QAAO,EAAE,KAAK;GACZ,IAAI;GACJ,SAAS;IACP,WAAW;IACX;IACA,OAAO;IACP,QAAQ;IACR,cAAc;IACd,cAAc,0BAA0B,WAAW,MAAM;IACzD,WAAW;IACZ;GACF,CAAC;EAIJ,MAAM,EAAE,OAAO,cAAc,gBADV,qBAAqB,WAAW,cACI;EACvD,MAAM,cAAc,UAAU;EAC9B,MAAM,cAAc,GAAG,aAAa;EACpC,MAAM,eAAe,0BAA0B,OAAO,aAAa,aAAa;EAChF,MAAM,MAAM,sBAAsB,SAAS;GACzC,cAAc;GACd,YAAY,cAAc,KAAA;GAC1B;GACA;GACA;GACD,CAAC;AAEF,SAAO,EAAE,KAAK;GACZ,IAAI;GACJ,SAAS;IACP,WAAW,IAAI;IACf,WAAW;IACX;IACA;IACA;IACA;IACA,QAAQ;IACR;IACA,cAAc;IACd,uBAAuB,cAAc,yBAAyB,eAAe,UAAU,GAAG,KAAA;IAC1F;IACA,SAAS,GAAG;IACb;GACF,CAAC;GACF;AAEF,eAAc,KAAK,8CAA8C,OAAO,MAAM;EAC5E,MAAM,KAAK,EAAE,IAAI,MAAM,KAAK,EAAE,MAAM,IAAI;AACxC,MAAI,CAAC,GACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAoB,SAAS;IAA0B;GAAE,EAAE,IAAI;EAG3G,MAAM,MAAM,sBAAsB,QAAQ,GAAG;AAC7C,MAAI,CAAC,IACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAoB,SAAS;IAA0B;GAAE,EAAE,IAAI;EAG3G,MAAM,aAAa,OAAO,EAAE,IAAI,MAAM,aAAa,KAAK,WAAW,EAAE,IAAI,MAAM,aAAa,CAAE,MAAM,GAAG;AACvG,MAAI,CAAC,wBAAwB,IAAI,YAAY,WAAW,CACtD,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAsB,SAAS;IAA4B;GAAE,EAAE,IAAI;EAI/G,MAAM,UAAS,MADK,EAAE,IAAI,MAAM,CAAC,aAAa,EAAE,EAAE,EAC9B;AACpB,MAAI,CAAC,sBAAsB,OAAO,CAChC,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAkB,SAAS;IAAkB;GAAE,EAAE,IAAI;AAEjG,MAAI,CAAC,IAAI,aAAa,SAAS,OAAO,CACpC,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAsB,SAAS;IAAsB;GAAE,EAAE,IAAI;EAGzG,IAAI;AACJ,MAAI;AACF,QAAK,MAAM,KAAK,IAAI,aAAa;UAC3B;AACN,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO;KAAE,MAAM;KAAkB,SAAS;KAAkB;IAAE,EAAE,IAAI;;AAGjG,SAAO,EAAE,KAAK;GACZ,IAAI;GACJ,SAAS;IACP,cAAc,IAAI;IAClB,aAAa,GAAG,aAAa;IAC9B;GACF,CAAC;GACF;AAEF,eAAc,KAAK,sCAAsC,OAAO,MAAM;EACpE,MAAM,KAAK,EAAE,IAAI,MAAM,KAAK,EAAE,MAAM,IAAI;AACxC,MAAI,CAAC,GACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAoB,SAAS;IAA0B;GAAE,EAAE,IAAI;EAG3G,MAAM,MAAM,sBAAsB,QAAQ,GAAG;AAC7C,MAAI,CAAC,IACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAoB,SAAS;IAA0B;GAAE,EAAE,IAAI;EAG3G,MAAM,aAAa,OAAO,EAAE,IAAI,MAAM,aAAa,KAAK,WAAW,EAAE,IAAI,MAAM,aAAa,CAAE,MAAM,GAAG;AACvG,MAAI,CAAC,wBAAwB,IAAI,YAAY,WAAW,CACtD,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAsB,SAAS;IAA4B;GAAE,EAAE,IAAI;AAG/G,MAAI,CAAC,IAAI,aAAa,SAAS,oBAAoB,CACjD,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAsB,SAAS;IAAoC;GAAE,EAAE,IAAI;EAGvH,IAAI;AACJ,MAAI;AACF,gBAAa,MAAM,KAAK,IAAI,aAAa;UACnC;AACN,yBAAsB,WAAW,GAAG;AACpC,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO;KAAE,MAAM;KAAoB,SAAS;KAAgC;IAAE,EAAE,IAAI;;AAEjH,MAAI,CAAC,WAAW,QAAQ,CACtB,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAmB,SAAS;IAAgC;GAAE,EAAE,IAAI;EAGhH,MAAM,YAAY,6BAA6B,QAAQ,cAAc;AACrE,MAAI,WAAW,OAAO,UAAU,SAC9B,QAAO,EAAE,KACP;GACE,IAAI;GACJ,OAAO;IACL,MAAM;IACN,SAAS,uCAAuC,UAAU,SAAS;IACpE;GACF,EACD,IACD;EAGH,MAAM,KAAK,MAAM,gCAAgC,SAAS,QAAQ,eAAe,YAAY,KAAA,EAAU;AACvG,MAAI,GAAG,OAAO,MACZ,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAA+B,SAAS,GAAG;IAAS;GAAE,EAAE,IAAI;EAExG,MAAM,gBAAgB,GAAG;EAEzB,IAAI;AACJ,MAAI;AACF,UAAQ,MAAM,EAAE,IAAI,MAAM,CAAC,aAAa,EAAE,EAAE;UACtC;AACN,UAAO,EAAE;;EAEX,MAAM,mBAAmB,OAAO,KAAK,gBAAgB,WAAW,KAAK,YAAY,MAAM,GAAG;EAC1F,MAAM,gBAAgB,OAAO,KAAK,eAAe,WAAW,KAAK,aAAa;AAC9E,MAAI,kBAAkB,YAAY,kBAAkB,eAAe,kBAAkB,QACnF,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAyB,SAAS;IAA4B;GAAE,EAAE,IAAI;EAElH,MAAM,aAAa;AACnB,MAAI,eAAe,eAAe,CAAC,UAAU,eAC3C,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAsB,SAAS;IAAmC;GAAE,EAAE,IAAI;EAGtH,MAAM,iBAAiB,uBAAuB,SAAS,IAAI,aAAa,CAAC,IAAI;EAC7E,IAAI;AACJ,MAAI,CAAC,iBACH,gBAAe,GAAG,UAAU,UAAU,GAAG;OACpC;GACL,MAAM,cAAc,iBAAiB,QAAQ,OAAO,IAAI;AAExD,kBAAe,YAAY,SAAS,IAAI,GAAG,GAAG,cAAc,mBAAmB;;EAGjF,IAAI,iBAAiB,yBAAyB,eAAe,aAAa;AAC1E,MAAI,CAAC,eACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAuB,SAAS;IAA4B;GAAE,EAAE,IAAI;EAIhH,MAAM,cAAc,kBAAkB,gBAAgB,cAAc;AACpE,MAAI,CAAC,YAAY,QACf,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAuB,SAAS,YAAY,UAAU;IAAuB;GAAE,EAAE,IAAI;AAGjI,MAAI,QAAQ,IAAI,aAAa,KAAK,QAAQ,eAAe,CACvD,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAiB,SAAS;IAAqC;GAAE,EAAE,IAAI;EAGnH,MAAM,UAAU,QAAQ,eAAe;AACvC,MAAI;AACF,SAAM,MAAM,SAAS,EAAE,WAAW,MAAM,CAAC;WAClC,KAAK;AACZ,OAAI,KAAK;IAAE;IAAK;IAAS,EAAE,gDAAgD;AAC3E,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO;KAAE,MAAM;KAAiB,SAAS;KAAiC;IAAE,EAAE,IAAI;;EAM/G,MAAM,SAAS,KAAK,SAAS,IADT,SAAS,eAAe,CAAC,UAAU,YAAY,CAAC,MAC/B;EAErC,MAAM,UAAU,KAAK,KAAK;EAC1B,IAAI,UAAU;EACd,IAAI,YAAY;EAChB,IAAI,eAAe;AAEnB,MAAI;AACF,OAAI;AACF,UAAM,SAAS,IAAI,cAAc,QAAQA,UAAY,iBAAiB;YAC/D,KAAK;AACZ,UAAM,OAAO,OAAO,CAAC,YAAY,GAAG;AACpC,QAAI,KAAK;KAAE;KAAK,QAAQ,IAAI;KAAc;KAAQ,EAAE,yCAAyC;AAC7F,WAAO,EAAE,KAAK;KAAE,IAAI;KAAO,OAAO;MAAE,MAAM;MAAiB,SAAS;MAA8B;KAAE,EAAE,IAAI;;AAG5G,OAAI,eAAe,aAAa;AAE9B,gBAAY,MAAM,KAAK,eAAe,CAAC,WAAW,KAAK,CAAC,YAAY,MAAM;AAC1E,QAAI;AACF,WAAM,OAAO,QAAQ,eAAe;aAC7B,KAAK;AACZ,WAAM,OAAO,OAAO,CAAC,YAAY,GAAG;AACpC,SAAI,KAAK;MAAE;MAAK,QAAQ;MAAgB,EAAE,uBAAuB;AACjE,YAAO,EAAE,KAAK;MAAE,IAAI;MAAO,OAAO;OAAE,MAAM;OAAiB,SAAS;OAA6B;MAAE,EAAE,IAAI;;cAElG,eAAe,SAAS;AAEjC,QAAI,MADiB,KAAK,eAAe,CAAC,WAAW,KAAK,CAAC,YAAY,MAAM,EACjE;AACV,WAAM,OAAO,OAAO,CAAC,YAAY,GAAG;AACpC,YAAO,EAAE,KAAK;MAAE,IAAI;MAAO,OAAO;OAAE,MAAM;OAAsB,SAAS;OAA8B;MAAE,EAAE,IAAI;;AAEjH,QAAI;AACF,WAAM,KAAK,QAAQ,eAAe;AAClC,WAAM,OAAO,OAAO,CAAC,YAAY,GAAG;aAC7B,KAAK;AACZ,WAAM,OAAO,OAAO,CAAC,YAAY,GAAG;AACpC,SAAI,KAAK;MAAE;MAAK,QAAQ;MAAgB,EAAE,kCAAkC;AAC5E,YAAO,EAAE,KAAK;MAAE,IAAI;MAAO,OAAO;OAAE,MAAM;OAAiB,SAAS;OAA6B;MAAE,EAAE,IAAI;;UAEtG;IAEL,MAAM,SAAS,MAAM,4BAA4B,QAAQ,eAAe;AACxE,QAAI,CAAC,OAAO,IAAI;AACd,WAAM,OAAO,OAAO,CAAC,YAAY,GAAG;AACpC,SAAI,KAAK;MAAE,QAAQ;MAAgB,UAAU,OAAO;MAAU,EAAE,yCAAyC;AACzG,YAAO,EAAE,KAAK;MAAE,IAAI;MAAO,OAAO;OAAE,MAAM;OAAiB,SAAS;OAAsC;MAAE,EAAE,IAAI;;AAEpH,UAAM,OAAO,OAAO,CAAC,YAAY,GAAG;AACpC,mBAAe,OAAO;AACtB,cAAU,OAAO,SAAS;;WAErB,KAAK;AACZ,SAAM,OAAO,OAAO,CAAC,YAAY,GAAG;AACpC,OAAI,MAAM,EAAE,KAAK,EAAE,iCAAiC;AACpD,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO;KAAE,MAAM;KAAiB,SAAS;KAAiB;IAAE,EAAE,IAAI;;EAG/F,IAAI;AACJ,MAAI;AACF,iBAAc,MAAM,KAAK,aAAa,EAAE;UAClC;AACN,gBAAa,KAAK,KAAK;;EAGzB,MAAM,eAAe,yBAAyB,eAAe,aAAa;AAE1E,wBAAsB,WAAW,GAAG;EACpC,MAAM,SAAS,sBAAsB,SAAS;GAC5C,cAAc;GACd,YAAY,cAAc,KAAA;GAC1B,OAAO;GACP,cAAc,0BAA0B,aAAa,MAAM;GAC5D,CAAC;EAEF,MAAM,cAAc,IAAI;EACxB,MAAM,qBAAqB,IAAI;AAE/B,UAAQ,KAAK,2BAA2B;GACtC,YAAY,cAAc,KAAA;GAC1B,uBAAuB;GACvB,cAAc;GACd,OAAO,WAAW;GAClB;GACA;GACD,CAAC;AAEF,MAAI,KACF;GACE;GACA,WAAW;GACX,oBAAoB,IAAI;GACxB;GACA;GACA,2BAA2B;GAC3B,OAAO,WAAW;GAClB;GACA;GACA,YAAY,KAAK,KAAK,GAAG;GAC1B,EACD,kCACD;AAED,SAAO,EAAE,KAAK;GACZ,IAAI;GACJ,SAAS;IACP,uBAAuB;IACvB,cAAc;IACd,aAAa,WAAW;IACxB,oBAAoB,IAAI;IACxB;IACA;IACA;IACA;IACA,SAAS;IACT,cAAc,OAAO;IACtB;GACF,CAAC;GACF;;;;;AAMF,eAAc,IAAI,6BAA6B,OAAO,MAAM;EAC1D,MAAM,UAAU,OAAO,EAAE,IAAI,MAAM,OAAO,KAAK,WAAW,EAAE,IAAI,MAAM,OAAO,GAAI;AACjF,MAAI,CAAC,QAAQ,MAAM,CACjB,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,gBAAgB;GAAE,EAAE,IAAI;EAEvE,MAAM,KAAK,MAAM,gCACf,SACA,QAAQ,eACR,EAAE,IAAI,MAAM,aAAa,EACzB,EAAE,IAAI,MAAM,UAAU,CACvB;AACD,MAAI,GAAG,OAAO,MACZ,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,GAAG,SAAS;GAAE,EAAE,IAAI;EAEnE,MAAM,gBAAgB,GAAG;EACzB,MAAM,MAAM,yBAAyB,eAAe,QAAQ;AAC5D,MAAI,CAAC,IACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,gBAAgB;GAAE,EAAE,IAAI;EAEvE,IAAI;AACJ,MAAI;AACF,QAAK,MAAM,KAAK,IAAI;UACd;AACN,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO,EAAE,SAAS,aAAa;IAAE,EAAE,IAAI;;AAEpE,MAAI,CAAC,GAAG,QAAQ,CACd,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,cAAc;GAAE,EAAE,IAAI;EA6BrE,MAAM,cAAc;GAzBlB,KAAK;GACL,KAAK;GACL,MAAM;GACN,KAAK;GACL,MAAM;GACN,KAAK;GACL,KAAK;GACL,KAAK;GACL,MAAM;GACN,MAAM;GACN,MAAM;GACN,KAAK;GACL,IAAI;GACJ,MAAM;GACN,MAAM;GACN,KAAK;GACL,IAAI;GACJ,IAAI;GACJ,KAAK;GACL,KAAK;GACL,KAAK;GACL,MAAM;GACN,KAAK;GACL,KAAK;GAEsB,CA3BjB,QAAQ,MAAM,IAAI,CAAC,KAAK,EAAE,aAAa,IAAI,OA2BjB;AACtC,MAAI;GACF,MAAM,MAAM,MAAM,SAAS,IAAI;AAC/B,UAAO,IAAI,SAAS,KAAK,EACvB,SAAS;IACP,gBAAgB;IAChB,iBAAiB;IAClB,EACF,CAAC;UACI;AACN,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO,EAAE,SAAS,eAAe;IAAE,EAAE,IAAI;;GAEtE;AAEF,eAAc,IAAI,+BAA+B,OAAO,MAAM;EAC5D,MAAM,KAAK,MAAM,gCACf,SACA,QAAQ,eACR,EAAE,IAAI,MAAM,aAAa,EACzB,EAAE,IAAI,MAAM,UAAU,CACvB;AACD,MAAI,GAAG,OAAO,MACZ,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,GAAG,SAAS;GAAE,EAAE,IAAI;EAEnE,MAAM,gBAAgB,GAAG;EACzB,IAAI;AACJ,MAAI;AACF,UAAO,MAAM,EAAE,IAAI,MAAM;UACnB;AACN,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO,EAAE,SAAS,gBAAgB;IAAE,EAAE,IAAI;;EAEvE,MAAM,UACJ,OAAO,SAAS,YAChB,SAAS,QACT,UAAU,QACV,OAAQ,KAA2B,SAAS,WACvC,KAA0B,OAC3B;EACN,MAAM,UACJ,OAAO,SAAS,YAChB,SAAS,QACT,aAAa,QACb,OAAQ,KAA8B,YAAY,WAC7C,KAA6B,UAC9B;AACN,MAAI,CAAC,QAAQ,MAAM,CACjB,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,gBAAgB;GAAE,EAAE,IAAI;EAEvE,MAAM,MAAM,yBAAyB,eAAe,QAAQ;AAC5D,MAAI,CAAC,IACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,gBAAgB;GAAE,EAAE,IAAI;EAEvE,IAAI;AACJ,MAAI;AACF,QAAK,MAAM,KAAK,IAAI;UACd;AACN,QAAK,KAAA;;AAEP,MAAI,MAAM,CAAC,GAAG,QAAQ,CACpB,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,cAAc;GAAE,EAAE,IAAI;AAErE,MAAI;AACF,SAAM,UAAU,KAAK,SAAS,QAAQ;GACtC,IAAI;AACJ,OAAI;AACF,eAAW,MAAM,KAAK,IAAI,EAAE;WACtB;AACN,cAAU,KAAK,KAAK;;AAEtB,UAAO,EAAE,KAAK;IACZ,IAAI;IACJ,SAAS;KAAE,MAAM,yBAAyB,eAAe,IAAI;KAAE;KAAS;IACzE,CAAC;WACK,KAAK;AACZ,OAAI,MAAM;IAAE;IAAK,MAAM;IAAK,EAAE,gCAAgC;AAC9D,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO,EAAE,SAAS,gBAAgB;IAAE,EAAE,IAAI;;GAEvE;AAEF,eAAc,IAAI,gCAAgC,OAAO,MAAM;EAC7D,MAAM,IAAI,OAAO,EAAE,IAAI,MAAM,IAAI,KAAK,WAAW,EAAE,IAAI,MAAM,IAAI,CAAE,MAAM,GAAG;EAC5E,MAAM,SAAS,OAAO,EAAE,IAAI,MAAM,MAAM,KAAK,WAAW,EAAE,IAAI,MAAM,MAAM,GAAI;AAC9E,MAAI,CAAC,EACH,QAAO,EAAE,KAAK;GACZ,IAAI;GACJ,SAAS,EAAE,SAAS,EAAE,EAA2G;GAClI,CAAC;EAEJ,MAAM,KAAK,MAAM,gCACf,SACA,QAAQ,eACR,EAAE,IAAI,MAAM,aAAa,EACzB,EAAE,IAAI,MAAM,UAAU,CACvB;AACD,MAAI,GAAG,OAAO,MACZ,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,GAAG,SAAS;GAAE,EAAE,IAAI;EAEnE,MAAM,gBAAgB,GAAG;EACzB,MAAM,SAAS,yBAAyB,eAAe,OAAO;AAC9D,MAAI,CAAC,OACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,gBAAgB;GAAE,EAAE,IAAI;EAEvE,IAAI;AACJ,MAAI;AACF,QAAK,MAAM,KAAK,OAAO;UACjB;AACN,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO,EAAE,SAAS,aAAa;IAAE,EAAE,IAAI;;AAEpE,MAAI,CAAC,GAAG,aAAa,CACnB,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,mBAAmB;GAAE,EAAE,IAAI;EAG1E,MAAM,WAAU,MADE,sBAAsB,GAAG,OAAO,EAE/C,QAAQ,MAAM,qBAAqB,eAAe,EAAE,SAAS,CAAC,CAC9D,KAAK,OAAO;GACX,GAAG;GACH,UAAU,yBAAyB,eAAe,QAAQ,EAAE,SAAS,CAAC;GACvE,EAAE;AACL,SAAO,EAAE,KAAK;GAAE,IAAI;GAAM,SAAS,EAAE,SAAS;GAAE,CAAC;GACjD;;AAGF,eAAc,IAAI,sCAAsC,OAAO,MAAM;EACnE,MAAM,IAAI,OAAO,EAAE,IAAI,MAAM,IAAI,KAAK,WAAW,EAAE,IAAI,MAAM,IAAI,CAAE,MAAM,GAAG;EAC5E,MAAM,WAAW,EAAE,IAAI,MAAM,QAAQ;EACrC,MAAM,QAAQ,KAAK,IACjB,KAAK,IAAI,SAAS,OAAO,aAAa,WAAW,WAAW,MAAM,GAAG,IAAI,IAAI,EAAE,EAC/E,sBACD;EAED,MAAM,KAAK,MAAM,gCACf,SACA,QAAQ,eACR,EAAE,IAAI,MAAM,aAAa,EACzB,EAAE,IAAI,MAAM,UAAU,CACvB;AACD,MAAI,GAAG,OAAO,MACZ,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,GAAG,SAAS;GAAE,EAAE,IAAI;EAGnE,MAAM,UAAU,MAAM,0BAA0B,GAAG,MAAM,GAAG,MAAM;AAClE,SAAO,EAAE,KAAK;GAAE,IAAI;GAAM,SAAS,EAAE,SAAS;GAAE,CAAC;GACjD"}
1
+ {"version":3,"file":"workspace.js","names":["fsConstants"],"sources":["../../../../../src/gateway/hono/routes/workspace.ts"],"sourcesContent":["import type { Hono } from 'hono';\nimport { randomUUID } from 'node:crypto';\nimport { constants as fsConstants } from 'node:fs';\nimport { copyFile, link, mkdir, readdir, readFile, rename, stat, unlink, writeFile } from 'node:fs/promises';\nimport { basename, dirname, join, resolve } from 'node:path';\n\nimport { extractProfileAgentId } from '../../../config/agent-profile.js';\nimport { type Config } from '../../../config/schema.js';\nimport { getWorkspacePath } from '../../../config/workspace-path-helpers.js';\nimport { validateWritePath } from '../../../agent/sandbox/path-policy.js';\nimport { resolveSafeInboundFilePath } from '../../../channels/attachments/inbound-persist.js';\nimport { resolveSafeTtsFilePath } from '../../../channels/attachments/outbound-tts-persist.js';\nimport {\n listAgentEntries,\n normalizeAgentId,\n resolveAgentHomeDir,\n resolveAgentWorkspaceDir,\n resolveDefaultAgentId,\n} from '../../../agent/agent-scope.js';\nimport { createLogger } from '../../../utils/logger.js';\nimport { resolveHeartbeatMdPath } from '../../workspace-heartbeat-path.js';\nimport {\n isPathUnderWorkspace,\n resolveWorkspaceSafePath,\n toWorkspaceRelativePosix,\n} from '../../workspace-editor-path.js';\nimport { listWorkspaceRelativeFilesFsFallback } from '../../workspace-fs-file-list.js';\nimport { runRipgrepInDirectory, runRipgrepListFiles } from '../../workspace-ripgrep.js';\nimport {\n buildFilePathClassifierContext,\n classifyFileLocation,\n displayNameForPath,\n fileRefSessionKeysMatch,\n resolveFileReferenceCandidate,\n} from '../../file-path-classifier.js';\nimport {\n fileReferenceRegistry,\n type FileReferenceCapability,\n type FileReferenceLocationKind,\n type FileReferenceScope,\n} from '../../file-reference-registry.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\nimport type { GatewayService } from '../../service.js';\n\nconst log = createLogger('HonoApp');\n\n/** Agent home for persisted `inbound/` and `tts/` attachments (matches `persistOutboundTtsAudio` / `prepareInboundAttachments`). */\nfunction resolvePersistedAttachmentAgentHome(cfg: Config, sessionKeyRaw: string | undefined): string {\n const sk = typeof sessionKeyRaw === 'string' ? sessionKeyRaw.trim() : '';\n const agentId = sk ? extractProfileAgentId(sk, cfg) : resolveDefaultAgentId(cfg);\n return resolveAgentHomeDir(cfg, agentId);\n}\n\nconst FILE_SEARCH_MAX_LIMIT = 50;\n\n/** Subsequence fuzzy match: all query chars appear in order in `candidate` (case-insensitive). */\nfunction fuzzySubsequenceScore(query: string, candidate: string): number | null {\n const q = query.toLowerCase();\n const c = candidate.toLowerCase();\n if (q.length === 0) return 0;\n let qi = 0;\n for (let ci = 0; ci < c.length && qi < q.length; ci++) {\n if (c[ci] === q[qi]) qi++;\n }\n if (qi < q.length) return null;\n const base = c.split('/').pop() ?? c;\n let score = 10;\n if (c.startsWith(q)) score += 40;\n if (base.startsWith(q)) score += 35;\n else if (base.includes(q)) score += 20;\n else if (c.includes(q)) score += 10;\n score -= c.length * 0.0001;\n return score;\n}\n\nasync function fuzzySearchWorkspaceFiles(\n workspaceRoot: string,\n query: string,\n limit: number,\n): Promise<Array<{ name: string; path: string; isDirectory: boolean }>> {\n let files = await runRipgrepListFiles(workspaceRoot);\n if (files.length === 0) {\n files = await listWorkspaceRelativeFilesFsFallback(workspaceRoot, 120_000);\n if (files.length > 0) {\n log.debug(\n { workspaceRoot, fileCount: files.length },\n 'workspace files/search: file list from fs walk (ripgrep unavailable or returned empty)',\n );\n }\n }\n const q = query.trim();\n const capped = Math.min(Math.max(limit, 1), FILE_SEARCH_MAX_LIMIT);\n\n type Row = { name: string; path: string; isDirectory: boolean; score: number };\n const rows: Row[] = [];\n\n if (!q) {\n const sorted = [...files].sort((a, b) => a.localeCompare(b));\n for (const rel of sorted.slice(0, capped)) {\n const name = rel.split('/').pop() ?? rel;\n rows.push({ name, path: rel, isDirectory: false, score: 0 });\n }\n return rows;\n }\n\n for (const rel of files) {\n const name = rel.split('/').pop() ?? rel;\n const scorePath = fuzzySubsequenceScore(q, rel);\n const scoreName = fuzzySubsequenceScore(q, name);\n const score = Math.max(scorePath ?? -Infinity, scoreName ?? -Infinity);\n if (score === -Infinity) continue;\n rows.push({ name, path: rel, isDirectory: false, score });\n }\n\n rows.sort((a, b) => b.score - a.score || a.path.localeCompare(b.path));\n return rows.slice(0, capped).map(({ name, path, isDirectory }) => ({ name, path, isDirectory }));\n}\n\nfunction isKnownEditorAgentId(cfg: Config, id: string): boolean {\n const n = normalizeAgentId(id);\n if (n === resolveDefaultAgentId(cfg)) return true;\n return listAgentEntries(cfg).some((e) => normalizeAgentId(e.id) === n);\n}\n\nfunction resolveEditorWorkspaceRoot(\n cfg: Config,\n agentIdRaw: string | undefined,\n): { ok: true; root: string } | { ok: false; message: string } {\n const trimmed = typeof agentIdRaw === 'string' ? agentIdRaw.trim() : '';\n if (!trimmed) {\n const root = getWorkspacePath(cfg);\n if (!root) return { ok: false, message: 'Workspace not configured' };\n return { ok: true, root };\n }\n const id = normalizeAgentId(trimmed);\n if (!isKnownEditorAgentId(cfg, id)) {\n return { ok: false, message: 'Unknown agent' };\n }\n return { ok: true, root: resolveAgentWorkspaceDir(cfg, id) };\n}\n\n/** Prefer `sessionKey` (per-session workspace override) over `agentId`. */\nasync function resolveEditorWorkspaceRootAsync(\n service: GatewayService,\n cfg: Config,\n sessionKeyRaw: string | undefined,\n agentIdRaw: string | undefined,\n): Promise<{ ok: true; root: string } | { ok: false; message: string }> {\n const sk = typeof sessionKeyRaw === 'string' ? sessionKeyRaw.trim() : '';\n if (sk) {\n try {\n const root = await service.sessions.getEffectiveWorkspacePath(sk);\n return { ok: true, root };\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n log.warn({ err, sessionKey: sk }, 'Session workspace root resolution failed');\n return { ok: false, message: em || 'Session workspace resolution failed' };\n }\n }\n return resolveEditorWorkspaceRoot(cfg, agentIdRaw);\n}\n\ninterface ResolvedWorkspaceImportConfig {\n targetDir: string;\n maxBytes: number;\n allowOverwrite: boolean;\n}\n\nfunction resolveWorkspaceImportConfig(cfg: Config): ResolvedWorkspaceImportConfig {\n const raw = cfg.workspace?.import;\n return {\n targetDir: raw?.targetDir?.trim() || 'imports',\n maxBytes: raw?.maxBytes ?? 104_857_600,\n allowOverwrite: raw?.allowOverwrite ?? true,\n };\n}\n\n/** Strip path separators, NULs and control chars from a basename so it stays in the destination dir. */\nfunction sanitizeImportBasename(name: string): string {\n return name\n .replace(/[\\\\/]/g, '')\n .replace(/[\\x00-\\x1f\\x7f]/g, '')\n .trim();\n}\n\n/**\n * Race-safe target picker for the `rename`-on-conflict strategy. Uses `link(tmp, target)`\n * which atomically fails with EEXIST when the candidate is taken; on success the tmp\n * file is left in place for the caller to unlink. Returns the linked target path.\n */\nasync function pickAvailableTargetWithLink(\n tmpAbs: string,\n initialDestAbs: string,\n maxAttempts = 1000,\n): Promise<{ ok: true; path: string; attempts: number } | { ok: false; attempts: number }> {\n const dir = dirname(initialDestAbs);\n const original = basename(initialDestAbs);\n const dotIdx = original.lastIndexOf('.');\n const stem = dotIdx > 0 ? original.slice(0, dotIdx) : original;\n const ext = dotIdx > 0 ? original.slice(dotIdx) : '';\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n const candidate = attempt === 1 ? initialDestAbs : join(dir, `${stem}-${attempt}${ext}`);\n try {\n await link(tmpAbs, candidate);\n return { ok: true, path: candidate, attempts: attempt };\n } catch (err) {\n const code = (err as NodeJS.ErrnoException)?.code;\n if (code !== 'EEXIST') {\n throw err;\n }\n }\n }\n return { ok: false, attempts: maxAttempts };\n}\n\nfunction fileReferenceCapabilities(\n scope: FileReferenceScope,\n isDirectory: boolean,\n locationKind?: FileReferenceLocationKind,\n): FileReferenceCapability[] {\n if (scope === 'workspace') {\n return isDirectory\n ? ['openExternal', 'revealInFolder', 'copyPath']\n : ['preview', 'edit', 'openExternal', 'revealInFolder', 'copyPath'];\n }\n if (scope === 'external' || scope === 'agent-profile' || scope === 'session-artifact') {\n const base: FileReferenceCapability[] = ['openExternal', 'revealInFolder', 'copyPath'];\n // v1: importToWorkspace for files only; exclude xopc-config to prevent copying\n // app config into the workspace (semantically wrong).\n if (!isDirectory && locationKind !== 'xopc-config') {\n base.push('importToWorkspace');\n }\n return base;\n }\n if (scope === 'missing') return ['copyPath'];\n return [];\n}\n\nfunction isFileReferenceAction(action: unknown): action is 'openExternal' | 'revealInFolder' {\n return action === 'openExternal' || action === 'revealInFolder';\n}\n\nexport function registerWorkspaceRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service } = deps;\n\n authenticated.get('/api/workspace/inbound-file', async (c) => {\n const rel = c.req.query('rel');\n if (!rel || typeof rel !== 'string') {\n return c.json({ ok: false, error: { message: 'Missing rel' } }, 400);\n }\n const cfg = service.currentConfig;\n const agentHome = resolvePersistedAttachmentAgentHome(cfg, c.req.query('sessionKey'));\n const abs = resolveSafeInboundFilePath({ agentHome }, rel);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Forbidden' } }, 403);\n }\n try {\n const buf = await readFile(abs);\n const ext = rel.split('.').pop()?.toLowerCase() ?? '';\n const mimeByExt: Record<string, string> = {\n pdf: 'application/pdf',\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n webp: 'image/webp',\n gif: 'image/gif',\n md: 'text/markdown',\n txt: 'text/plain',\n json: 'application/json',\n html: 'text/html',\n css: 'text/css',\n js: 'text/javascript',\n ts: 'text/typescript',\n webm: 'audio/webm',\n ogg: 'audio/ogg',\n opus: 'audio/ogg',\n mp3: 'audio/mpeg',\n wav: 'audio/wav',\n m4a: 'audio/mp4',\n };\n const contentType = mimeByExt[ext] || 'application/octet-stream';\n return new Response(buf, {\n headers: {\n 'Content-Type': contentType,\n 'Cache-Control': 'private, max-age=3600',\n },\n });\n } catch {\n return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n }\n });\n\n authenticated.get('/api/workspace/tts-file', async (c) => {\n const rel = c.req.query('rel');\n if (!rel || typeof rel !== 'string') {\n return c.json({ ok: false, error: { message: 'Missing rel' } }, 400);\n }\n const cfg = service.currentConfig;\n const agentHome = resolvePersistedAttachmentAgentHome(cfg, c.req.query('sessionKey'));\n const abs = resolveSafeTtsFilePath({ agentHome }, rel);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Forbidden' } }, 403);\n }\n try {\n const buf = await readFile(abs);\n const ext = rel.split('.').pop()?.toLowerCase() ?? '';\n const mimeByExt: Record<string, string> = {\n ogg: 'audio/ogg',\n opus: 'audio/ogg',\n mp3: 'audio/mpeg',\n wav: 'audio/wav',\n m4a: 'audio/mp4',\n };\n const contentType = mimeByExt[ext] || 'application/octet-stream';\n return new Response(buf, {\n headers: {\n 'Content-Type': contentType,\n 'Cache-Control': 'private, max-age=3600',\n },\n });\n } catch {\n return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n }\n });\n\n authenticated.get('/api/workspace/heartbeat-md', async (c) => {\n const abs = resolveHeartbeatMdPath(service.currentConfig);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Workspace not configured' } }, 400);\n }\n try {\n const content = await readFile(abs, 'utf-8');\n return c.json({ ok: true, payload: { content: content, file: 'HEARTBEAT.md' } });\n } catch {\n return c.json({ ok: true, payload: { content: '', file: 'HEARTBEAT.md' } });\n }\n });\n\n authenticated.put('/api/workspace/heartbeat-md', async (c) => {\n const abs = resolveHeartbeatMdPath(service.currentConfig);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Workspace not configured' } }, 400);\n }\n let body: unknown;\n try {\n body = await c.req.json();\n } catch {\n return c.json({ ok: false, error: { message: 'Invalid JSON' } }, 400);\n }\n const content =\n typeof body === 'object' &&\n body !== null &&\n 'content' in body &&\n typeof (body as { content: unknown }).content === 'string'\n ? (body as { content: string }).content\n : '';\n try {\n await writeFile(abs, content, 'utf-8');\n return c.json({ ok: true, payload: { file: 'HEARTBEAT.md' } });\n } catch (err) {\n log.error({ err, path: abs }, 'Failed to write HEARTBEAT.md');\n return c.json({ ok: false, error: { message: 'Write failed' } }, 500);\n }\n });\n\n authenticated.get('/api/workspace/editor/list', async (c) => {\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n const dirRel = typeof c.req.query('dir') === 'string' ? c.req.query('dir')! : '';\n const absDir = resolveWorkspaceSafePath(workspaceRoot, dirRel);\n if (!absDir) {\n return c.json({ ok: false, error: { message: 'Invalid path' } }, 400);\n }\n let st: Awaited<ReturnType<typeof stat>>;\n try {\n st = await stat(absDir);\n } catch {\n return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n }\n if (!st.isDirectory()) {\n return c.json({ ok: false, error: { message: 'Not a directory' } }, 400);\n }\n const dirents = await readdir(absDir, { withFileTypes: true });\n const entries: { name: string; path: string; absolutePath: string; isDirectory: boolean }[] = [];\n for (const entry of dirents) {\n if (entry.name.startsWith('.')) continue;\n const fullPath = join(absDir, entry.name);\n if (entry.isDirectory()) {\n entries.push({\n name: entry.name,\n path: toWorkspaceRelativePosix(workspaceRoot, fullPath),\n absolutePath: fullPath,\n isDirectory: true,\n });\n } else {\n entries.push({\n name: entry.name,\n path: toWorkspaceRelativePosix(workspaceRoot, fullPath),\n absolutePath: fullPath,\n isDirectory: false,\n });\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({ ok: true, payload: { entries } });\n });\n\n authenticated.get('/api/workspace/editor/read', async (c) => {\n const pathRel = typeof c.req.query('path') === 'string' ? c.req.query('path')! : '';\n if (!pathRel.trim()) {\n return c.json({ ok: false, error: { message: 'Missing path' } }, 400);\n }\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n const abs = resolveWorkspaceSafePath(workspaceRoot, pathRel);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Invalid path' } }, 400);\n }\n let st: Awaited<ReturnType<typeof stat>>;\n try {\n st = await stat(abs);\n } catch {\n return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n }\n if (!st.isFile()) {\n return c.json({ ok: false, error: { message: 'Not a file' } }, 400);\n }\n try {\n const content = await readFile(abs, 'utf-8');\n return c.json({\n ok: true,\n payload: {\n content,\n path: toWorkspaceRelativePosix(workspaceRoot, abs),\n absolutePath: abs,\n mtimeMs: st.mtimeMs,\n },\n });\n } catch {\n return c.json({ ok: false, error: { message: 'Read failed' } }, 500);\n }\n });\n\n /** Read file as raw bytes and return base64 (for PDF/images in workspace preview — avoids UTF-8 corruption). */\n authenticated.get('/api/workspace/editor/read-base64', async (c) => {\n const pathRel = typeof c.req.query('path') === 'string' ? c.req.query('path')! : '';\n if (!pathRel.trim()) {\n return c.json({ ok: false, error: { message: 'Missing path' } }, 400);\n }\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n const abs = resolveWorkspaceSafePath(workspaceRoot, pathRel);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Invalid path' } }, 400);\n }\n let st: Awaited<ReturnType<typeof stat>>;\n try {\n st = await stat(abs);\n } catch {\n return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n }\n if (!st.isFile()) {\n return c.json({ ok: false, error: { message: 'Not a file' } }, 400);\n }\n try {\n const buf = await readFile(abs);\n return c.json({\n ok: true,\n payload: {\n contentBase64: buf.toString('base64'),\n path: toWorkspaceRelativePosix(workspaceRoot, abs),\n /** Host absolute path — Electron can open with the default app (shell.openPath). */\n absolutePath: abs,\n mtimeMs: st.mtimeMs,\n },\n });\n } catch {\n return c.json({ ok: false, error: { message: 'Read failed' } }, 500);\n }\n });\n\n /** Map an absolute host path to a workspace-relative path (if under this session’s workspace). */\n authenticated.get('/api/workspace/editor/resolve-path', async (c) => {\n const raw = c.req.query('absolutePath');\n if (!raw || typeof raw !== 'string' || !raw.trim()) {\n return c.json({ ok: false, error: { message: 'Missing absolutePath' } }, 400);\n }\n const absolutePath = raw.trim();\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n const normalized = resolve(absolutePath);\n if (!isPathUnderWorkspace(workspaceRoot, normalized)) {\n return c.json({ ok: false, error: { message: 'Path not under workspace' } }, 403);\n }\n const rel = toWorkspaceRelativePosix(workspaceRoot, normalized);\n return c.json({ ok: true, payload: { workspaceRelativePath: rel } });\n });\n\n authenticated.get('/api/workspace/editor/resolve-reference', async (c) => {\n const rawPath = typeof c.req.query('path') === 'string' ? c.req.query('path')!.trim() : '';\n if (!rawPath) {\n return c.json({ ok: false, error: { code: 'INVALID_PATH', message: 'Missing path' } }, 400);\n }\n\n const sessionKey = typeof c.req.query('sessionKey') === 'string' ? c.req.query('sessionKey')!.trim() : '';\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n sessionKey,\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { code: 'WORKSPACE_RESOLUTION_FAILED', message: ws.message } }, 400);\n }\n\n const workspaceRoot = ws.root;\n const classifierCtx = { ...buildFilePathClassifierContext(service.currentConfig, sessionKey), workspaceRoot };\n const displayName = displayNameForPath(rawPath);\n const { candidate, invalid } = await resolveFileReferenceCandidate(rawPath, workspaceRoot, classifierCtx);\n\n if (!candidate || invalid) {\n return c.json({\n ok: true,\n payload: {\n inputPath: rawPath,\n displayName,\n scope: 'invalid' satisfies FileReferenceScope,\n exists: false,\n capabilities: [] as FileReferenceCapability[],\n errorCode: 'INVALID_PATH',\n },\n });\n }\n\n let st: Awaited<ReturnType<typeof stat>> | null = null;\n try {\n st = await stat(candidate);\n } catch {\n st = null;\n }\n\n if (!st) {\n // Always include the resolved candidate so the UI's \"Copy path\" yields\n // something actionable (\"I looked here, no file\"). Without this, bare\n // workspace-relative mentions fall back to the `rel:<path>` UI sentinel.\n return c.json({\n ok: true,\n payload: {\n inputPath: rawPath,\n displayName,\n scope: 'missing' satisfies FileReferenceScope,\n exists: false,\n absolutePath: candidate,\n capabilities: fileReferenceCapabilities('missing', false),\n errorCode: 'FILE_NOT_FOUND',\n },\n });\n }\n\n const classified = classifyFileLocation(candidate, classifierCtx);\n const { scope, locationKind, manageRoute } = classified;\n const inWorkspace = scope === 'workspace';\n const isDirectory = st.isDirectory();\n const capabilities = fileReferenceCapabilities(scope, isDirectory, locationKind);\n const ref = fileReferenceRegistry.register({\n absolutePath: candidate,\n sessionKey: sessionKey || undefined,\n scope,\n locationKind,\n capabilities,\n });\n\n return c.json({\n ok: true,\n payload: {\n fileRefId: ref.id,\n inputPath: rawPath,\n displayName,\n scope,\n locationKind,\n manageRoute,\n exists: true,\n isDirectory,\n absolutePath: candidate,\n workspaceRelativePath: inWorkspace ? toWorkspaceRelativePosix(workspaceRoot, candidate) : undefined,\n capabilities,\n mtimeMs: st.mtimeMs,\n },\n });\n });\n\n authenticated.post('/api/workspace/file-ref/:id/resolve-action', async (c) => {\n const id = c.req.param('id')?.trim() ?? '';\n if (!id) {\n return c.json({ ok: false, error: { code: 'INVALID_FILE_REF', message: 'Missing file reference' } }, 400);\n }\n\n const ref = fileReferenceRegistry.resolve(id);\n if (!ref) {\n return c.json({ ok: false, error: { code: 'FILE_REF_EXPIRED', message: 'File reference expired' } }, 404);\n }\n\n const sessionKey = typeof c.req.query('sessionKey') === 'string' ? c.req.query('sessionKey')!.trim() : '';\n if (!fileRefSessionKeysMatch(ref.sessionKey, sessionKey)) {\n return c.json({ ok: false, error: { code: 'FILE_REF_FORBIDDEN', message: 'File reference forbidden' } }, 403);\n }\n\n const body = (await c.req.json().catch(() => ({}))) as { action?: unknown };\n const action = body.action;\n if (!isFileReferenceAction(action)) {\n return c.json({ ok: false, error: { code: 'INVALID_ACTION', message: 'Invalid action' } }, 400);\n }\n if (!ref.capabilities.includes(action)) {\n return c.json({ ok: false, error: { code: 'ACTION_NOT_ALLOWED', message: 'Action not allowed' } }, 403);\n }\n\n let st: Awaited<ReturnType<typeof stat>>;\n try {\n st = await stat(ref.absolutePath);\n } catch {\n return c.json({ ok: false, error: { code: 'FILE_NOT_FOUND', message: 'File not found' } }, 404);\n }\n\n return c.json({\n ok: true,\n payload: {\n absolutePath: ref.absolutePath,\n isDirectory: st.isDirectory(),\n },\n });\n });\n\n authenticated.post('/api/workspace/import-file-ref/:id', async (c) => {\n const id = c.req.param('id')?.trim() ?? '';\n if (!id) {\n return c.json({ ok: false, error: { code: 'INVALID_FILE_REF', message: 'Missing file reference' } }, 400);\n }\n\n const ref = fileReferenceRegistry.resolve(id);\n if (!ref) {\n return c.json({ ok: false, error: { code: 'FILE_REF_EXPIRED', message: 'File reference expired' } }, 404);\n }\n\n const sessionKey = typeof c.req.query('sessionKey') === 'string' ? c.req.query('sessionKey')!.trim() : '';\n if (!fileRefSessionKeysMatch(ref.sessionKey, sessionKey)) {\n return c.json({ ok: false, error: { code: 'FILE_REF_FORBIDDEN', message: 'File reference forbidden' } }, 403);\n }\n\n if (!ref.capabilities.includes('importToWorkspace')) {\n return c.json({ ok: false, error: { code: 'IMPORT_NOT_ALLOWED', message: 'Import not allowed for this file' } }, 403);\n }\n\n let sourceStat: Awaited<ReturnType<typeof stat>>;\n try {\n sourceStat = await stat(ref.absolutePath);\n } catch {\n fileReferenceRegistry.expireById(id);\n return c.json({ ok: false, error: { code: 'SOURCE_NOT_FOUND', message: 'Source file no longer exists' } }, 404);\n }\n if (!sourceStat.isFile()) {\n return c.json({ ok: false, error: { code: 'SOURCE_NOT_FILE', message: 'Source is not a regular file' } }, 400);\n }\n\n const importCfg = resolveWorkspaceImportConfig(service.currentConfig);\n if (sourceStat.size > importCfg.maxBytes) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'SOURCE_TOO_LARGE',\n message: `Source exceeds maximum import size (${importCfg.maxBytes} bytes)`,\n },\n },\n 413,\n );\n }\n\n const ws = await resolveEditorWorkspaceRootAsync(service, service.currentConfig, sessionKey, undefined);\n if (ws.ok === false) {\n return c.json({ ok: false, error: { code: 'WORKSPACE_RESOLUTION_FAILED', message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n\n let body: { destination?: unknown; onConflict?: unknown };\n try {\n body = (await c.req.json().catch(() => ({}))) as typeof body;\n } catch {\n body = {};\n }\n const requestedDestRaw = typeof body.destination === 'string' ? body.destination.trim() : '';\n const onConflictRaw = typeof body.onConflict === 'string' ? body.onConflict : 'rename';\n if (onConflictRaw !== 'rename' && onConflictRaw !== 'overwrite' && onConflictRaw !== 'error') {\n return c.json({ ok: false, error: { code: 'INVALID_CONFLICT_MODE', message: 'Invalid onConflict value' } }, 400);\n }\n const onConflict = onConflictRaw as 'rename' | 'overwrite' | 'error';\n if (onConflict === 'overwrite' && !importCfg.allowOverwrite) {\n return c.json({ ok: false, error: { code: 'OVERWRITE_DISABLED', message: 'Overwrite is disabled by config' } }, 403);\n }\n\n const sourceBasename = sanitizeImportBasename(basename(ref.absolutePath)) || 'imported-file';\n let requestedRel: string;\n if (!requestedDestRaw) {\n requestedRel = `${importCfg.targetDir}/${sourceBasename}`;\n } else {\n const trimmedDest = requestedDestRaw.replace(/\\\\/g, '/');\n // Path ending with `/` is treated as a directory; append source basename.\n requestedRel = trimmedDest.endsWith('/') ? `${trimmedDest}${sourceBasename}` : trimmedDest;\n }\n\n let initialDestAbs = resolveWorkspaceSafePath(workspaceRoot, requestedRel);\n if (!initialDestAbs) {\n return c.json({ ok: false, error: { code: 'INVALID_DESTINATION', message: 'Invalid destination path' } }, 400);\n }\n\n // Sandbox: blocks `.xopc/xopc.json`, `.env*`, etc.; canonical symlink resolution included.\n const writePolicy = validateWritePath(initialDestAbs, workspaceRoot);\n if (!writePolicy.allowed) {\n return c.json({ ok: false, error: { code: 'DESTINATION_BLOCKED', message: writePolicy.reason ?? 'Destination blocked' } }, 403);\n }\n\n if (resolve(ref.absolutePath) === resolve(initialDestAbs)) {\n return c.json({ ok: false, error: { code: 'SAME_LOCATION', message: 'Destination is the same as source' } }, 400);\n }\n\n const destDir = dirname(initialDestAbs);\n try {\n await mkdir(destDir, { recursive: true });\n } catch (err) {\n log.warn({ err, destDir }, 'Failed to create import destination directory');\n return c.json({ ok: false, error: { code: 'IMPORT_FAILED', message: 'Failed to prepare destination' } }, 500);\n }\n\n // Stage source into a hidden tmp file inside the destination directory so we\n // can atomically `link` (rename strategy) or `rename` (overwrite) to land it.\n const tmpName = `.${basename(initialDestAbs)}.import-${randomUUID()}.tmp`;\n const tmpAbs = join(destDir, tmpName);\n\n const started = Date.now();\n let renamed = false;\n let overwrote = false;\n let finalDestAbs = initialDestAbs;\n\n try {\n try {\n await copyFile(ref.absolutePath, tmpAbs, fsConstants.COPYFILE_FICLONE);\n } catch (err) {\n await unlink(tmpAbs).catch(() => {});\n log.warn({ err, source: ref.absolutePath, tmpAbs }, 'Failed to copy source into staging tmp');\n return c.json({ ok: false, error: { code: 'IMPORT_FAILED', message: 'Failed to copy source file' } }, 500);\n }\n\n if (onConflict === 'overwrite') {\n // Snapshot pre-rename existence for telemetry; the actual overwrite is unconditional.\n overwrote = await stat(initialDestAbs).then(() => true).catch(() => false);\n try {\n await rename(tmpAbs, initialDestAbs);\n } catch (err) {\n await unlink(tmpAbs).catch(() => {});\n log.warn({ err, target: initialDestAbs }, 'Atomic rename failed');\n return c.json({ ok: false, error: { code: 'IMPORT_FAILED', message: 'Failed to finalize import' } }, 500);\n }\n } else if (onConflict === 'error') {\n const exists = await stat(initialDestAbs).then(() => true).catch(() => false);\n if (exists) {\n await unlink(tmpAbs).catch(() => {});\n return c.json({ ok: false, error: { code: 'DESTINATION_EXISTS', message: 'Destination already exists' } }, 409);\n }\n try {\n await link(tmpAbs, initialDestAbs);\n await unlink(tmpAbs).catch(() => {});\n } catch (err) {\n await unlink(tmpAbs).catch(() => {});\n log.warn({ err, target: initialDestAbs }, 'Hard link to destination failed');\n return c.json({ ok: false, error: { code: 'IMPORT_FAILED', message: 'Failed to finalize import' } }, 500);\n }\n } else {\n // rename: race-safe loop using O_EXCL semantics of `link`.\n const picked = await pickAvailableTargetWithLink(tmpAbs, initialDestAbs);\n if (!picked.ok) {\n await unlink(tmpAbs).catch(() => {});\n log.warn({ target: initialDestAbs, attempts: picked.attempts }, 'Failed to find free import target name');\n return c.json({ ok: false, error: { code: 'IMPORT_FAILED', message: 'No free destination name available' } }, 500);\n }\n await unlink(tmpAbs).catch(() => {});\n finalDestAbs = picked.path;\n renamed = picked.path !== initialDestAbs;\n }\n } catch (err) {\n await unlink(tmpAbs).catch(() => {});\n log.error({ err }, 'Import file unexpected failure');\n return c.json({ ok: false, error: { code: 'IMPORT_FAILED', message: 'Import failed' } }, 500);\n }\n\n let finalMtime: number;\n try {\n finalMtime = (await stat(finalDestAbs)).mtimeMs;\n } catch {\n finalMtime = Date.now();\n }\n\n const workspaceRel = toWorkspaceRelativePosix(workspaceRoot, finalDestAbs);\n\n fileReferenceRegistry.expireById(id);\n const newRef = fileReferenceRegistry.register({\n absolutePath: finalDestAbs,\n sessionKey: sessionKey || undefined,\n scope: 'workspace',\n capabilities: fileReferenceCapabilities('workspace', false),\n });\n\n const sourceScope = ref.scope;\n const sourceLocationKind = ref.locationKind;\n\n service.emit('workspace.file-imported', {\n sessionKey: sessionKey || undefined,\n workspaceRelativePath: workspaceRel,\n absolutePath: finalDestAbs,\n bytes: sourceStat.size,\n sourceScope,\n sourceLocationKind,\n });\n\n log.info(\n {\n sessionKey,\n fileRefId: id,\n sourceAbsolutePath: ref.absolutePath,\n sourceScope,\n sourceLocationKind,\n destWorkspaceRelativePath: workspaceRel,\n bytes: sourceStat.size,\n renamed,\n overwrote,\n durationMs: Date.now() - started,\n },\n 'Workspace file import succeeded',\n );\n\n return c.json({\n ok: true,\n payload: {\n workspaceRelativePath: workspaceRel,\n absolutePath: finalDestAbs,\n bytesCopied: sourceStat.size,\n sourceAbsolutePath: ref.absolutePath,\n sourceScope,\n sourceLocationKind,\n renamed,\n overwrote,\n mtimeMs: finalMtime,\n newFileRefId: newRef.id,\n },\n });\n });\n\n /**\n * Serve a workspace file as raw bytes (e.g. <img> after auth fetch + blob URL).\n * Path is workspace-relative; scope via sessionKey / agentId like other editor routes.\n */\n authenticated.get('/api/workspace/editor/raw', async (c) => {\n const pathRel = typeof c.req.query('path') === 'string' ? c.req.query('path')! : '';\n if (!pathRel.trim()) {\n return c.json({ ok: false, error: { message: 'Missing path' } }, 400);\n }\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n const abs = resolveWorkspaceSafePath(workspaceRoot, pathRel);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Invalid path' } }, 400);\n }\n let st: Awaited<ReturnType<typeof stat>>;\n try {\n st = await stat(abs);\n } catch {\n return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n }\n if (!st.isFile()) {\n return c.json({ ok: false, error: { message: 'Not a file' } }, 400);\n }\n const ext = pathRel.split('.').pop()?.toLowerCase() ?? '';\n const mimeByExt: Record<string, string> = {\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n webp: 'image/webp',\n bmp: 'image/bmp',\n svg: 'image/svg+xml',\n pdf: 'application/pdf',\n docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n txt: 'text/plain',\n md: 'text/markdown',\n json: 'application/json',\n html: 'text/html',\n css: 'text/css',\n js: 'text/javascript',\n ts: 'text/typescript',\n mp3: 'audio/mpeg',\n wav: 'audio/wav',\n ogg: 'audio/ogg',\n webm: 'video/webm',\n mp4: 'video/mp4',\n mov: 'video/quicktime',\n };\n const contentType = mimeByExt[ext] || 'application/octet-stream';\n try {\n const buf = await readFile(abs);\n return new Response(buf, {\n headers: {\n 'Content-Type': contentType,\n 'Cache-Control': 'private, max-age=3600',\n },\n });\n } catch {\n return c.json({ ok: false, error: { message: 'Read failed' } }, 500);\n }\n });\n\n authenticated.put('/api/workspace/editor/write', async (c) => {\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n let body: unknown;\n try {\n body = await c.req.json();\n } catch {\n return c.json({ ok: false, error: { message: 'Invalid JSON' } }, 400);\n }\n const pathRel =\n typeof body === 'object' &&\n body !== null &&\n 'path' in body &&\n typeof (body as { path: unknown }).path === 'string'\n ? (body as { path: string }).path\n : '';\n const content =\n typeof body === 'object' &&\n body !== null &&\n 'content' in body &&\n typeof (body as { content: unknown }).content === 'string'\n ? (body as { content: string }).content\n : '';\n if (!pathRel.trim()) {\n return c.json({ ok: false, error: { message: 'Missing path' } }, 400);\n }\n const abs = resolveWorkspaceSafePath(workspaceRoot, pathRel);\n if (!abs) {\n return c.json({ ok: false, error: { message: 'Invalid path' } }, 400);\n }\n let st: Awaited<ReturnType<typeof stat>> | undefined;\n try {\n st = await stat(abs);\n } catch {\n st = undefined;\n }\n if (st && !st.isFile()) {\n return c.json({ ok: false, error: { message: 'Not a file' } }, 400);\n }\n try {\n await writeFile(abs, content, 'utf-8');\n let mtimeMs: number;\n try {\n mtimeMs = (await stat(abs)).mtimeMs;\n } catch {\n mtimeMs = Date.now();\n }\n return c.json({\n ok: true,\n payload: { path: toWorkspaceRelativePosix(workspaceRoot, abs), mtimeMs },\n });\n } catch (err) {\n log.error({ err, path: abs }, 'workspace editor write failed');\n return c.json({ ok: false, error: { message: 'Write failed' } }, 500);\n }\n });\n\n authenticated.get('/api/workspace/editor/search', async (c) => {\n const q = typeof c.req.query('q') === 'string' ? c.req.query('q')!.trim() : '';\n const dirRel = typeof c.req.query('dir') === 'string' ? c.req.query('dir')! : '';\n if (!q) {\n return c.json({\n ok: true,\n payload: { results: [] as { filePath: string; lineNumber: number; lineContent: string; matchStart: number; matchEnd: number }[] },\n });\n }\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n const workspaceRoot = ws.root;\n const absDir = resolveWorkspaceSafePath(workspaceRoot, dirRel);\n if (!absDir) {\n return c.json({ ok: false, error: { message: 'Invalid path' } }, 400);\n }\n let st: Awaited<ReturnType<typeof stat>>;\n try {\n st = await stat(absDir);\n } catch {\n return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n }\n if (!st.isDirectory()) {\n return c.json({ ok: false, error: { message: 'Not a directory' } }, 400);\n }\n const raw = await runRipgrepInDirectory(q, absDir);\n const results = raw\n .filter((r) => isPathUnderWorkspace(workspaceRoot, r.filePath))\n .map((r) => ({\n ...r,\n filePath: toWorkspaceRelativePosix(workspaceRoot, resolve(r.filePath)),\n }));\n return c.json({ ok: true, payload: { results } });\n });\n\n /** Fuzzy filename / path search over the session workspace (ripgrep `--files` + subsequence scoring). */\n authenticated.get('/api/workspace/editor/files/search', async (c) => {\n const q = typeof c.req.query('q') === 'string' ? c.req.query('q')!.trim() : '';\n const limitRaw = c.req.query('limit');\n const limit = Math.min(\n Math.max(parseInt(typeof limitRaw === 'string' ? limitRaw : '15', 10) || 15, 1),\n FILE_SEARCH_MAX_LIMIT,\n );\n\n const ws = await resolveEditorWorkspaceRootAsync(\n service,\n service.currentConfig,\n c.req.query('sessionKey'),\n c.req.query('agentId'),\n );\n if (ws.ok === false) {\n return c.json({ ok: false, error: { message: ws.message } }, 400);\n }\n\n const entries = await fuzzySearchWorkspaceFiles(ws.root, q, limit);\n return c.json({ ok: true, payload: { entries } });\n });\n}\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
- import { buildSessionKey, init_session_key, parseSessionKey } from "../../routing/session-key.js";
2
- import { getDefaultAgentId, init_resolve_route } from "../../routing/resolve-route.js";
3
1
  import { updateAsyncLogContext } from "../../utils/logger/context.js";
4
2
  import { createLogger } from "../../utils/logger/index.js";
5
3
  import { init_logger } from "../../utils/logger.js";
4
+ import { buildSessionKey, init_session_key, parseSessionKey } from "../../routing/session-key.js";
5
+ import { getDefaultAgentId, init_resolve_route } from "../../routing/resolve-route.js";
6
6
  import { MAX_WEBCHAT_ATTACHMENT_FILE_BYTES } from "../chat-limits.js";
7
7
  import { stringifySSEData } from "./sse-json.js";
8
8
  import { randomUUID } from "node:crypto";
@@ -12,4 +12,4 @@ export { isLoopbackHost, isAllInterfacesHost, buildDefaultCorsOrigins, resolveEf
12
12
  export { resolveGatewayListenHost, resolveGatewayListenPlan, } from './listen.js';
13
13
  export { resolveGatewayBindMode, resolveGatewayBindHost, resolveGatewayBindHostSync, resolveGatewayEffectiveHost, defaultGatewayBindMode, isContainerEnvironment, } from '../config/gateway-bind.js';
14
14
  export { isSecureWebSocketUrl, assertSecureWebSocketUrl, assertSecureGatewayHttpUrl, isInsecurePrivateWsAllowed, } from './ws-security.js';
15
- export { isGatewayStrictSecurityEnabled, buildBrowserOriginRateLimitKey, resolveAuthRateLimitTracking, resetAuthRateLimitersForTests, } from './auth-rate-limit.js';
15
+ export { isGatewayStrictSecurityEnabled, buckets } from './rate-limit/index.js';
@@ -1,8 +1,10 @@
1
1
  import { buildDefaultCorsOrigins, isAllInterfacesHost, isLoopbackHost, resolveEffectiveGatewayPort, resolveGatewayServiceListenPort } from "./host.js";
2
2
  import { defaultGatewayBindMode, isContainerEnvironment, resolveGatewayBindHost, resolveGatewayBindHostSync, resolveGatewayBindMode, resolveGatewayEffectiveHost } from "../config/gateway-bind.js";
3
- import { buildBrowserOriginRateLimitKey, isGatewayStrictSecurityEnabled, resetAuthRateLimitersForTests, resolveAuthRateLimitTracking } from "./auth-rate-limit.js";
4
3
  import { assertGatewayAuthConfigured, extractToken, resolveGatewayAuth, validateToken } from "./auth.js";
5
4
  import { resolveGatewayListenHost, resolveGatewayListenPlan } from "./listen.js";
5
+ import { buckets } from "./rate-limit/buckets.js";
6
+ import { isGatewayStrictSecurityEnabled } from "./rate-limit/env-flags.js";
7
+ import "./rate-limit/index.js";
6
8
  import { assertGatewayRuntimeConfig } from "./runtime-config.js";
7
9
  import { restartGatewayProcessWithFreshPid } from "./respawn.js";
8
10
  import { GatewayService } from "./service.js";
@@ -15,4 +17,4 @@ import { checkPortAvailable, forceFreePortAndWait, listPortListeners, parseLsofO
15
17
  import { apiError, apiOk } from "./protocol.js";
16
18
  import "./hono/index.js";
17
19
  import { assertSecureGatewayHttpUrl, assertSecureWebSocketUrl, isInsecurePrivateWsAllowed, isSecureWebSocketUrl } from "./ws-security.js";
18
- export { GatewayLockError, GatewayServer, GatewayService, acquireGatewayLock, apiError, apiOk, assertGatewayAuthConfigured, assertGatewayRuntimeConfig, assertSecureGatewayHttpUrl, assertSecureWebSocketUrl, buildBrowserOriginRateLimitKey, buildDefaultCorsOrigins, checkPortAvailable, createAgentResumeHandler, createAgentSSEHandler, createEventsSSEHandler, createHonoApp, createSendHandler, defaultGatewayBindMode, extractToken, forceFreePortAndWait, isAllInterfacesHost, isContainerEnvironment, isGatewayStrictSecurityEnabled, isInsecurePrivateWsAllowed, isLoopbackHost, isSecureWebSocketUrl, listPortListeners, parseLsofOutput, resetAuthRateLimitersForTests, resolveAuthRateLimitTracking, resolveEffectiveGatewayPort, resolveGatewayAuth, resolveGatewayBindHost, resolveGatewayBindHostSync, resolveGatewayBindMode, resolveGatewayEffectiveHost, resolveGatewayListenHost, resolveGatewayListenPlan, resolveGatewayServiceListenPort, restartGatewayProcessWithFreshPid, runGatewayLoop, validateToken };
20
+ export { GatewayLockError, GatewayServer, GatewayService, acquireGatewayLock, apiError, apiOk, assertGatewayAuthConfigured, assertGatewayRuntimeConfig, assertSecureGatewayHttpUrl, assertSecureWebSocketUrl, buckets, buildDefaultCorsOrigins, checkPortAvailable, createAgentResumeHandler, createAgentSSEHandler, createEventsSSEHandler, createHonoApp, createSendHandler, defaultGatewayBindMode, extractToken, forceFreePortAndWait, isAllInterfacesHost, isContainerEnvironment, isGatewayStrictSecurityEnabled, isInsecurePrivateWsAllowed, isLoopbackHost, isSecureWebSocketUrl, listPortListeners, parseLsofOutput, resolveEffectiveGatewayPort, resolveGatewayAuth, resolveGatewayBindHost, resolveGatewayBindHostSync, resolveGatewayBindMode, resolveGatewayEffectiveHost, resolveGatewayListenHost, resolveGatewayListenPlan, resolveGatewayServiceListenPort, restartGatewayProcessWithFreshPid, runGatewayLoop, validateToken };
@@ -1,9 +1,9 @@
1
- import path from "node:path";
2
- import fsSync from "node:fs";
3
1
  import { createHash } from "node:crypto";
2
+ import fsSync from "node:fs";
4
3
  import fs from "node:fs/promises";
5
- import net from "node:net";
4
+ import path from "node:path";
6
5
  import { homedir } from "os";
6
+ import net from "node:net";
7
7
  //#region src/gateway/lock.ts
8
8
  /**
9
9
  * Gateway Lock - Prevents multiple gateway instances from running simultaneously
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Auth-failure rate-limit policy: how to derive a tracking key from a request,
3
+ * and whether a given client should be exempted before counting.
4
+ *
5
+ * The {@link FailureLimiter} primitive is intentionally policy-free — this
6
+ * module is the only place that knows about IPs, browser origins, and the
7
+ * loopback exemption. Adding a new exemption rule (e.g. private CIDRs) means
8
+ * editing this file alone; the limiter primitive does not change.
9
+ */
10
+ export type AuthRateLimitPolicyConfig = {
11
+ enabled: boolean;
12
+ exemptLoopback: boolean;
13
+ };
14
+ /**
15
+ * `key` is `''` when `exempt` is true. We use a flat shape rather than a
16
+ * tagged union because the project's tsconfig has `strict: false`, which
17
+ * disables boolean-discriminator narrowing.
18
+ */
19
+ export type AuthRateLimitTracking = {
20
+ exempt: boolean;
21
+ key: string;
22
+ };
23
+ /**
24
+ * Decide whether to track this client and, if so, what key identifies them.
25
+ * Browser clients are tracked separately (origin + IP) so a rogue `<iframe>`
26
+ * cannot piggyback on a CLI client's bucket and DoS it.
27
+ */
28
+ export declare function resolveAuthTracking(params: {
29
+ clientIp: string;
30
+ origin?: string | null;
31
+ cfg: AuthRateLimitPolicyConfig;
32
+ }): AuthRateLimitTracking;
33
+ /** Browser-tracking key format. Stable across reloads — survives state replay. */
34
+ export declare function buildBrowserOriginKey(origin: string, clientIp: string): string;
@@ -0,0 +1,49 @@
1
+ import { isLoopbackClientIp, isLoopbackEmbeddedBrowserClient } from "../security/loopback.js";
2
+ //#region src/gateway/rate-limit/auth-policy.ts
3
+ /**
4
+ * Auth-failure rate-limit policy: how to derive a tracking key from a request,
5
+ * and whether a given client should be exempted before counting.
6
+ *
7
+ * The {@link FailureLimiter} primitive is intentionally policy-free — this
8
+ * module is the only place that knows about IPs, browser origins, and the
9
+ * loopback exemption. Adding a new exemption rule (e.g. private CIDRs) means
10
+ * editing this file alone; the limiter primitive does not change.
11
+ */
12
+ /**
13
+ * Decide whether to track this client and, if so, what key identifies them.
14
+ * Browser clients are tracked separately (origin + IP) so a rogue `<iframe>`
15
+ * cannot piggyback on a CLI client's bucket and DoS it.
16
+ */
17
+ function resolveAuthTracking(params) {
18
+ if (!params.cfg.enabled) return {
19
+ exempt: true,
20
+ key: ""
21
+ };
22
+ const origin = params.origin?.trim();
23
+ if (origin) {
24
+ if (isLoopbackEmbeddedBrowserClient(origin, params.clientIp) && params.cfg.exemptLoopback) return {
25
+ exempt: true,
26
+ key: ""
27
+ };
28
+ return {
29
+ exempt: false,
30
+ key: buildBrowserOriginKey(origin, params.clientIp)
31
+ };
32
+ }
33
+ if (params.cfg.exemptLoopback && isLoopbackClientIp(params.clientIp)) return {
34
+ exempt: true,
35
+ key: ""
36
+ };
37
+ return {
38
+ exempt: false,
39
+ key: params.clientIp
40
+ };
41
+ }
42
+ /** Browser-tracking key format. Stable across reloads — survives state replay. */
43
+ function buildBrowserOriginKey(origin, clientIp) {
44
+ return `browser-origin:${origin.trim().toLowerCase()}|${clientIp.trim()}`;
45
+ }
46
+ //#endregion
47
+ export { buildBrowserOriginKey, resolveAuthTracking };
48
+
49
+ //# sourceMappingURL=auth-policy.js.map