@xopcai/xopc 0.0.81 → 0.0.83

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 (709) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -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-CrpYTHJS.js +222 -0
  23. package/dist/gateway/static/root/assets/{apps-page-Ci17oA_o.js → apps-page-1mcKh5Rh.js} +1 -1
  24. package/dist/gateway/static/root/assets/channels-settings-zd6QNKPx.js +1 -0
  25. package/dist/gateway/static/root/assets/{channels-status-swr-CUU3faST.js → channels-status-swr-uRAuhiUo.js} +1 -1
  26. package/dist/gateway/static/root/assets/{cron-api-BVQ2n75R.js → cron-api-O2Q_ruV6.js} +1 -1
  27. package/dist/gateway/static/root/assets/{cron-page-x582Y6D5.js → cron-page-By09AQD-.js} +1 -1
  28. package/dist/gateway/static/root/assets/{dist-XT96cQdR.js → dist-BpQxde0t.js} +1 -1
  29. package/dist/gateway/static/root/assets/{extension-debug-page-Czzfrtt5.js → extension-debug-page-CY27wj_p.js} +1 -1
  30. package/dist/gateway/static/root/assets/{extension-page-B_c5UIqX.js → extension-page-C-Ed5ZmP.js} +1 -1
  31. package/dist/gateway/static/root/assets/{extension-settings-page-Ckvjgw0_.js → extension-settings-page-raLux7E7.js} +1 -1
  32. package/dist/gateway/static/root/assets/fetch-2iRFmd3n.js +3 -0
  33. package/dist/gateway/static/root/assets/{field-primitives-DQpT8iVa.js → field-primitives-fa_hiQcX.js} +1 -1
  34. package/dist/gateway/static/root/assets/{heartbeat-config-api-DKqOuQ0V.js → heartbeat-config-api-BVl5VHvL.js} +1 -1
  35. package/dist/gateway/static/root/assets/index-BuFldCsB.css +1 -0
  36. package/dist/gateway/static/root/assets/{index-Bq3Lg4bG.js → index-Y-iqo-gL.js} +95 -86
  37. package/dist/gateway/static/root/assets/{logs-page-B3CwJNBq.js → logs-page-BdH2n7ZW.js} +1 -1
  38. package/dist/gateway/static/root/assets/sessions-page-Vpchzdp-.js +1 -0
  39. package/dist/gateway/static/root/assets/{settings-form-section-CjjEpVYM.js → settings-form-section-Kk1yAGBl.js} +1 -1
  40. package/dist/gateway/static/root/assets/settings-page-KBm0u6Dz.js +3 -0
  41. package/dist/gateway/static/root/assets/skills-page-BjeXXaOn.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-DQehHvlm.js → utils-DpTxN4AF.js} +1 -1
  44. package/dist/gateway/static/root/assets/voice-api-key-field-CwO8Cf01.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 +133 -167
  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/commands/agent/stream-renderer.js +1 -1
  271. package/dist/src/cli/commands/agent/stream-renderer.js.map +1 -1
  272. package/dist/src/cli/commands/agent.js +4 -4
  273. package/dist/src/cli/commands/agent.js.map +1 -1
  274. package/dist/src/cli/commands/browser-cli-helpers.js +2 -1
  275. package/dist/src/cli/commands/browser-cli-helpers.js.map +1 -1
  276. package/dist/src/cli/commands/doctor/checks/config-health.js +1 -1
  277. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  278. package/dist/src/cli/commands/doctor/checks/session-integrity.js +1 -1
  279. package/dist/src/cli/commands/doctor/checks/state-integrity.js +1 -1
  280. package/dist/src/cli/commands/doctor/checks/workspace-status.js +1 -1
  281. package/dist/src/cli/commands/extension-dev.js +2 -2
  282. package/dist/src/cli/commands/extension-dev.js.map +1 -1
  283. package/dist/src/cli/commands/extension-marketplace.js +2 -2
  284. package/dist/src/cli/commands/extension-marketplace.js.map +1 -1
  285. package/dist/src/cli/commands/extension-pack.js +1 -1
  286. package/dist/src/cli/commands/gateway/call.js +1 -1
  287. package/dist/src/cli/commands/gateway/call.js.map +1 -1
  288. package/dist/src/cli/commands/gateway/health.js +1 -1
  289. package/dist/src/cli/commands/gateway/health.js.map +1 -1
  290. package/dist/src/cli/commands/gateway/lifecycle-core.d.ts +31 -12
  291. package/dist/src/cli/commands/gateway/lifecycle-core.js +167 -116
  292. package/dist/src/cli/commands/gateway/lifecycle-core.js.map +1 -1
  293. package/dist/src/cli/commands/gateway/lifecycle.d.ts +11 -0
  294. package/dist/src/cli/commands/gateway/lifecycle.js +102 -0
  295. package/dist/src/cli/commands/gateway/lifecycle.js.map +1 -0
  296. package/dist/src/cli/commands/gateway/logs.js +1 -1
  297. package/dist/src/cli/commands/gateway/logs.js.map +1 -1
  298. package/dist/src/cli/commands/gateway/probe.js +1 -1
  299. package/dist/src/cli/commands/gateway/probe.js.map +1 -1
  300. package/dist/src/cli/commands/gateway/restart-health.d.ts +12 -0
  301. package/dist/src/cli/commands/gateway/restart-health.js +45 -1
  302. package/dist/src/cli/commands/gateway/restart-health.js.map +1 -1
  303. package/dist/src/cli/commands/gateway/restart.js +3 -3
  304. package/dist/src/cli/commands/gateway/restart.js.map +1 -1
  305. package/dist/src/cli/commands/gateway/run-foreground.d.ts +0 -1
  306. package/dist/src/cli/commands/gateway/run-foreground.js +0 -35
  307. package/dist/src/cli/commands/gateway/run-foreground.js.map +1 -1
  308. package/dist/src/cli/commands/gateway/service.js +1 -1
  309. package/dist/src/cli/commands/gateway/service.js.map +1 -1
  310. package/dist/src/cli/commands/gateway/shared.d.ts +3 -0
  311. package/dist/src/cli/commands/gateway/shared.js +54 -0
  312. package/dist/src/cli/commands/gateway/shared.js.map +1 -0
  313. package/dist/src/cli/commands/gateway/status.js +1 -1
  314. package/dist/src/cli/commands/gateway/status.js.map +1 -1
  315. package/dist/src/cli/commands/gateway/stop.js +2 -2
  316. package/dist/src/cli/commands/gateway/stop.js.map +1 -1
  317. package/dist/src/cli/commands/gateway/token.js +1 -1
  318. package/dist/src/cli/commands/gateway/token.js.map +1 -1
  319. package/dist/src/cli/commands/gateway.js +5 -5
  320. package/dist/src/cli/commands/gateway.js.map +1 -1
  321. package/dist/src/cli/commands/image.js +2 -2
  322. package/dist/src/cli/commands/image.js.map +1 -1
  323. package/dist/src/cli/commands/init.js +4 -4
  324. package/dist/src/cli/commands/models.js +1 -1
  325. package/dist/src/cli/commands/models.js.map +1 -1
  326. package/dist/src/cli/commands/onboard/gateway.d.ts +0 -8
  327. package/dist/src/cli/commands/onboard/gateway.js +48 -49
  328. package/dist/src/cli/commands/onboard/gateway.js.map +1 -1
  329. package/dist/src/cli/commands/onboard.js +9 -64
  330. package/dist/src/cli/commands/onboard.js.map +1 -1
  331. package/dist/src/cli/commands/session/utils.js +1 -1
  332. package/dist/src/cli/commands/session/utils.js.map +1 -1
  333. package/dist/src/cli/commands/skills.js +1 -1
  334. package/dist/src/cli/commands/tailscale.js +1 -1
  335. package/dist/src/cli/commands/tailscale.js.map +1 -1
  336. package/dist/src/cli/context.d.ts +20 -0
  337. package/dist/src/cli/context.js +23 -0
  338. package/dist/src/cli/context.js.map +1 -0
  339. package/dist/src/cli/extension-cli-register.js +3 -3
  340. package/dist/src/cli/gateway-run-argv.js +1 -4
  341. package/dist/src/cli/gateway-run-argv.js.map +1 -1
  342. package/dist/src/cli/gateway-run-fast-path.js +1 -1
  343. package/dist/src/cli/gateway-run-fast-path.js.map +1 -1
  344. package/dist/src/cli/index.d.ts +1 -7
  345. package/dist/src/cli/index.js +4 -6
  346. package/dist/src/cli/index.js.map +1 -1
  347. package/dist/src/cli/utils/init-workspace-core.js +2 -2
  348. package/dist/src/config/commands.flags.d.ts +3 -0
  349. package/dist/src/config/commands.flags.js +11 -0
  350. package/dist/src/config/commands.flags.js.map +1 -0
  351. package/dist/src/config/index.d.ts +1 -0
  352. package/dist/src/config/index.js +6 -5
  353. package/dist/src/config/index.js.map +1 -1
  354. package/dist/src/config/loader.js +2 -2
  355. package/dist/src/config/models-json.js +2 -2
  356. package/dist/src/config/profile.js +2 -2
  357. package/dist/src/config/schema.d.ts +11 -4
  358. package/dist/src/config/schema.js +13 -12
  359. package/dist/src/config/schema.js.map +1 -1
  360. package/dist/src/config/workspace-path-helpers.d.ts +15 -0
  361. package/dist/src/config/workspace-path-helpers.js +14 -0
  362. package/dist/src/config/workspace-path-helpers.js.map +1 -0
  363. package/dist/src/cron/executor.js +4 -4
  364. package/dist/src/cron/executor.js.map +1 -1
  365. package/dist/src/cron/persistence.js +1 -1
  366. package/dist/src/cron/run-log-store.js +1 -1
  367. package/dist/src/daemon/index.d.ts +0 -1
  368. package/dist/src/daemon/index.js +1 -2
  369. package/dist/src/daemon/install-plan.js +3 -2
  370. package/dist/src/daemon/install-plan.js.map +1 -1
  371. package/dist/src/daemon/launchd.js +2 -2
  372. package/dist/src/daemon/systemd.js +2 -2
  373. package/dist/src/daemon/types.d.ts +0 -6
  374. package/dist/src/extensions/api.d.ts +1 -1
  375. package/dist/src/extensions/api.js +2 -2
  376. package/dist/src/extensions/api.js.map +1 -1
  377. package/dist/src/extensions/bundle-mcp.js +1 -1
  378. package/dist/src/extensions/discover-extensions.js +1 -1
  379. package/dist/src/extensions/extension-registry-impl.d.ts +51 -0
  380. package/dist/src/extensions/extension-registry-impl.js +117 -0
  381. package/dist/src/extensions/extension-registry-impl.js.map +1 -0
  382. package/dist/src/extensions/health.js +1 -1
  383. package/dist/src/extensions/index.js +3 -2
  384. package/dist/src/extensions/loader.d.ts +3 -43
  385. package/dist/src/extensions/loader.js +3 -110
  386. package/dist/src/extensions/loader.js.map +1 -1
  387. package/dist/src/extensions/lockfile.js +2 -2
  388. package/dist/src/extensions/sdk/index.js +2 -1
  389. package/dist/src/extensions/sdk/index.js.map +1 -1
  390. package/dist/src/extensions/types/events.d.ts +7 -1
  391. package/dist/src/gateway/agents-admin.js +2 -2
  392. package/dist/src/gateway/file-path-classifier.js +2 -2
  393. package/dist/src/gateway/heartbeat/service.js +2 -2
  394. package/dist/src/gateway/heartbeat/service.js.map +1 -1
  395. package/dist/src/gateway/hono/app.js +40 -37
  396. package/dist/src/gateway/hono/app.js.map +1 -1
  397. package/dist/src/gateway/hono/lib/extension-store.js +1 -1
  398. package/dist/src/gateway/hono/lib/static-ui.js +2 -2
  399. package/dist/src/gateway/hono/middleware/auth.d.ts +5 -14
  400. package/dist/src/gateway/hono/middleware/auth.js +92 -105
  401. package/dist/src/gateway/hono/middleware/auth.js.map +1 -1
  402. package/dist/src/gateway/hono/middleware/logger.d.ts +5 -1
  403. package/dist/src/gateway/hono/middleware/logger.js +41 -5
  404. package/dist/src/gateway/hono/middleware/logger.js.map +1 -1
  405. package/dist/src/gateway/hono/middleware/strict-rate-limit.d.ts +14 -0
  406. package/dist/src/gateway/hono/middleware/strict-rate-limit.js +62 -0
  407. package/dist/src/gateway/hono/middleware/strict-rate-limit.js.map +1 -0
  408. package/dist/src/gateway/hono/oauth.js +1 -1
  409. package/dist/src/gateway/hono/routes/auth-registry-extensions.js +4 -4
  410. package/dist/src/gateway/hono/routes/auth-registry-extensions.js.map +1 -1
  411. package/dist/src/gateway/hono/routes/browser.d.ts +20 -0
  412. package/dist/src/gateway/hono/routes/browser.js +626 -0
  413. package/dist/src/gateway/hono/routes/browser.js.map +1 -0
  414. package/dist/src/gateway/hono/routes/commands-skills.js +13 -13
  415. package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
  416. package/dist/src/gateway/hono/routes/config-patch/agents.d.ts +18 -0
  417. package/dist/src/gateway/hono/routes/config-patch/agents.js +418 -0
  418. package/dist/src/gateway/hono/routes/config-patch/agents.js.map +1 -0
  419. package/dist/src/gateway/hono/routes/config-patch/channels.d.ts +12 -0
  420. package/dist/src/gateway/hono/routes/config-patch/channels.js +186 -0
  421. package/dist/src/gateway/hono/routes/config-patch/channels.js.map +1 -0
  422. package/dist/src/gateway/hono/routes/config-patch/gateway.d.ts +18 -0
  423. package/dist/src/gateway/hono/routes/config-patch/gateway.js +264 -0
  424. package/dist/src/gateway/hono/routes/config-patch/gateway.js.map +1 -0
  425. package/dist/src/gateway/hono/routes/config-patch/index.d.ts +9 -0
  426. package/dist/src/gateway/hono/routes/config-patch/index.js +6 -0
  427. package/dist/src/gateway/hono/routes/config-patch/misc.d.ts +23 -0
  428. package/dist/src/gateway/hono/routes/config-patch/misc.js +139 -0
  429. package/dist/src/gateway/hono/routes/config-patch/misc.js.map +1 -0
  430. package/dist/src/gateway/hono/routes/config-patch/result.d.ts +18 -0
  431. package/dist/src/gateway/hono/routes/config-patch/result.js +13 -0
  432. package/dist/src/gateway/hono/routes/config-patch/result.js.map +1 -0
  433. package/dist/src/gateway/hono/routes/config.js +20 -1764
  434. package/dist/src/gateway/hono/routes/config.js.map +1 -1
  435. package/dist/src/gateway/hono/routes/dreaming.js +2 -3
  436. package/dist/src/gateway/hono/routes/dreaming.js.map +1 -1
  437. package/dist/src/gateway/hono/routes/exposure.js +2 -1
  438. package/dist/src/gateway/hono/routes/exposure.js.map +1 -1
  439. package/dist/src/gateway/hono/routes/host-fs.js +1 -1
  440. package/dist/src/gateway/hono/routes/lazy-bundles.js +10 -5
  441. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  442. package/dist/src/gateway/hono/routes/mcp.js +1 -2
  443. package/dist/src/gateway/hono/routes/mcp.js.map +1 -1
  444. package/dist/src/gateway/hono/routes/models.js +1 -1
  445. package/dist/src/gateway/hono/routes/sessions.js +32 -32
  446. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  447. package/dist/src/gateway/hono/routes/shares.js +4 -4
  448. package/dist/src/gateway/hono/routes/shares.js.map +1 -1
  449. package/dist/src/gateway/hono/routes/tunnel.js +1 -1
  450. package/dist/src/gateway/hono/routes/tunnel.js.map +1 -1
  451. package/dist/src/gateway/hono/routes/workspace.js +6 -7
  452. package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
  453. package/dist/src/gateway/hono/sse.d.ts +1 -0
  454. package/dist/src/gateway/hono/sse.js +3 -2
  455. package/dist/src/gateway/hono/sse.js.map +1 -1
  456. package/dist/src/gateway/index.d.ts +1 -1
  457. package/dist/src/gateway/index.js +4 -2
  458. package/dist/src/gateway/lock.js +3 -3
  459. package/dist/src/gateway/rate-limit/auth-policy.d.ts +34 -0
  460. package/dist/src/gateway/rate-limit/auth-policy.js +49 -0
  461. package/dist/src/gateway/rate-limit/auth-policy.js.map +1 -0
  462. package/dist/src/gateway/rate-limit/buckets.d.ts +63 -0
  463. package/dist/src/gateway/rate-limit/buckets.js +143 -0
  464. package/dist/src/gateway/rate-limit/buckets.js.map +1 -0
  465. package/dist/src/gateway/rate-limit/env-flags.d.ts +13 -0
  466. package/dist/src/gateway/rate-limit/env-flags.js +16 -0
  467. package/dist/src/gateway/rate-limit/env-flags.js.map +1 -0
  468. package/dist/src/gateway/rate-limit/index.d.ts +3 -0
  469. package/dist/src/gateway/rate-limit/index.js +4 -0
  470. package/dist/src/gateway/run-loop.d.ts +1 -1
  471. package/dist/src/gateway/run-loop.js +24 -4
  472. package/dist/src/gateway/run-loop.js.map +1 -1
  473. package/dist/src/gateway/runtime-config.js +2 -1
  474. package/dist/src/gateway/runtime-config.js.map +1 -1
  475. package/dist/src/gateway/security/audit.js +2 -1
  476. package/dist/src/gateway/security/audit.js.map +1 -1
  477. package/dist/src/gateway/security/index.d.ts +0 -1
  478. package/dist/src/gateway/security/index.js +1 -2
  479. package/dist/src/gateway/security/loopback.d.ts +13 -0
  480. package/dist/src/gateway/security/loopback.js +45 -0
  481. package/dist/src/gateway/security/loopback.js.map +1 -0
  482. package/dist/src/gateway/service/agent-runner.d.ts +108 -0
  483. package/dist/src/gateway/service/agent-runner.js +184 -0
  484. package/dist/src/gateway/service/agent-runner.js.map +1 -0
  485. package/dist/src/gateway/service/config-coordinator.d.ts +119 -0
  486. package/dist/src/gateway/service/config-coordinator.js +351 -0
  487. package/dist/src/gateway/service/config-coordinator.js.map +1 -0
  488. package/dist/src/gateway/service/marketplace-service.d.ts +85 -0
  489. package/dist/src/gateway/service/marketplace-service.js +239 -0
  490. package/dist/src/gateway/service/marketplace-service.js.map +1 -0
  491. package/dist/src/gateway/service/run-gateway-agent.js +5 -5
  492. package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
  493. package/dist/src/gateway/service/sessions-api.d.ts +125 -0
  494. package/dist/src/gateway/service/sessions-api.js +135 -0
  495. package/dist/src/gateway/service/sessions-api.js.map +1 -0
  496. package/dist/src/gateway/service.d.ts +30 -360
  497. package/dist/src/gateway/service.js +122 -904
  498. package/dist/src/gateway/service.js.map +1 -1
  499. package/dist/src/gateway/workspace-fs-file-list.js +1 -1
  500. package/dist/src/gateway/workspace-heartbeat-path.js +1 -2
  501. package/dist/src/gateway/workspace-heartbeat-path.js.map +1 -1
  502. package/dist/src/heartbeat/index.js +1 -1
  503. package/dist/src/infra/gateway-process-argv.d.ts +4 -0
  504. package/dist/src/infra/gateway-process-argv.js +26 -0
  505. package/dist/src/infra/gateway-process-argv.js.map +1 -0
  506. package/dist/src/infra/gateway-processes.d.ts +5 -0
  507. package/dist/src/infra/gateway-processes.js +65 -0
  508. package/dist/src/infra/gateway-processes.js.map +1 -0
  509. package/dist/src/infra/rate-limit/failure-limiter.d.ts +50 -0
  510. package/dist/src/infra/rate-limit/failure-limiter.js +100 -0
  511. package/dist/src/infra/rate-limit/failure-limiter.js.map +1 -0
  512. package/dist/src/infra/rate-limit/index.d.ts +5 -0
  513. package/dist/src/infra/rate-limit/index.js +3 -0
  514. package/dist/src/infra/rate-limit/keyed-store.d.ts +34 -0
  515. package/dist/src/infra/rate-limit/keyed-store.js +44 -0
  516. package/dist/src/infra/rate-limit/keyed-store.js.map +1 -0
  517. package/dist/src/infra/rate-limit/rate-limiter.d.ts +39 -0
  518. package/dist/src/infra/rate-limit/rate-limiter.js +65 -0
  519. package/dist/src/infra/rate-limit/rate-limiter.js.map +1 -0
  520. package/dist/src/infra/restart.d.ts +21 -0
  521. package/dist/src/infra/restart.js +122 -0
  522. package/dist/src/infra/restart.js.map +1 -0
  523. package/dist/src/infra/update-check.js +1 -1
  524. package/dist/src/infra/update-lock.js +3 -3
  525. package/dist/src/infra/update-runner.js +1 -1
  526. package/dist/src/infra/update-startup.js +2 -2
  527. package/dist/src/infra/write-file-atomic.js +2 -2
  528. package/dist/src/mcp/channel-bridge.d.ts +0 -6
  529. package/dist/src/mcp/channel-bridge.js +1 -5
  530. package/dist/src/mcp/channel-bridge.js.map +1 -1
  531. package/dist/src/media-shared/http/ssrf-guard.js +1 -1
  532. package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
  533. package/dist/src/providers/index.js +2 -2
  534. package/dist/src/providers/model-registry.js +1 -1
  535. package/dist/src/session/config-store.js +2 -2
  536. package/dist/src/session/parity/jsonl-transcript-io.js +2 -2
  537. package/dist/src/session/parity/sessions-json-file-read.d.ts +2 -1
  538. package/dist/src/session/parity/sessions-json-file-read.js.map +1 -1
  539. package/dist/src/session/parity/sessions-json-file.js +1 -1
  540. package/dist/src/session/parity/transcript-file-lock.js +2 -2
  541. package/dist/src/session/parity/transcript-paths.js +1 -1
  542. package/dist/src/session/search-index-cache.js +1 -1
  543. package/dist/src/session/search-index.js +1 -1
  544. package/dist/src/session/session-title.js +1 -1
  545. package/dist/src/session/store.js +5 -5
  546. package/dist/src/share/share-rate-limit.d.ts +10 -2
  547. package/dist/src/share/share-rate-limit.js +39 -27
  548. package/dist/src/share/share-rate-limit.js.map +1 -1
  549. package/dist/src/share/share-store.js +3 -3
  550. package/dist/src/tui/backends/embedded-backend.js +16 -12
  551. package/dist/src/tui/backends/embedded-backend.js.map +1 -1
  552. package/dist/src/tui/clipboard-image.js +2 -2
  553. package/dist/src/tui/extension-host/load-extensions.js +1 -1
  554. package/dist/src/tui/format-tui-hotkeys.js +1 -1
  555. package/dist/src/tui/theme-manager.js +1 -1
  556. package/dist/src/tui/tui-keybindings-file.js +1 -1
  557. package/dist/src/tui/tui-scoped-models.js +1 -1
  558. package/dist/src/tui/tui-settings.js +1 -1
  559. package/dist/src/tui/tui-skills-autocomplete.js +1 -1
  560. package/dist/src/tui/tui.js +1 -2
  561. package/dist/src/tui/tui.js.map +1 -1
  562. package/dist/src/tui/xopc-tui-keybindings.d.ts +0 -1
  563. package/dist/src/tui/xopc-tui-keybindings.js +1 -2
  564. package/dist/src/tui/xopc-tui-keybindings.js.map +1 -1
  565. package/dist/src/tunnel/frpc-binary.js +2 -2
  566. package/dist/src/tunnel/frpc-config.js +1 -1
  567. package/dist/src/tunnel/frpc-extract.js +1 -1
  568. package/dist/src/tunnel/pairing-rate-limit.d.ts +10 -2
  569. package/dist/src/tunnel/pairing-rate-limit.js +19 -15
  570. package/dist/src/tunnel/pairing-rate-limit.js.map +1 -1
  571. package/dist/src/tunnel/tunnel-rate-limit.d.ts +6 -3
  572. package/dist/src/tunnel/tunnel-rate-limit.js +19 -18
  573. package/dist/src/tunnel/tunnel-rate-limit.js.map +1 -1
  574. package/dist/src/tunnel/tunnel-state.js +1 -1
  575. package/dist/src/utils/logger/audit.js +1 -1
  576. package/dist/src/utils/logger/log-store.js +1 -1
  577. package/dist/src/utils/logger/rotation.js +1 -1
  578. package/dist/src/utils/logger/stats.d.ts +1 -1
  579. package/dist/src/voice/tts/audio.js +1 -1
  580. package/dist/src/voice/tts/factory.js +1 -1
  581. package/dist/src/voice/tts/index.js +2 -2
  582. package/dist/src/voice/tts/merge-config.js +1 -1
  583. package/dist/src/voice/tts/providers/edge-speech.js +1 -1
  584. package/dist/src/voice/tts/service.js +1 -1
  585. package/dist/src/voice/tts/service.js.map +1 -1
  586. package/dist/src/voice/tts/speak-core.js +1 -1
  587. package/package.json +10 -5
  588. package/dist/gateway/static/root/assets/agents-DOONGaKz.js +0 -222
  589. package/dist/gateway/static/root/assets/channels-settings-CARdL-ys.js +0 -1
  590. package/dist/gateway/static/root/assets/fetch-BAAh_kXG.js +0 -3
  591. package/dist/gateway/static/root/assets/index-C8yHX-AA.css +0 -1
  592. package/dist/gateway/static/root/assets/sessions-page-BCNnhz9g.js +0 -1
  593. package/dist/gateway/static/root/assets/settings-page-B7_PjiHL.js +0 -3
  594. package/dist/gateway/static/root/assets/skills-page-VrL9TeVF.js +0 -2
  595. package/dist/gateway/static/root/assets/voice-api-key-field-k4FWwgkk.js +0 -1
  596. package/dist/src/agent/embedded/session-raw-append-message.d.ts +0 -11
  597. package/dist/src/agent/embedded/session-raw-append-message.js +0 -15
  598. package/dist/src/agent/embedded/session-raw-append-message.js.map +0 -1
  599. package/dist/src/agent/embedded/session-tool-result-guard-wrapper.d.ts +0 -15
  600. package/dist/src/agent/embedded/session-tool-result-guard-wrapper.js +0 -24
  601. package/dist/src/agent/embedded/session-tool-result-guard-wrapper.js.map +0 -1
  602. package/dist/src/agent/embedded/session-tool-result-state.d.ts +0 -17
  603. package/dist/src/agent/embedded/session-tool-result-state.js +0 -26
  604. package/dist/src/agent/embedded/session-tool-result-state.js.map +0 -1
  605. package/dist/src/daemon/launchd-restart-handoff.d.ts +0 -25
  606. package/dist/src/daemon/launchd-restart-handoff.js +0 -132
  607. package/dist/src/daemon/launchd-restart-handoff.js.map +0 -1
  608. package/dist/src/gateway/auth-rate-limit.d.ts +0 -71
  609. package/dist/src/gateway/auth-rate-limit.js +0 -192
  610. package/dist/src/gateway/auth-rate-limit.js.map +0 -1
  611. package/dist/src/gateway/restart-handler.d.ts +0 -14
  612. package/dist/src/gateway/restart-handler.js +0 -64
  613. package/dist/src/gateway/restart-handler.js.map +0 -1
  614. package/dist/src/gateway/security/flood-guard.d.ts +0 -28
  615. package/dist/src/gateway/security/flood-guard.js +0 -42
  616. package/dist/src/gateway/security/flood-guard.js.map +0 -1
  617. package/dist/src/infra/rate-limit.d.ts +0 -38
  618. package/dist/src/infra/rate-limit.js +0 -60
  619. package/dist/src/infra/rate-limit.js.map +0 -1
  620. package/dist/src/infra/restart-intent.d.ts +0 -13
  621. package/dist/src/infra/restart-intent.js +0 -40
  622. package/dist/src/infra/restart-intent.js.map +0 -1
  623. package/dist/src/infra/restart-sentinel.d.ts +0 -23
  624. package/dist/src/infra/restart-sentinel.js +0 -75
  625. package/dist/src/infra/restart-sentinel.js.map +0 -1
  626. package/skills/creative/canvas-design/LICENSE.txt +0 -202
  627. package/skills/creative/canvas-design/SKILL-zh.md +0 -130
  628. package/skills/creative/canvas-design/SKILL.md +0 -130
  629. package/skills/creative/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +0 -93
  630. package/skills/creative/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
  631. package/skills/creative/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
  632. package/skills/creative/canvas-design/canvas-fonts/BigShoulders-OFL.txt +0 -93
  633. package/skills/creative/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
  634. package/skills/creative/canvas-design/canvas-fonts/Boldonse-OFL.txt +0 -93
  635. package/skills/creative/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
  636. package/skills/creative/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
  637. package/skills/creative/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +0 -93
  638. package/skills/creative/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
  639. package/skills/creative/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
  640. package/skills/creative/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
  641. package/skills/creative/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +0 -93
  642. package/skills/creative/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
  643. package/skills/creative/canvas-design/canvas-fonts/DMMono-OFL.txt +0 -93
  644. package/skills/creative/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
  645. package/skills/creative/canvas-design/canvas-fonts/EricaOne-OFL.txt +0 -94
  646. package/skills/creative/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
  647. package/skills/creative/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
  648. package/skills/creative/canvas-design/canvas-fonts/GeistMono-OFL.txt +0 -93
  649. package/skills/creative/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
  650. package/skills/creative/canvas-design/canvas-fonts/Gloock-OFL.txt +0 -93
  651. package/skills/creative/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
  652. package/skills/creative/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
  653. package/skills/creative/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +0 -93
  654. package/skills/creative/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
  655. package/skills/creative/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
  656. package/skills/creative/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
  657. package/skills/creative/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
  658. package/skills/creative/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
  659. package/skills/creative/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
  660. package/skills/creative/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
  661. package/skills/creative/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
  662. package/skills/creative/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +0 -93
  663. package/skills/creative/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
  664. package/skills/creative/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
  665. package/skills/creative/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
  666. package/skills/creative/canvas-design/canvas-fonts/Italiana-OFL.txt +0 -93
  667. package/skills/creative/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
  668. package/skills/creative/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
  669. package/skills/creative/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +0 -93
  670. package/skills/creative/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
  671. package/skills/creative/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
  672. package/skills/creative/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
  673. package/skills/creative/canvas-design/canvas-fonts/Jura-OFL.txt +0 -93
  674. package/skills/creative/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +0 -93
  675. package/skills/creative/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
  676. package/skills/creative/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
  677. package/skills/creative/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
  678. package/skills/creative/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
  679. package/skills/creative/canvas-design/canvas-fonts/Lora-OFL.txt +0 -93
  680. package/skills/creative/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
  681. package/skills/creative/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
  682. package/skills/creative/canvas-design/canvas-fonts/NationalPark-OFL.txt +0 -93
  683. package/skills/creative/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
  684. package/skills/creative/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +0 -93
  685. package/skills/creative/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
  686. package/skills/creative/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
  687. package/skills/creative/canvas-design/canvas-fonts/Outfit-OFL.txt +0 -93
  688. package/skills/creative/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
  689. package/skills/creative/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
  690. package/skills/creative/canvas-design/canvas-fonts/PixelifySans-OFL.txt +0 -93
  691. package/skills/creative/canvas-design/canvas-fonts/PoiretOne-OFL.txt +0 -93
  692. package/skills/creative/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
  693. package/skills/creative/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
  694. package/skills/creative/canvas-design/canvas-fonts/RedHatMono-OFL.txt +0 -93
  695. package/skills/creative/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
  696. package/skills/creative/canvas-design/canvas-fonts/Silkscreen-OFL.txt +0 -93
  697. package/skills/creative/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
  698. package/skills/creative/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
  699. package/skills/creative/canvas-design/canvas-fonts/SmoochSans-OFL.txt +0 -93
  700. package/skills/creative/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
  701. package/skills/creative/canvas-design/canvas-fonts/Tektur-OFL.txt +0 -93
  702. package/skills/creative/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
  703. package/skills/creative/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
  704. package/skills/creative/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
  705. package/skills/creative/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
  706. package/skills/creative/canvas-design/canvas-fonts/WorkSans-OFL.txt +0 -93
  707. package/skills/creative/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
  708. package/skills/creative/canvas-design/canvas-fonts/YoungSerif-OFL.txt +0 -93
  709. package/skills/creative/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"shares.js","names":[],"sources":["../../../../../src/gateway/hono/routes/shares.ts"],"sourcesContent":["import type { Hono } from 'hono';\nimport { createReadStream } from 'node:fs';\nimport { stat } from 'node:fs/promises';\nimport { createHash } from 'node:crypto';\nimport { Readable } from 'node:stream';\n\nimport { extractToken } from '../../auth.js';\nimport { getClientIpFromHeaders } from '../../auth-rate-limit.js';\nimport { getShareStore } from '../../../share/share-store.js';\nimport { resolveShareUrl } from '../../../share/share-url.js';\nimport { consumeSharePublicLimit } from '../../../share/share-rate-limit.js';\nimport { logShareAudit } from '../../../share/share-audit.js';\nimport { renderShareLandingPage, renderShareExpiredPage } from '../../../share/share-landing.js';\nimport type { ShareExpiredReason } from '../../../share/share-landing.js';\nimport type { ShareConfig } from '../../../share/share-types.js';\nimport { resolveGatewayEffectiveHost } from '../../../config/gateway-bind.js';\nimport { SHARE_CONFIG_DEFAULTS } from '../../../share/share-types.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\nimport type { GatewayService } from '../../service.js';\n\nfunction getShareUrlContext(service: GatewayService) {\n const gateway = service.currentConfig.gateway;\n return {\n gatewayHost: resolveGatewayEffectiveHost(service.currentConfig),\n gatewayPort: gateway.port ?? 18790,\n };\n}\n\nfunction resolveShareConfig(service: GatewayService): Partial<ShareConfig> {\n const raw = (service.currentConfig.gateway as Record<string, unknown>)?.share;\n if (!raw || typeof raw !== 'object') return {};\n return raw as Partial<ShareConfig>;\n}\n\nfunction hashGatewayToken(token: string): string {\n return createHash('sha256').update(token, 'utf8').digest('hex').slice(0, 12);\n}\n\nconst MAX_CONCURRENT_DOWNLOADS_PER_TOKEN = 5;\nconst activeDownloads = new Map<string, number>();\n\nfunction acquireDownloadSlot(token: string): boolean {\n const current = activeDownloads.get(token) ?? 0;\n if (current >= MAX_CONCURRENT_DOWNLOADS_PER_TOKEN) return false;\n activeDownloads.set(token, current + 1);\n return true;\n}\n\nfunction releaseDownloadSlot(token: string): void {\n const current = activeDownloads.get(token) ?? 0;\n if (current <= 1) {\n activeDownloads.delete(token);\n } else {\n activeDownloads.set(token, current - 1);\n }\n}\n\n// ── Public routes (no auth required) ──────────────────────────────────────────\n\nexport function registerSharePublicRoutes(app: Hono, service: GatewayService): void {\n const store = getShareStore(resolveShareConfig(service));\n\n /** Landing page — does NOT consume viewCount (prevents link unfurl from wasting views). */\n app.get('/s/:token', async (c) => {\n const clientIp = getClientIpFromHeaders({ get: (n: string) => c.req.header(n) ?? undefined });\n const rateResult = consumeSharePublicLimit(clientIp);\n if (!rateResult.allowed) {\n c.header('Retry-After', String(Math.ceil(rateResult.retryAfterMs / 1000)));\n return c.text('Too many requests', 429);\n }\n\n const token = c.req.param('token');\n const record = store.getByToken(token);\n\n if (!record) {\n return c.html(renderShareExpiredPage('not_found'), 404);\n }\n\n const validation = store.validateAccess(record);\n if (!validation.valid) {\n logShareAudit(\n 'share.access_denied',\n { shareId: record.id, tokenPrefix: token.slice(0, 8), reason: validation.reason, clientIp },\n `Share access denied: ${validation.reason}`,\n );\n return c.html(renderShareExpiredPage(validation.reason as ShareExpiredReason), 410);\n }\n\n // Direct download shortcut: ?dl=1\n if (c.req.query('dl') === '1') {\n return handleDownload(c, store, record, clientIp);\n }\n\n // Inline preview for whitelisted MIME types: ?inline=1\n if (c.req.query('inline') === '1') {\n const cfg = { ...SHARE_CONFIG_DEFAULTS, ...resolveShareConfig(service) };\n if (cfg.inlinePreviewMimes.includes(record.mimeType)) {\n return handleDownload(c, store, record, clientIp, true);\n }\n }\n\n const downloadPath = `/s/${token}/download`;\n return c.html(renderShareLandingPage(record, downloadPath));\n });\n\n /** Actual file download — consumes viewCount. */\n app.post('/s/:token/download', async (c) => {\n const clientIp = getClientIpFromHeaders({ get: (n: string) => c.req.header(n) ?? undefined });\n const rateResult = consumeSharePublicLimit(clientIp);\n if (!rateResult.allowed) {\n c.header('Retry-After', String(Math.ceil(rateResult.retryAfterMs / 1000)));\n return c.text('Too many requests', 429);\n }\n\n const token = c.req.param('token');\n const record = store.getByToken(token);\n\n if (!record) {\n return c.html(renderShareExpiredPage('not_found'), 404);\n }\n\n const validation = store.validateAccess(record);\n if (!validation.valid) {\n logShareAudit(\n 'share.access_denied',\n { shareId: record.id, tokenPrefix: token.slice(0, 8), reason: validation.reason, clientIp },\n `Share download denied: ${validation.reason}`,\n );\n return c.html(renderShareExpiredPage(validation.reason as ShareExpiredReason), 410);\n }\n\n return handleDownload(c, store, record, clientIp);\n });\n\n /** File metadata (for link preview cards). */\n app.get('/s/:token/meta', async (c) => {\n const clientIp = getClientIpFromHeaders({ get: (n: string) => c.req.header(n) ?? undefined });\n const rateResult = consumeSharePublicLimit(clientIp);\n if (!rateResult.allowed) {\n c.header('Retry-After', String(Math.ceil(rateResult.retryAfterMs / 1000)));\n return c.text('Too many requests', 429);\n }\n\n const token = c.req.param('token');\n const record = store.getByToken(token);\n if (!record) {\n return c.json({ valid: false }, 404);\n }\n\n const validation = store.validateAccess(record);\n const remainingViews = record.maxViews !== null ? Math.max(0, record.maxViews - record.viewCount) : null;\n\n return c.json({\n fileName: record.fileName,\n fileSize: record.fileSize,\n mimeType: record.mimeType,\n description: record.description ?? null,\n expiresAt: record.expiresAt,\n remainingViews,\n valid: validation.valid,\n });\n });\n\n /** HEAD check (Hono uses .on() for HEAD method). */\n app.on('HEAD', '/s/:token', async (c) => {\n const token = c.req.param('token');\n const record = store.getByToken(token);\n if (!record) return c.body(null, 404);\n const validation = store.validateAccess(record);\n return c.body(null, validation.valid ? 200 : 410);\n });\n}\n\n// ── Authenticated routes ──────────────────────────────────────────────────────\n\nexport function registerShareRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service } = deps;\n const store = getShareStore(resolveShareConfig(service));\n\n /** Create a share. */\n authenticated.post('/api/shares', async (c) => {\n const gatewayToken = extractToken({ authorization: c.req.header('authorization') ?? undefined });\n if (!gatewayToken) return c.json({ ok: false, error: { message: 'Token required' } }, 401);\n\n let body: Record<string, unknown>;\n try {\n body = (await c.req.json()) as Record<string, unknown>;\n } catch {\n return c.json({ ok: false, error: { message: 'Invalid JSON' } }, 400);\n }\n\n const path = typeof body.path === 'string' ? body.path.trim() : '';\n if (!path) {\n return c.json({ ok: false, error: { message: 'Missing path' } }, 400);\n }\n\n const sessionKey = typeof body.sessionKey === 'string' ? body.sessionKey.trim() : undefined;\n const agentId = typeof body.agentId === 'string' ? body.agentId.trim() : undefined;\n\n // Resolve workspace root (same logic as workspace editor routes)\n const workspaceRoot = await resolveWorkspaceRootForShare(service, sessionKey, agentId);\n if (!workspaceRoot) {\n return c.json({ ok: false, error: { message: 'Workspace not configured' } }, 400);\n }\n\n const ttlMs = typeof body.ttlMs === 'number' ? body.ttlMs : undefined;\n const maxViews = body.maxViews === null ? null : typeof body.maxViews === 'number' ? body.maxViews : undefined;\n const description = typeof body.description === 'string' ? body.description.trim() || undefined : undefined;\n\n try {\n store.updateConfig(resolveShareConfig(service));\n const record = await store.create({\n path,\n ttlMs,\n maxViews,\n description,\n sessionKey,\n agentId,\n workspaceRoot,\n gatewayTokenHash: hashGatewayToken(gatewayToken),\n });\n\n const urlCtx = getShareUrlContext(service);\n const resolved = resolveShareUrl(record.token, urlCtx);\n\n return c.json({\n ok: true,\n payload: {\n id: record.id,\n token: record.token,\n shareUrl: resolved.shareUrl,\n lanUrl: resolved.lanUrl,\n reachability: resolved.reachability,\n reachabilityHint: resolved.reachabilityHint,\n expiresAt: record.expiresAt,\n maxViews: record.maxViews,\n fileName: record.fileName,\n fileSize: record.fileSize,\n },\n }, 201);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return c.json({ ok: false, error: { message } }, 400);\n }\n });\n\n /** List all shares. */\n authenticated.get('/api/shares', async (c) => {\n store.updateConfig(resolveShareConfig(service));\n const shares = store.getAllShares();\n const urlCtx = getShareUrlContext(service);\n const now = Date.now();\n\n const items = shares.map((r) => {\n const resolved = resolveShareUrl(r.token, urlCtx);\n const expired = now >= new Date(r.expiresAt).getTime();\n return {\n id: r.id,\n fileName: r.fileName,\n workspaceRelativePath: r.workspaceRelativePath,\n shareUrl: resolved.shareUrl,\n lanUrl: resolved.lanUrl,\n reachability: resolved.reachability,\n createdAt: r.createdAt,\n expiresAt: r.expiresAt,\n viewCount: r.viewCount,\n maxViews: r.maxViews,\n revoked: r.revoked,\n expired,\n description: r.description ?? null,\n fileSize: r.fileSize,\n mimeType: r.mimeType,\n };\n });\n\n return c.json({ ok: true, payload: { shares: items } });\n });\n\n /** Get single share details. */\n authenticated.get('/api/shares/:id', async (c) => {\n const id = c.req.param('id');\n const record = store.getById(id);\n if (!record) return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n\n const urlCtx = getShareUrlContext(service);\n const resolved = resolveShareUrl(record.token, urlCtx);\n const expired = Date.now() >= new Date(record.expiresAt).getTime();\n\n return c.json({\n ok: true,\n payload: {\n ...record,\n token: undefined,\n shareUrl: resolved.shareUrl,\n lanUrl: resolved.lanUrl,\n reachability: resolved.reachability,\n expired,\n },\n });\n });\n\n /** Revoke a share. */\n authenticated.delete('/api/shares/:id', async (c) => {\n const id = c.req.param('id');\n const success = store.revoke(id);\n if (!success) return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n return c.json({ ok: true });\n });\n\n /** Batch revoke. */\n authenticated.delete('/api/shares', async (c) => {\n let body: Record<string, unknown> = {};\n try {\n body = (await c.req.json()) as Record<string, unknown>;\n } catch {\n /* empty body = no-op */\n }\n\n if (body.expired === true) {\n const count = store.revokeExpired();\n return c.json({ ok: true, payload: { revokedCount: count } });\n }\n\n const ids = Array.isArray(body.ids) ? (body.ids as string[]).filter((x) => typeof x === 'string') : [];\n if (ids.length === 0) {\n return c.json({ ok: false, error: { message: 'Provide ids array or expired: true' } }, 400);\n }\n const count = store.revokeMany(ids);\n return c.json({ ok: true, payload: { revokedCount: count } });\n });\n\n /** Update a share (extend TTL or change maxViews). */\n authenticated.patch('/api/shares/:id', async (c) => {\n const id = c.req.param('id');\n let body: Record<string, unknown>;\n try {\n body = (await c.req.json()) as Record<string, unknown>;\n } catch {\n return c.json({ ok: false, error: { message: 'Invalid JSON' } }, 400);\n }\n\n const patch: { extendTtlMs?: number; maxViews?: number | null } = {};\n if (typeof body.extendTtlMs === 'number') patch.extendTtlMs = body.extendTtlMs;\n if (body.maxViews === null || typeof body.maxViews === 'number') patch.maxViews = body.maxViews as number | null;\n\n const updated = store.update(id, patch);\n if (!updated) return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n\n const urlCtx = getShareUrlContext(service);\n const resolved = resolveShareUrl(updated.token, urlCtx);\n\n return c.json({\n ok: true,\n payload: {\n id: updated.id,\n expiresAt: updated.expiresAt,\n maxViews: updated.maxViews,\n shareUrl: resolved.shareUrl,\n },\n });\n });\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nasync function resolveWorkspaceRootForShare(\n service: GatewayService,\n sessionKey: string | undefined,\n agentId: string | undefined,\n): Promise<string | null> {\n const cfg = service.currentConfig;\n\n if (sessionKey) {\n try {\n return await service.getEffectiveWorkspacePathForSession(sessionKey);\n } catch {\n /* fall through to agentId */\n }\n }\n\n // Import dynamically to avoid circular dependency at module load time\n const { getWorkspacePath } = await import('../../../config/schema.js');\n const { resolveAgentWorkspaceDir, normalizeAgentId, resolveDefaultAgentId } = await import(\n '../../../agent/agent-scope.js'\n );\n\n if (agentId) {\n const normalized = normalizeAgentId(agentId);\n return resolveAgentWorkspaceDir(cfg, normalized);\n }\n\n const root = getWorkspacePath(cfg);\n if (root) return root;\n\n const defaultId = resolveDefaultAgentId(cfg);\n return resolveAgentWorkspaceDir(cfg, defaultId);\n}\n\nasync function handleDownload(\n c: { header: (n: string, v: string) => void; body: (b: unknown, s?: number) => Response },\n store: ReturnType<typeof getShareStore>,\n record: ReturnType<ReturnType<typeof getShareStore>['getByToken']> & {},\n clientIp: string,\n inline = false,\n): Promise<Response> {\n // Concurrency check\n if (!acquireDownloadSlot(record.token)) {\n return c.body('Too many concurrent downloads for this share', 429) as unknown as Response;\n }\n\n try {\n // File integrity check (inode + path)\n const integrity = await store.validateFileIntegrity(record);\n if (!integrity.valid) {\n logShareAudit(\n 'share.access_denied',\n { shareId: record.id, tokenPrefix: record.token.slice(0, 8), reason: integrity.reason, clientIp },\n `Share file integrity check failed: ${integrity.reason}`,\n );\n const { renderShareExpiredPage: render } = await import('../../../share/share-landing.js');\n return new Response(render(integrity.reason as ShareExpiredReason), {\n status: 410,\n headers: { 'Content-Type': 'text/html; charset=utf-8' },\n });\n }\n\n // Consume viewCount\n store.incrementViewCount(record.id);\n\n logShareAudit(\n 'share.access',\n { shareId: record.id, tokenPrefix: record.token.slice(0, 8), clientIp, viewCount: record.viewCount },\n `Share downloaded: ${record.fileName}`,\n );\n\n // Stream file\n const fileStat = await stat(record.absolutePath);\n const stream = createReadStream(record.absolutePath);\n const webStream = Readable.toWeb(stream) as ReadableStream;\n\n const disposition = inline ? `inline; filename=\"${encodeURIComponent(record.fileName)}\"` : `attachment; filename=\"${encodeURIComponent(record.fileName)}\"`;\n\n return new Response(webStream, {\n status: 200,\n headers: {\n 'Content-Type': record.mimeType,\n 'Content-Disposition': disposition,\n 'Content-Length': String(fileStat.size),\n 'Cache-Control': 'private, no-store',\n 'X-Content-Type-Options': 'nosniff',\n 'X-Frame-Options': 'DENY',\n 'Referrer-Policy': 'no-referrer',\n },\n });\n } finally {\n releaseDownloadSlot(record.token);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"shares.js","names":[],"sources":["../../../../../src/gateway/hono/routes/shares.ts"],"sourcesContent":["import type { Hono } from 'hono';\nimport { createReadStream } from 'node:fs';\nimport { stat } from 'node:fs/promises';\nimport { createHash } from 'node:crypto';\nimport { Readable } from 'node:stream';\n\nimport { extractToken } from '../../auth.js';\nimport { getClientIpFromHeaders } from '../../security/loopback.js';\nimport { getShareStore } from '../../../share/share-store.js';\nimport { resolveShareUrl } from '../../../share/share-url.js';\nimport { consumeSharePublicLimit } from '../../../share/share-rate-limit.js';\nimport { logShareAudit } from '../../../share/share-audit.js';\nimport { renderShareLandingPage, renderShareExpiredPage } from '../../../share/share-landing.js';\nimport type { ShareExpiredReason } from '../../../share/share-landing.js';\nimport type { ShareConfig } from '../../../share/share-types.js';\nimport { resolveGatewayEffectiveHost } from '../../../config/gateway-bind.js';\nimport { SHARE_CONFIG_DEFAULTS } from '../../../share/share-types.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\nimport type { GatewayService } from '../../service.js';\n\nfunction getShareUrlContext(service: GatewayService) {\n const gateway = service.currentConfig.gateway;\n return {\n gatewayHost: resolveGatewayEffectiveHost(service.currentConfig),\n gatewayPort: gateway.port ?? 18790,\n };\n}\n\nfunction resolveShareConfig(service: GatewayService): Partial<ShareConfig> {\n const raw = (service.currentConfig.gateway as Record<string, unknown>)?.share;\n if (!raw || typeof raw !== 'object') return {};\n return raw as Partial<ShareConfig>;\n}\n\nfunction hashGatewayToken(token: string): string {\n return createHash('sha256').update(token, 'utf8').digest('hex').slice(0, 12);\n}\n\nconst MAX_CONCURRENT_DOWNLOADS_PER_TOKEN = 5;\nconst activeDownloads = new Map<string, number>();\n\nfunction acquireDownloadSlot(token: string): boolean {\n const current = activeDownloads.get(token) ?? 0;\n if (current >= MAX_CONCURRENT_DOWNLOADS_PER_TOKEN) return false;\n activeDownloads.set(token, current + 1);\n return true;\n}\n\nfunction releaseDownloadSlot(token: string): void {\n const current = activeDownloads.get(token) ?? 0;\n if (current <= 1) {\n activeDownloads.delete(token);\n } else {\n activeDownloads.set(token, current - 1);\n }\n}\n\n// ── Public routes (no auth required) ──────────────────────────────────────────\n\nexport function registerSharePublicRoutes(app: Hono, service: GatewayService): void {\n const store = getShareStore(resolveShareConfig(service));\n\n /** Landing page — does NOT consume viewCount (prevents link unfurl from wasting views). */\n app.get('/s/:token', async (c) => {\n const clientIp = getClientIpFromHeaders({ get: (n: string) => c.req.header(n) ?? undefined });\n const rateResult = consumeSharePublicLimit(clientIp);\n if (!rateResult.allowed) {\n c.header('Retry-After', String(Math.ceil(rateResult.retryAfterMs / 1000)));\n return c.text('Too many requests', 429);\n }\n\n const token = c.req.param('token');\n const record = store.getByToken(token);\n\n if (!record) {\n return c.html(renderShareExpiredPage('not_found'), 404);\n }\n\n const validation = store.validateAccess(record);\n if (!validation.valid) {\n logShareAudit(\n 'share.access_denied',\n { shareId: record.id, tokenPrefix: token.slice(0, 8), reason: validation.reason, clientIp },\n `Share access denied: ${validation.reason}`,\n );\n return c.html(renderShareExpiredPage(validation.reason as ShareExpiredReason), 410);\n }\n\n // Direct download shortcut: ?dl=1\n if (c.req.query('dl') === '1') {\n return handleDownload(c, store, record, clientIp);\n }\n\n // Inline preview for whitelisted MIME types: ?inline=1\n if (c.req.query('inline') === '1') {\n const cfg = { ...SHARE_CONFIG_DEFAULTS, ...resolveShareConfig(service) };\n if (cfg.inlinePreviewMimes.includes(record.mimeType)) {\n return handleDownload(c, store, record, clientIp, true);\n }\n }\n\n const downloadPath = `/s/${token}/download`;\n return c.html(renderShareLandingPage(record, downloadPath));\n });\n\n /** Actual file download — consumes viewCount. */\n app.post('/s/:token/download', async (c) => {\n const clientIp = getClientIpFromHeaders({ get: (n: string) => c.req.header(n) ?? undefined });\n const rateResult = consumeSharePublicLimit(clientIp);\n if (!rateResult.allowed) {\n c.header('Retry-After', String(Math.ceil(rateResult.retryAfterMs / 1000)));\n return c.text('Too many requests', 429);\n }\n\n const token = c.req.param('token');\n const record = store.getByToken(token);\n\n if (!record) {\n return c.html(renderShareExpiredPage('not_found'), 404);\n }\n\n const validation = store.validateAccess(record);\n if (!validation.valid) {\n logShareAudit(\n 'share.access_denied',\n { shareId: record.id, tokenPrefix: token.slice(0, 8), reason: validation.reason, clientIp },\n `Share download denied: ${validation.reason}`,\n );\n return c.html(renderShareExpiredPage(validation.reason as ShareExpiredReason), 410);\n }\n\n return handleDownload(c, store, record, clientIp);\n });\n\n /** File metadata (for link preview cards). */\n app.get('/s/:token/meta', async (c) => {\n const clientIp = getClientIpFromHeaders({ get: (n: string) => c.req.header(n) ?? undefined });\n const rateResult = consumeSharePublicLimit(clientIp);\n if (!rateResult.allowed) {\n c.header('Retry-After', String(Math.ceil(rateResult.retryAfterMs / 1000)));\n return c.text('Too many requests', 429);\n }\n\n const token = c.req.param('token');\n const record = store.getByToken(token);\n if (!record) {\n return c.json({ valid: false }, 404);\n }\n\n const validation = store.validateAccess(record);\n const remainingViews = record.maxViews !== null ? Math.max(0, record.maxViews - record.viewCount) : null;\n\n return c.json({\n fileName: record.fileName,\n fileSize: record.fileSize,\n mimeType: record.mimeType,\n description: record.description ?? null,\n expiresAt: record.expiresAt,\n remainingViews,\n valid: validation.valid,\n });\n });\n\n /** HEAD check (Hono uses .on() for HEAD method). */\n app.on('HEAD', '/s/:token', async (c) => {\n const token = c.req.param('token');\n const record = store.getByToken(token);\n if (!record) return c.body(null, 404);\n const validation = store.validateAccess(record);\n return c.body(null, validation.valid ? 200 : 410);\n });\n}\n\n// ── Authenticated routes ──────────────────────────────────────────────────────\n\nexport function registerShareRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service } = deps;\n const store = getShareStore(resolveShareConfig(service));\n\n /** Create a share. */\n authenticated.post('/api/shares', async (c) => {\n const gatewayToken = extractToken({ authorization: c.req.header('authorization') ?? undefined });\n if (!gatewayToken) return c.json({ ok: false, error: { message: 'Token required' } }, 401);\n\n let body: Record<string, unknown>;\n try {\n body = (await c.req.json()) as Record<string, unknown>;\n } catch {\n return c.json({ ok: false, error: { message: 'Invalid JSON' } }, 400);\n }\n\n const path = typeof body.path === 'string' ? body.path.trim() : '';\n if (!path) {\n return c.json({ ok: false, error: { message: 'Missing path' } }, 400);\n }\n\n const sessionKey = typeof body.sessionKey === 'string' ? body.sessionKey.trim() : undefined;\n const agentId = typeof body.agentId === 'string' ? body.agentId.trim() : undefined;\n\n // Resolve workspace root (same logic as workspace editor routes)\n const workspaceRoot = await resolveWorkspaceRootForShare(service, sessionKey, agentId);\n if (!workspaceRoot) {\n return c.json({ ok: false, error: { message: 'Workspace not configured' } }, 400);\n }\n\n const ttlMs = typeof body.ttlMs === 'number' ? body.ttlMs : undefined;\n const maxViews = body.maxViews === null ? null : typeof body.maxViews === 'number' ? body.maxViews : undefined;\n const description = typeof body.description === 'string' ? body.description.trim() || undefined : undefined;\n\n try {\n store.updateConfig(resolveShareConfig(service));\n const record = await store.create({\n path,\n ttlMs,\n maxViews,\n description,\n sessionKey,\n agentId,\n workspaceRoot,\n gatewayTokenHash: hashGatewayToken(gatewayToken),\n });\n\n const urlCtx = getShareUrlContext(service);\n const resolved = resolveShareUrl(record.token, urlCtx);\n\n return c.json({\n ok: true,\n payload: {\n id: record.id,\n token: record.token,\n shareUrl: resolved.shareUrl,\n lanUrl: resolved.lanUrl,\n reachability: resolved.reachability,\n reachabilityHint: resolved.reachabilityHint,\n expiresAt: record.expiresAt,\n maxViews: record.maxViews,\n fileName: record.fileName,\n fileSize: record.fileSize,\n },\n }, 201);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return c.json({ ok: false, error: { message } }, 400);\n }\n });\n\n /** List all shares. */\n authenticated.get('/api/shares', async (c) => {\n store.updateConfig(resolveShareConfig(service));\n const shares = store.getAllShares();\n const urlCtx = getShareUrlContext(service);\n const now = Date.now();\n\n const items = shares.map((r) => {\n const resolved = resolveShareUrl(r.token, urlCtx);\n const expired = now >= new Date(r.expiresAt).getTime();\n return {\n id: r.id,\n fileName: r.fileName,\n workspaceRelativePath: r.workspaceRelativePath,\n shareUrl: resolved.shareUrl,\n lanUrl: resolved.lanUrl,\n reachability: resolved.reachability,\n createdAt: r.createdAt,\n expiresAt: r.expiresAt,\n viewCount: r.viewCount,\n maxViews: r.maxViews,\n revoked: r.revoked,\n expired,\n description: r.description ?? null,\n fileSize: r.fileSize,\n mimeType: r.mimeType,\n };\n });\n\n return c.json({ ok: true, payload: { shares: items } });\n });\n\n /** Get single share details. */\n authenticated.get('/api/shares/:id', async (c) => {\n const id = c.req.param('id');\n const record = store.getById(id);\n if (!record) return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n\n const urlCtx = getShareUrlContext(service);\n const resolved = resolveShareUrl(record.token, urlCtx);\n const expired = Date.now() >= new Date(record.expiresAt).getTime();\n\n return c.json({\n ok: true,\n payload: {\n ...record,\n token: undefined,\n shareUrl: resolved.shareUrl,\n lanUrl: resolved.lanUrl,\n reachability: resolved.reachability,\n expired,\n },\n });\n });\n\n /** Revoke a share. */\n authenticated.delete('/api/shares/:id', async (c) => {\n const id = c.req.param('id');\n const success = store.revoke(id);\n if (!success) return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n return c.json({ ok: true });\n });\n\n /** Batch revoke. */\n authenticated.delete('/api/shares', async (c) => {\n let body: Record<string, unknown> = {};\n try {\n body = (await c.req.json()) as Record<string, unknown>;\n } catch {\n /* empty body = no-op */\n }\n\n if (body.expired === true) {\n const count = store.revokeExpired();\n return c.json({ ok: true, payload: { revokedCount: count } });\n }\n\n const ids = Array.isArray(body.ids) ? (body.ids as string[]).filter((x) => typeof x === 'string') : [];\n if (ids.length === 0) {\n return c.json({ ok: false, error: { message: 'Provide ids array or expired: true' } }, 400);\n }\n const count = store.revokeMany(ids);\n return c.json({ ok: true, payload: { revokedCount: count } });\n });\n\n /** Update a share (extend TTL or change maxViews). */\n authenticated.patch('/api/shares/:id', async (c) => {\n const id = c.req.param('id');\n let body: Record<string, unknown>;\n try {\n body = (await c.req.json()) as Record<string, unknown>;\n } catch {\n return c.json({ ok: false, error: { message: 'Invalid JSON' } }, 400);\n }\n\n const patch: { extendTtlMs?: number; maxViews?: number | null } = {};\n if (typeof body.extendTtlMs === 'number') patch.extendTtlMs = body.extendTtlMs;\n if (body.maxViews === null || typeof body.maxViews === 'number') patch.maxViews = body.maxViews as number | null;\n\n const updated = store.update(id, patch);\n if (!updated) return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n\n const urlCtx = getShareUrlContext(service);\n const resolved = resolveShareUrl(updated.token, urlCtx);\n\n return c.json({\n ok: true,\n payload: {\n id: updated.id,\n expiresAt: updated.expiresAt,\n maxViews: updated.maxViews,\n shareUrl: resolved.shareUrl,\n },\n });\n });\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nasync function resolveWorkspaceRootForShare(\n service: GatewayService,\n sessionKey: string | undefined,\n agentId: string | undefined,\n): Promise<string | null> {\n const cfg = service.currentConfig;\n\n if (sessionKey) {\n try {\n return await service.sessions.getEffectiveWorkspacePath(sessionKey);\n } catch {\n /* fall through to agentId */\n }\n }\n\n // Import dynamically to avoid circular dependency at module load time\n const { getWorkspacePath } = await import('../../../config/workspace-path-helpers.js');\n const { resolveAgentWorkspaceDir, normalizeAgentId, resolveDefaultAgentId } = await import(\n '../../../agent/agent-scope.js'\n );\n\n if (agentId) {\n const normalized = normalizeAgentId(agentId);\n return resolveAgentWorkspaceDir(cfg, normalized);\n }\n\n const root = getWorkspacePath(cfg);\n if (root) return root;\n\n const defaultId = resolveDefaultAgentId(cfg);\n return resolveAgentWorkspaceDir(cfg, defaultId);\n}\n\nasync function handleDownload(\n c: { header: (n: string, v: string) => void; body: (b: unknown, s?: number) => Response },\n store: ReturnType<typeof getShareStore>,\n record: ReturnType<ReturnType<typeof getShareStore>['getByToken']> & {},\n clientIp: string,\n inline = false,\n): Promise<Response> {\n // Concurrency check\n if (!acquireDownloadSlot(record.token)) {\n return c.body('Too many concurrent downloads for this share', 429) as unknown as Response;\n }\n\n try {\n // File integrity check (inode + path)\n const integrity = await store.validateFileIntegrity(record);\n if (!integrity.valid) {\n logShareAudit(\n 'share.access_denied',\n { shareId: record.id, tokenPrefix: record.token.slice(0, 8), reason: integrity.reason, clientIp },\n `Share file integrity check failed: ${integrity.reason}`,\n );\n const { renderShareExpiredPage: render } = await import('../../../share/share-landing.js');\n return new Response(render(integrity.reason as ShareExpiredReason), {\n status: 410,\n headers: { 'Content-Type': 'text/html; charset=utf-8' },\n });\n }\n\n // Consume viewCount\n store.incrementViewCount(record.id);\n\n logShareAudit(\n 'share.access',\n { shareId: record.id, tokenPrefix: record.token.slice(0, 8), clientIp, viewCount: record.viewCount },\n `Share downloaded: ${record.fileName}`,\n );\n\n // Stream file\n const fileStat = await stat(record.absolutePath);\n const stream = createReadStream(record.absolutePath);\n const webStream = Readable.toWeb(stream) as ReadableStream;\n\n const disposition = inline ? `inline; filename=\"${encodeURIComponent(record.fileName)}\"` : `attachment; filename=\"${encodeURIComponent(record.fileName)}\"`;\n\n return new Response(webStream, {\n status: 200,\n headers: {\n 'Content-Type': record.mimeType,\n 'Content-Disposition': disposition,\n 'Content-Length': String(fileStat.size),\n 'Cache-Control': 'private, no-store',\n 'X-Content-Type-Options': 'nosniff',\n 'X-Frame-Options': 'DENY',\n 'Referrer-Policy': 'no-referrer',\n },\n });\n } finally {\n releaseDownloadSlot(record.token);\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,4 @@
1
1
  import { resolveGatewayEffectiveHost } from "../../../config/gateway-bind.js";
2
- import { getClientIpFromHeaders } from "../../auth-rate-limit.js";
3
2
  import { extractToken } from "../../auth.js";
4
3
  import { TUNNEL_CONSENT_REQUIRED_CODE, TunnelConsentError, assertTunnelMayStart, getTunnelConsentState } from "../../../tunnel/consent.js";
5
4
  import { getTunnelRegistrationSecretMeta, readTunnelRegistrationSecretFromConfigOnly, resolveTunnelBrokerUrl } from "../../../tunnel/env.js";
@@ -9,6 +8,7 @@ import { logTunnelAudit } from "../../../tunnel/tunnel-audit.js";
9
8
  import { getTunnelService, hashGatewayToken } from "../../../tunnel/tunnel-service.js";
10
9
  import { configureTunnelFromGatewayConfig } from "../../../tunnel/gateway-lifecycle.js";
11
10
  import { applyTunnelConsentToConfig, setTunnelEnabledInConfig } from "../../../tunnel/tunnel-config.js";
11
+ import { getClientIpFromHeaders } from "../../security/loopback.js";
12
12
  import { applyLanPairingGatewayPatch } from "../../../tunnel/enable-lan-pairing.js";
13
13
  import { consumeTunnelMutationLimit } from "../../../tunnel/tunnel-rate-limit.js";
14
14
  import "../../../tunnel/index.js";
@@ -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":"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"}
@@ -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);