@xopcai/xopc 0.0.83 → 0.0.85

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 (457) hide show
  1. package/README.md +2 -0
  2. package/README.zh-CN.md +3 -1
  3. package/dist/browser-ext/manifest.json +1 -1
  4. package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
  5. package/dist/extensions/feishu/src/plugin.d.ts +2 -0
  6. package/dist/extensions/feishu/src/plugin.js +10 -0
  7. package/dist/extensions/feishu/src/plugin.js.map +1 -1
  8. package/dist/extensions/feishu/src/workflow-progress.d.ts +27 -0
  9. package/dist/extensions/feishu/src/workflow-progress.js +99 -0
  10. package/dist/extensions/feishu/src/workflow-progress.js.map +1 -0
  11. package/dist/extensions/telegram/src/plugin.d.ts +2 -0
  12. package/dist/extensions/telegram/src/plugin.js +11 -1
  13. package/dist/extensions/telegram/src/plugin.js.map +1 -1
  14. package/dist/extensions/telegram/src/routing-integration.js +2 -2
  15. package/dist/extensions/telegram/src/workflow-progress.d.ts +24 -0
  16. package/dist/extensions/telegram/src/workflow-progress.js +73 -0
  17. package/dist/extensions/telegram/src/workflow-progress.js.map +1 -0
  18. package/dist/extensions/telegram/xopc.extension.json +1 -1
  19. package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js +158 -0
  20. package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js.map +1 -0
  21. package/dist/extensions/weixin/src/api/api.js +2 -2
  22. package/dist/extensions/weixin/src/auth/accounts.js +1 -1
  23. package/dist/extensions/weixin/src/cdn/upload.js +1 -1
  24. package/dist/extensions/weixin/src/media/data-url.js +1 -1
  25. package/dist/extensions/weixin/src/messaging/debug-mode.js +1 -1
  26. package/dist/extensions/weixin/src/messaging/inbound.js +1 -1
  27. package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
  28. package/dist/extensions/weixin/src/plugin.d.ts +2 -0
  29. package/dist/extensions/weixin/src/plugin.js +11 -1
  30. package/dist/extensions/weixin/src/plugin.js.map +1 -1
  31. package/dist/extensions/weixin/src/storage/sync-buf.js +1 -1
  32. package/dist/extensions/weixin/src/workflow-progress.d.ts +26 -0
  33. package/dist/extensions/weixin/src/workflow-progress.js +99 -0
  34. package/dist/extensions/weixin/src/workflow-progress.js.map +1 -0
  35. package/dist/gateway/static/root/assets/agents-D3_-kNlZ.js +222 -0
  36. package/dist/gateway/static/root/assets/apps-page-D7v7649T.js +1 -0
  37. package/dist/gateway/static/root/assets/channels-settings-nCaMb0a7.js +1 -0
  38. package/dist/gateway/static/root/assets/channels-status-swr-C1gZBcJV.js +8 -0
  39. package/dist/gateway/static/root/assets/createLucideIcon-DPHK1VkS.js +1 -0
  40. package/dist/gateway/static/root/assets/cron-api-CoYK0hlm.js +1 -0
  41. package/dist/gateway/static/root/assets/cron-page-DeGo-Vjc.js +1 -0
  42. package/dist/gateway/static/root/assets/dist-BTWC-BTN.js +45 -0
  43. package/dist/gateway/static/root/assets/{dist-BpQxde0t.js → dist-DaK4dsss.js} +1 -1
  44. package/dist/gateway/static/root/assets/{extension-debug-page-CY27wj_p.js → extension-debug-page-BZngZWbO.js} +1 -1
  45. package/dist/gateway/static/root/assets/extension-page-D6JSyV27.js +1 -0
  46. package/dist/gateway/static/root/assets/extension-settings-page-8PZcmWI7.js +1 -0
  47. package/dist/gateway/static/root/assets/fetch-B2MYHbWg.js +1 -0
  48. package/dist/gateway/static/root/assets/{field-primitives-fa_hiQcX.js → field-primitives-Zzl22MvN.js} +1 -1
  49. package/dist/gateway/static/root/assets/heartbeat-config-api-BtIcpG0O.js +1 -0
  50. package/dist/gateway/static/root/assets/index-D4vM3-P7.js +4700 -0
  51. package/dist/gateway/static/root/assets/index-ew_2L2We.css +1 -0
  52. package/dist/gateway/static/root/assets/logs-page-_d4UJ-qQ.js +1 -0
  53. package/dist/gateway/static/root/assets/sessions-page-5N4aF2Wk.js +1 -0
  54. package/dist/gateway/static/root/assets/settings-form-section-D_tgb8r2.js +1 -0
  55. package/dist/gateway/static/root/assets/settings-page-C18xBt4X.js +3 -0
  56. package/dist/gateway/static/root/assets/share-preview-page-D4EG_vM1.js +2 -0
  57. package/dist/gateway/static/root/assets/skills-page-sPAXhh8w.js +2 -0
  58. package/dist/gateway/static/root/assets/theme-store-DryYl3qD.js +1 -0
  59. package/dist/gateway/static/root/assets/url-BwNL6Rgk.js +3 -0
  60. package/dist/gateway/static/root/assets/utils-CYO9eTCM.js +1 -0
  61. package/dist/gateway/static/root/assets/voice-api-key-field-Ds51havm.js +1 -0
  62. package/dist/gateway/static/root/index.html +7 -6
  63. package/dist/package.js +1 -1
  64. package/dist/src/agent/agent-manager.js +7 -7
  65. package/dist/src/agent/bootstrap/load-bootstrap-files.js +1 -1
  66. package/dist/src/agent/context/workspace-seed.js +3 -3
  67. package/dist/src/agent/embedded/map-stream-events.js +6 -0
  68. package/dist/src/agent/embedded/map-stream-events.js.map +1 -1
  69. package/dist/src/agent/embedded/subscribe-session.js +24 -0
  70. package/dist/src/agent/embedded/subscribe-session.js.map +1 -1
  71. package/dist/src/agent/embedded/types.d.ts +19 -0
  72. package/dist/src/agent/goals/goal-locale.js +2 -2
  73. package/dist/src/agent/goals/goal-run-store.js +4 -4
  74. package/dist/src/agent/goals/persistent-goal-service.js +1 -1
  75. package/dist/src/agent/goals/post-turn.js +2 -2
  76. package/dist/src/agent/image/load-image-media.js +2 -2
  77. package/dist/src/agent/ipc/bus.js +1 -1
  78. package/dist/src/agent/ipc/inbox.js +2 -2
  79. package/dist/src/agent/ipc/socket.js +1 -1
  80. package/dist/src/agent/memory/builtin-memory-store.js +1 -1
  81. package/dist/src/agent/memory/dreaming/deep-promotion.js +1 -1
  82. package/dist/src/agent/memory/dreaming/events.js +1 -1
  83. package/dist/src/agent/memory/dreaming/last-run.js +1 -1
  84. package/dist/src/agent/memory/dreaming/light-sweep.js +1 -1
  85. package/dist/src/agent/memory/dreaming/preview.js +1 -1
  86. package/dist/src/agent/memory/dreaming/rem-patterns.js +1 -1
  87. package/dist/src/agent/memory/dreaming/short-term-store.js +1 -1
  88. package/dist/src/agent/memory/dreaming/utils.js +1 -1
  89. package/dist/src/agent/memory/plugin-discovery.js +1 -1
  90. package/dist/src/agent/models/manager.js +1 -1
  91. package/dist/src/agent/prompt/service-prompt-builder.js +2 -2
  92. package/dist/src/agent/reply/post-compaction-context.js +1 -1
  93. package/dist/src/agent/reply/startup-context.d.ts +3 -0
  94. package/dist/src/agent/reply/startup-context.js +25 -2
  95. package/dist/src/agent/reply/startup-context.js.map +1 -1
  96. package/dist/src/agent/reply/workspace-boundary-read.js +1 -1
  97. package/dist/src/agent/sandbox/path-policy.js +2 -2
  98. package/dist/src/agent/service/build-direct-message-content.js +1 -1
  99. package/dist/src/agent/service.d.ts +1 -0
  100. package/dist/src/agent/service.js +10 -4
  101. package/dist/src/agent/service.js.map +1 -1
  102. package/dist/src/agent/session/session-inspector.js +1 -1
  103. package/dist/src/agent/skills/config.js +1 -1
  104. package/dist/src/agent/skills/hub-hash.js +2 -2
  105. package/dist/src/agent/skills/hub-lock.js +1 -1
  106. package/dist/src/agent/skills/hub-pull.js +3 -3
  107. package/dist/src/agent/skills/index.js +1 -1
  108. package/dist/src/agent/skills/managed-store.js +1 -1
  109. package/dist/src/agent/skills/scanner.js +1 -1
  110. package/dist/src/agent/skills/skill-manage-ops.js +1 -1
  111. package/dist/src/agent/skills/skill-manager.js +1 -1
  112. package/dist/src/agent/tools/create-share-tool.d.ts +27 -0
  113. package/dist/src/agent/tools/create-share-tool.js +237 -0
  114. package/dist/src/agent/tools/create-share-tool.js.map +1 -0
  115. package/dist/src/agent/tools/dreaming-tool.js +1 -1
  116. package/dist/src/agent/tools/factory.js +35 -1
  117. package/dist/src/agent/tools/factory.js.map +1 -1
  118. package/dist/src/agent/tools/image-generate-tool.js +1 -1
  119. package/dist/src/agent/tools/index.d.ts +2 -0
  120. package/dist/src/agent/tools/index.js +3 -1
  121. package/dist/src/agent/tools/send-media.js +1 -1
  122. package/dist/src/agent/tools/skill-manage-tool.js +1 -1
  123. package/dist/src/agent/tools/workflow-tool.d.ts +41 -0
  124. package/dist/src/agent/tools/workflow-tool.js +271 -0
  125. package/dist/src/agent/tools/workflow-tool.js.map +1 -0
  126. package/dist/src/agent/tools/write.js +1 -1
  127. package/dist/src/agent/workflow/builtins/audit-repo.d.ts +9 -0
  128. package/dist/src/agent/workflow/builtins/audit-repo.js +115 -0
  129. package/dist/src/agent/workflow/builtins/audit-repo.js.map +1 -0
  130. package/dist/src/agent/workflow/builtins/index.d.ts +15 -0
  131. package/dist/src/agent/workflow/builtins/index.js +28 -0
  132. package/dist/src/agent/workflow/builtins/index.js.map +1 -0
  133. package/dist/src/agent/workflow/builtins/multi-perspective-review.d.ts +9 -0
  134. package/dist/src/agent/workflow/builtins/multi-perspective-review.js +113 -0
  135. package/dist/src/agent/workflow/builtins/multi-perspective-review.js.map +1 -0
  136. package/dist/src/agent/workflow/builtins/research.d.ts +9 -0
  137. package/dist/src/agent/workflow/builtins/research.js +129 -0
  138. package/dist/src/agent/workflow/builtins/research.js.map +1 -0
  139. package/dist/src/agent/workflow/catalog.d.ts +51 -0
  140. package/dist/src/agent/workflow/catalog.js +155 -0
  141. package/dist/src/agent/workflow/catalog.js.map +1 -0
  142. package/dist/src/agent/workflow/channel-capability.d.ts +76 -0
  143. package/dist/src/agent/workflow/channel-capability.js +1 -0
  144. package/dist/src/agent/workflow/index.d.ts +11 -0
  145. package/dist/src/agent/workflow/index.js +10 -0
  146. package/dist/src/agent/workflow/last-run-memory.d.ts +42 -0
  147. package/dist/src/agent/workflow/last-run-memory.js +60 -0
  148. package/dist/src/agent/workflow/last-run-memory.js.map +1 -0
  149. package/dist/src/agent/workflow/parser.d.ts +20 -0
  150. package/dist/src/agent/workflow/parser.js +137 -0
  151. package/dist/src/agent/workflow/parser.js.map +1 -0
  152. package/dist/src/agent/workflow/progress-broker.d.ts +80 -0
  153. package/dist/src/agent/workflow/progress-broker.js +263 -0
  154. package/dist/src/agent/workflow/progress-broker.js.map +1 -0
  155. package/dist/src/agent/workflow/runtime.d.ts +31 -0
  156. package/dist/src/agent/workflow/runtime.js +301 -0
  157. package/dist/src/agent/workflow/runtime.js.map +1 -0
  158. package/dist/src/agent/workflow/snapshot.d.ts +18 -0
  159. package/dist/src/agent/workflow/snapshot.js +144 -0
  160. package/dist/src/agent/workflow/snapshot.js.map +1 -0
  161. package/dist/src/agent/workflow/structured-output-tool.d.ts +33 -0
  162. package/dist/src/agent/workflow/structured-output-tool.js +58 -0
  163. package/dist/src/agent/workflow/structured-output-tool.js.map +1 -0
  164. package/dist/src/agent/workflow/subagent-runner.d.ts +42 -0
  165. package/dist/src/agent/workflow/subagent-runner.js +104 -0
  166. package/dist/src/agent/workflow/subagent-runner.js.map +1 -0
  167. package/dist/src/agent/workflow/types.d.ts +137 -0
  168. package/dist/src/agent/workflow/types.js +1 -0
  169. package/dist/src/auth/credentials.js +3 -3
  170. package/dist/src/auth/profiles/store.js +1 -1
  171. package/dist/src/auth/sync-provider-auth.js +1 -1
  172. package/dist/src/browser/cache-dir-policy.js +1 -1
  173. package/dist/src/browser/cdp-local-launcher.js +2 -2
  174. package/dist/src/browser/providers/browser-ext-install.js +4 -4
  175. package/dist/src/browser/providers/cloakbrowser.js +4 -4
  176. package/dist/src/browser/providers/playwright-doctor.js +1 -1
  177. package/dist/src/browser/stealth.js +1 -1
  178. package/dist/src/channels/attachments/inbound-persist.js +1 -1
  179. package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
  180. package/dist/src/channels/outbound/persist-store.js +1 -1
  181. package/dist/src/channels/pairing/allow-from-file.js +1 -1
  182. package/dist/src/channels/pairing/pairing-store.js +2 -2
  183. package/dist/src/chat-commands/builtins/config.js +2 -2
  184. package/dist/src/chat-commands/builtins/model.js +40 -23
  185. package/dist/src/chat-commands/builtins/model.js.map +1 -1
  186. package/dist/src/chat-commands/builtins/system.js +30 -15
  187. package/dist/src/chat-commands/builtins/system.js.map +1 -1
  188. package/dist/src/chat-commands/builtins/workflow.d.ts +18 -0
  189. package/dist/src/chat-commands/builtins/workflow.js +167 -0
  190. package/dist/src/chat-commands/builtins/workflow.js.map +1 -0
  191. package/dist/src/chat-commands/context.js +1 -1
  192. package/dist/src/chat-commands/format-output.d.ts +28 -0
  193. package/dist/src/chat-commands/format-output.js +45 -0
  194. package/dist/src/chat-commands/format-output.js.map +1 -0
  195. package/dist/src/chat-commands/index.d.ts +1 -0
  196. package/dist/src/chat-commands/index.js +3 -1
  197. package/dist/src/chat-commands/index.js.map +1 -1
  198. package/dist/src/cli/command-catalog.js +110 -8
  199. package/dist/src/cli/command-catalog.js.map +1 -1
  200. package/dist/src/cli/command-loaders.js +2 -0
  201. package/dist/src/cli/command-loaders.js.map +1 -1
  202. package/dist/src/cli/command-manifest.js +9 -1
  203. package/dist/src/cli/command-manifest.js.map +1 -1
  204. package/dist/src/cli/commands/config.js +71 -20
  205. package/dist/src/cli/commands/config.js.map +1 -1
  206. package/dist/src/cli/commands/cron-cli.d.ts +2 -0
  207. package/dist/src/cli/commands/cron-cli.js +15 -0
  208. package/dist/src/cli/commands/cron-cli.js.map +1 -0
  209. package/dist/src/cli/commands/cron.d.ts +4 -1
  210. package/dist/src/cli/commands/cron.js +76 -41
  211. package/dist/src/cli/commands/cron.js.map +1 -1
  212. package/dist/src/cli/commands/doctor/checks/channel-config.js +1 -1
  213. package/dist/src/cli/commands/doctor/checks/channel-config.js.map +1 -1
  214. package/dist/src/cli/commands/doctor/checks/config-health.js +2 -2
  215. package/dist/src/cli/commands/doctor/checks/config-health.js.map +1 -1
  216. package/dist/src/cli/commands/doctor/checks/cron-health.js +1 -1
  217. package/dist/src/cli/commands/doctor/checks/cron-health.js.map +1 -1
  218. package/dist/src/cli/commands/doctor/checks/gateway-health.js +2 -2
  219. package/dist/src/cli/commands/doctor/checks/gateway-health.js.map +1 -1
  220. package/dist/src/cli/commands/doctor/checks/gateway-service.js +2 -2
  221. package/dist/src/cli/commands/doctor/checks/gateway-service.js.map +1 -1
  222. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  223. package/dist/src/cli/commands/doctor/checks/session-integrity.js +1 -1
  224. package/dist/src/cli/commands/doctor/checks/state-integrity.js +2 -2
  225. package/dist/src/cli/commands/doctor/checks/state-integrity.js.map +1 -1
  226. package/dist/src/cli/commands/doctor/checks/workspace-status.js +4 -4
  227. package/dist/src/cli/commands/doctor/checks/workspace-status.js.map +1 -1
  228. package/dist/src/cli/commands/extension-dev.js +1 -1
  229. package/dist/src/cli/commands/extension-marketplace.js +1 -1
  230. package/dist/src/cli/commands/extension-pack.js +1 -1
  231. package/dist/src/cli/commands/gateway/index.d.ts +1 -1
  232. package/dist/src/cli/commands/gateway/index.js +2 -2
  233. package/dist/src/cli/commands/gateway/lifecycle.js +10 -4
  234. package/dist/src/cli/commands/gateway/lifecycle.js.map +1 -1
  235. package/dist/src/cli/commands/gateway/service.d.ts +4 -0
  236. package/dist/src/cli/commands/gateway/service.js +17 -2
  237. package/dist/src/cli/commands/gateway/service.js.map +1 -1
  238. package/dist/src/cli/commands/gateway/shared.js +1 -1
  239. package/dist/src/cli/commands/gateway/subcommands.js +1 -4
  240. package/dist/src/cli/commands/gateway/subcommands.js.map +1 -1
  241. package/dist/src/cli/commands/image.js +1 -1
  242. package/dist/src/cli/commands/init.js +31 -4
  243. package/dist/src/cli/commands/init.js.map +1 -1
  244. package/dist/src/cli/commands/models.d.ts +4 -1
  245. package/dist/src/cli/commands/models.js +86 -74
  246. package/dist/src/cli/commands/models.js.map +1 -1
  247. package/dist/src/cli/commands/onboard.js +4 -2
  248. package/dist/src/cli/commands/onboard.js.map +1 -1
  249. package/dist/src/cli/commands/profile.d.ts +3 -5
  250. package/dist/src/cli/commands/profile.js +31 -31
  251. package/dist/src/cli/commands/profile.js.map +1 -1
  252. package/dist/src/cli/commands/setup.js +6 -1
  253. package/dist/src/cli/commands/setup.js.map +1 -1
  254. package/dist/src/cli/commands/tunnel.js +2 -2
  255. package/dist/src/cli/gateway-run-argv.js +15 -5
  256. package/dist/src/cli/gateway-run-argv.js.map +1 -1
  257. package/dist/src/cli/utils/gateway-client.js +1 -1
  258. package/dist/src/cli/utils/init-workspace-core.js +2 -2
  259. package/dist/src/config/agent-profile.js +1 -1
  260. package/dist/src/config/gateway-bind.js +1 -1
  261. package/dist/src/config/index.js +5 -5
  262. package/dist/src/config/loader.js +2 -2
  263. package/dist/src/config/models-json.js +2 -2
  264. package/dist/src/config/paths-state.js +1 -1
  265. package/dist/src/config/profile.js +2 -2
  266. package/dist/src/config/public-url.d.ts +28 -0
  267. package/dist/src/config/public-url.js +103 -0
  268. package/dist/src/config/public-url.js.map +1 -0
  269. package/dist/src/config/schema.d.ts +82 -0
  270. package/dist/src/config/schema.js +130 -1
  271. package/dist/src/config/schema.js.map +1 -1
  272. package/dist/src/config/workspace-path.js +1 -1
  273. package/dist/src/cron/executor.js +2 -2
  274. package/dist/src/cron/persistence.js +1 -1
  275. package/dist/src/cron/run-log-store.js +1 -1
  276. package/dist/src/daemon/constants.js +1 -1
  277. package/dist/src/daemon/install-plan.js +3 -3
  278. package/dist/src/daemon/install-plan.js.map +1 -1
  279. package/dist/src/daemon/launchd.js +2 -2
  280. package/dist/src/daemon/schtasks.js +38 -1
  281. package/dist/src/daemon/schtasks.js.map +1 -1
  282. package/dist/src/daemon/systemd.js +2 -2
  283. package/dist/src/extensions/bundle-mcp.js +1 -1
  284. package/dist/src/extensions/discover-extensions.js +1 -1
  285. package/dist/src/extensions/health.js +1 -1
  286. package/dist/src/extensions/loader.js +1 -1
  287. package/dist/src/extensions/lockfile.js +2 -2
  288. package/dist/src/gateway/agents-admin.js +2 -2
  289. package/dist/src/gateway/file-path-classifier.js +2 -2
  290. package/dist/src/gateway/heartbeat/service.js +1 -1
  291. package/dist/src/gateway/hono/app.js +33 -2
  292. package/dist/src/gateway/hono/app.js.map +1 -1
  293. package/dist/src/gateway/hono/lib/config-payload.js +1 -1
  294. package/dist/src/gateway/hono/lib/extension-store.js +2 -2
  295. package/dist/src/gateway/hono/lib/static-ui.js +2 -2
  296. package/dist/src/gateway/hono/oauth.js +1 -1
  297. package/dist/src/gateway/hono/routes/agents.js +1 -1
  298. package/dist/src/gateway/hono/routes/auth-registry-extensions.js +1 -1
  299. package/dist/src/gateway/hono/routes/config-patch/misc.js +1 -1
  300. package/dist/src/gateway/hono/routes/dreaming.js +1 -1
  301. package/dist/src/gateway/hono/routes/host-fs.js +2 -2
  302. package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
  303. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  304. package/dist/src/gateway/hono/routes/models.js +1 -1
  305. package/dist/src/gateway/hono/routes/shares.js +631 -34
  306. package/dist/src/gateway/hono/routes/shares.js.map +1 -1
  307. package/dist/src/gateway/hono/routes/site-shares.d.ts +3 -0
  308. package/dist/src/gateway/hono/routes/site-shares.js +228 -0
  309. package/dist/src/gateway/hono/routes/site-shares.js.map +1 -0
  310. package/dist/src/gateway/hono/routes/tunnel.js +97 -8
  311. package/dist/src/gateway/hono/routes/tunnel.js.map +1 -1
  312. package/dist/src/gateway/hono/routes/workspace.js +5 -5
  313. package/dist/src/gateway/hono/sse.js +2 -2
  314. package/dist/src/gateway/host.d.ts +3 -1
  315. package/dist/src/gateway/host.js +3 -1
  316. package/dist/src/gateway/host.js.map +1 -1
  317. package/dist/src/gateway/lock.js +3 -3
  318. package/dist/src/gateway/ports.d.ts +6 -0
  319. package/dist/src/gateway/ports.js +38 -2
  320. package/dist/src/gateway/ports.js.map +1 -1
  321. package/dist/src/gateway/public-url.d.ts +8 -0
  322. package/dist/src/gateway/public-url.js +10 -0
  323. package/dist/src/gateway/public-url.js.map +1 -0
  324. package/dist/src/gateway/security/origin-check.d.ts +9 -1
  325. package/dist/src/gateway/security/origin-check.js +4 -0
  326. package/dist/src/gateway/security/origin-check.js.map +1 -1
  327. package/dist/src/gateway/server.js +15 -0
  328. package/dist/src/gateway/server.js.map +1 -1
  329. package/dist/src/gateway/service/agent-runner.js +2 -2
  330. package/dist/src/gateway/service/marketplace-service.js +2 -2
  331. package/dist/src/gateway/service/run-gateway-agent.js +2 -2
  332. package/dist/src/gateway/service.js +3 -2
  333. package/dist/src/gateway/service.js.map +1 -1
  334. package/dist/src/gateway/workspace-fs-file-list.js +1 -1
  335. package/dist/src/heartbeat/index.js +1 -1
  336. package/dist/src/i18n/goals-bundle.js +1 -1
  337. package/dist/src/i18n/index.d.ts +1 -0
  338. package/dist/src/i18n/index.js +2 -1
  339. package/dist/src/i18n/locales/share-tool.en.js +15 -0
  340. package/dist/src/i18n/locales/share-tool.en.js.map +1 -0
  341. package/dist/src/i18n/locales/share-tool.zh.js +15 -0
  342. package/dist/src/i18n/locales/share-tool.zh.js.map +1 -0
  343. package/dist/src/i18n/share-tool-bundle.d.ts +20 -0
  344. package/dist/src/i18n/share-tool-bundle.js +56 -0
  345. package/dist/src/i18n/share-tool-bundle.js.map +1 -0
  346. package/dist/src/infra/gateway-processes.js +1 -0
  347. package/dist/src/infra/gateway-processes.js.map +1 -1
  348. package/dist/src/infra/restart.js +2 -2
  349. package/dist/src/infra/update-check.js +1 -1
  350. package/dist/src/infra/update-lock.js +3 -3
  351. package/dist/src/infra/update-runner.js +1 -1
  352. package/dist/src/infra/update-startup.js +2 -2
  353. package/dist/src/infra/write-file-atomic.js +2 -2
  354. package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
  355. package/dist/src/providers/index.js +2 -2
  356. package/dist/src/providers/model-registry.js +1 -1
  357. package/dist/src/session/config-store.js +2 -2
  358. package/dist/src/session/parity/jsonl-transcript-io.js +2 -2
  359. package/dist/src/session/parity/sessions-json-file.js +1 -1
  360. package/dist/src/session/parity/transcript-file-lock.js +2 -2
  361. package/dist/src/session/parity/transcript-paths.js +1 -1
  362. package/dist/src/session/search-index-cache.js +1 -1
  363. package/dist/src/session/search-index.js +1 -1
  364. package/dist/src/session/session-title.js +3 -2
  365. package/dist/src/session/session-title.js.map +1 -1
  366. package/dist/src/session/store.js +5 -5
  367. package/dist/src/share/share-auto.d.ts +74 -0
  368. package/dist/src/share/share-auto.js +247 -0
  369. package/dist/src/share/share-auto.js.map +1 -0
  370. package/dist/src/share/share-config.js +63 -4
  371. package/dist/src/share/share-config.js.map +1 -1
  372. package/dist/src/share/share-landing.d.ts +28 -2
  373. package/dist/src/share/share-landing.js +155 -34
  374. package/dist/src/share/share-landing.js.map +1 -1
  375. package/dist/src/share/share-store.d.ts +48 -4
  376. package/dist/src/share/share-store.js +322 -51
  377. package/dist/src/share/share-store.js.map +1 -1
  378. package/dist/src/share/share-thumbnail.d.ts +35 -0
  379. package/dist/src/share/share-thumbnail.js +277 -0
  380. package/dist/src/share/share-thumbnail.js.map +1 -0
  381. package/dist/src/share/share-types.d.ts +68 -10
  382. package/dist/src/share/share-types.js +18 -1
  383. package/dist/src/share/share-types.js.map +1 -1
  384. package/dist/src/share/share-url.js +1 -1
  385. package/dist/src/share/share-zip.d.ts +35 -0
  386. package/dist/src/share/share-zip.js +303 -0
  387. package/dist/src/share/share-zip.js.map +1 -0
  388. package/dist/src/share/site-proxy.d.ts +35 -0
  389. package/dist/src/share/site-proxy.js +234 -0
  390. package/dist/src/share/site-proxy.js.map +1 -0
  391. package/dist/src/share/site-share-config.d.ts +11 -0
  392. package/dist/src/share/site-share-config.js +103 -0
  393. package/dist/src/share/site-share-config.js.map +1 -0
  394. package/dist/src/share/site-share-router.d.ts +23 -0
  395. package/dist/src/share/site-share-router.js +147 -0
  396. package/dist/src/share/site-share-router.js.map +1 -0
  397. package/dist/src/share/site-share-store.d.ts +53 -0
  398. package/dist/src/share/site-share-store.js +400 -0
  399. package/dist/src/share/site-share-store.js.map +1 -0
  400. package/dist/src/share/site-share-types.d.ts +103 -0
  401. package/dist/src/share/site-share-types.js +41 -0
  402. package/dist/src/share/site-share-types.js.map +1 -0
  403. package/dist/src/share/site-static-serve.d.ts +10 -0
  404. package/dist/src/share/site-static-serve.js +145 -0
  405. package/dist/src/share/site-static-serve.js.map +1 -0
  406. package/dist/src/tui/clipboard-image.js +3 -3
  407. package/dist/src/tui/theme-manager.js +1 -1
  408. package/dist/src/tui/tui-commands.js +18 -0
  409. package/dist/src/tui/tui-commands.js.map +1 -1
  410. package/dist/src/tui/tui-keybindings-file.js +1 -1
  411. package/dist/src/tui/tui-scoped-models.js +2 -2
  412. package/dist/src/tui/tui-settings.js +1 -1
  413. package/dist/src/tui/tui-workflow-slash.d.ts +32 -0
  414. package/dist/src/tui/tui-workflow-slash.js +63 -0
  415. package/dist/src/tui/tui-workflow-slash.js.map +1 -0
  416. package/dist/src/tui/tui.js +2 -2
  417. package/dist/src/tunnel/enable-lan-pairing.js +1 -1
  418. package/dist/src/tunnel/frpc-binary.js +3 -3
  419. package/dist/src/tunnel/frpc-config.js +1 -1
  420. package/dist/src/tunnel/frpc-extract.js +1 -1
  421. package/dist/src/tunnel/index.js +2 -2
  422. package/dist/src/tunnel/pair-context.d.ts +7 -1
  423. package/dist/src/tunnel/pair-context.js +25 -9
  424. package/dist/src/tunnel/pair-context.js.map +1 -1
  425. package/dist/src/tunnel/pair-url.d.ts +14 -1
  426. package/dist/src/tunnel/pair-url.js +14 -1
  427. package/dist/src/tunnel/pair-url.js.map +1 -1
  428. package/dist/src/tunnel/tunnel-service.js +2 -2
  429. package/dist/src/tunnel/tunnel-state.js +1 -1
  430. package/dist/src/utils/logger/audit.js +1 -1
  431. package/dist/src/utils/logger/log-store.js +1 -1
  432. package/dist/src/utils/logger/rotation.js +1 -1
  433. package/dist/src/voice/tts/audio.js +1 -1
  434. package/dist/src/voice/tts/providers/edge-speech.js +2 -2
  435. package/package.json +3 -2
  436. package/dist/gateway/static/root/assets/agents-CrpYTHJS.js +0 -222
  437. package/dist/gateway/static/root/assets/apps-page-1mcKh5Rh.js +0 -1
  438. package/dist/gateway/static/root/assets/button-KafIU8dx.js +0 -1
  439. package/dist/gateway/static/root/assets/channels-settings-zd6QNKPx.js +0 -1
  440. package/dist/gateway/static/root/assets/channels-status-swr-uRAuhiUo.js +0 -8
  441. package/dist/gateway/static/root/assets/cron-api-O2Q_ruV6.js +0 -1
  442. package/dist/gateway/static/root/assets/cron-page-By09AQD-.js +0 -1
  443. package/dist/gateway/static/root/assets/dist-C57OMHW8.js +0 -48
  444. package/dist/gateway/static/root/assets/extension-page-C-Ed5ZmP.js +0 -1
  445. package/dist/gateway/static/root/assets/extension-settings-page-raLux7E7.js +0 -1
  446. package/dist/gateway/static/root/assets/fetch-2iRFmd3n.js +0 -3
  447. package/dist/gateway/static/root/assets/heartbeat-config-api-BVl5VHvL.js +0 -1
  448. package/dist/gateway/static/root/assets/index-BuFldCsB.css +0 -1
  449. package/dist/gateway/static/root/assets/index-Y-iqo-gL.js +0 -4693
  450. package/dist/gateway/static/root/assets/logs-page-BdH2n7ZW.js +0 -1
  451. package/dist/gateway/static/root/assets/sessions-page-Vpchzdp-.js +0 -1
  452. package/dist/gateway/static/root/assets/settings-form-section-Kk1yAGBl.js +0 -1
  453. package/dist/gateway/static/root/assets/settings-page-KBm0u6Dz.js +0 -3
  454. package/dist/gateway/static/root/assets/skills-page-BjeXXaOn.js +0 -2
  455. package/dist/gateway/static/root/assets/theme-store-D01dJt95.js +0 -1
  456. package/dist/gateway/static/root/assets/utils-DpTxN4AF.js +0 -1
  457. package/dist/gateway/static/root/assets/voice-api-key-field-CwO8Cf01.js +0 -1
@@ -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 '../../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":";;;;;;;;;;;;;;AAoBA,SAAS,mBAAmB,SAAyB;CACnD,MAAM,UAAU,QAAQ,cAAc;AACtC,QAAO;EACL,aAAa,4BAA4B,QAAQ,cAAc;EAC/D,aAAa,QAAQ,QAAQ;EAC9B;;AAGH,SAAS,mBAAmB,SAA+C;CACzE,MAAM,MAAO,QAAQ,cAAc,SAAqC;AACxE,KAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO,EAAE;AAC9C,QAAO;;AAGT,SAAS,iBAAiB,OAAuB;AAC/C,QAAO,WAAW,SAAS,CAAC,OAAO,OAAO,OAAO,CAAC,OAAO,MAAM,CAAC,MAAM,GAAG,GAAG;;AAG9E,MAAM,qCAAqC;AAC3C,MAAM,kCAAkB,IAAI,KAAqB;AAEjD,SAAS,oBAAoB,OAAwB;CACnD,MAAM,UAAU,gBAAgB,IAAI,MAAM,IAAI;AAC9C,KAAI,WAAW,mCAAoC,QAAO;AAC1D,iBAAgB,IAAI,OAAO,UAAU,EAAE;AACvC,QAAO;;AAGT,SAAS,oBAAoB,OAAqB;CAChD,MAAM,UAAU,gBAAgB,IAAI,MAAM,IAAI;AAC9C,KAAI,WAAW,EACb,iBAAgB,OAAO,MAAM;KAE7B,iBAAgB,IAAI,OAAO,UAAU,EAAE;;AAM3C,SAAgB,0BAA0B,KAAW,SAA+B;CAClF,MAAM,QAAQ,cAAc,mBAAmB,QAAQ,CAAC;;AAGxD,KAAI,IAAI,aAAa,OAAO,MAAM;EAChC,MAAM,WAAW,uBAAuB,EAAE,MAAM,MAAc,EAAE,IAAI,OAAO,EAAE,IAAI,KAAA,GAAW,CAAC;EAC7F,MAAM,aAAa,wBAAwB,SAAS;AACpD,MAAI,CAAC,WAAW,SAAS;AACvB,KAAE,OAAO,eAAe,OAAO,KAAK,KAAK,WAAW,eAAe,IAAK,CAAC,CAAC;AAC1E,UAAO,EAAE,KAAK,qBAAqB,IAAI;;EAGzC,MAAM,QAAQ,EAAE,IAAI,MAAM,QAAQ;EAClC,MAAM,SAAS,MAAM,WAAW,MAAM;AAEtC,MAAI,CAAC,OACH,QAAO,EAAE,KAAK,uBAAuB,YAAY,EAAE,IAAI;EAGzD,MAAM,aAAa,MAAM,eAAe,OAAO;AAC/C,MAAI,CAAC,WAAW,OAAO;AACrB,iBACE,uBACA;IAAE,SAAS,OAAO;IAAI,aAAa,MAAM,MAAM,GAAG,EAAE;IAAE,QAAQ,WAAW;IAAQ;IAAU,EAC3F,wBAAwB,WAAW,SACpC;AACD,UAAO,EAAE,KAAK,uBAAuB,WAAW,OAA6B,EAAE,IAAI;;AAIrF,MAAI,EAAE,IAAI,MAAM,KAAK,KAAK,IACxB,QAAO,eAAe,GAAG,OAAO,QAAQ,SAAS;AAInD,MAAI,EAAE,IAAI,MAAM,SAAS,KAAK;OAExB;IADU,GAAG;IAAuB,GAAG,mBAAmB,QAAQ;IAC/D,CAAC,mBAAmB,SAAS,OAAO,SAAS,CAClD,QAAO,eAAe,GAAG,OAAO,QAAQ,UAAU,KAAK;;EAI3D,MAAM,eAAe,MAAM,MAAM;AACjC,SAAO,EAAE,KAAK,uBAAuB,QAAQ,aAAa,CAAC;GAC3D;;AAGF,KAAI,KAAK,sBAAsB,OAAO,MAAM;EAC1C,MAAM,WAAW,uBAAuB,EAAE,MAAM,MAAc,EAAE,IAAI,OAAO,EAAE,IAAI,KAAA,GAAW,CAAC;EAC7F,MAAM,aAAa,wBAAwB,SAAS;AACpD,MAAI,CAAC,WAAW,SAAS;AACvB,KAAE,OAAO,eAAe,OAAO,KAAK,KAAK,WAAW,eAAe,IAAK,CAAC,CAAC;AAC1E,UAAO,EAAE,KAAK,qBAAqB,IAAI;;EAGzC,MAAM,QAAQ,EAAE,IAAI,MAAM,QAAQ;EAClC,MAAM,SAAS,MAAM,WAAW,MAAM;AAEtC,MAAI,CAAC,OACH,QAAO,EAAE,KAAK,uBAAuB,YAAY,EAAE,IAAI;EAGzD,MAAM,aAAa,MAAM,eAAe,OAAO;AAC/C,MAAI,CAAC,WAAW,OAAO;AACrB,iBACE,uBACA;IAAE,SAAS,OAAO;IAAI,aAAa,MAAM,MAAM,GAAG,EAAE;IAAE,QAAQ,WAAW;IAAQ;IAAU,EAC3F,0BAA0B,WAAW,SACtC;AACD,UAAO,EAAE,KAAK,uBAAuB,WAAW,OAA6B,EAAE,IAAI;;AAGrF,SAAO,eAAe,GAAG,OAAO,QAAQ,SAAS;GACjD;;AAGF,KAAI,IAAI,kBAAkB,OAAO,MAAM;EAErC,MAAM,aAAa,wBADF,uBAAuB,EAAE,MAAM,MAAc,EAAE,IAAI,OAAO,EAAE,IAAI,KAAA,GAAW,CACzC,CAAC;AACpD,MAAI,CAAC,WAAW,SAAS;AACvB,KAAE,OAAO,eAAe,OAAO,KAAK,KAAK,WAAW,eAAe,IAAK,CAAC,CAAC;AAC1E,UAAO,EAAE,KAAK,qBAAqB,IAAI;;EAGzC,MAAM,QAAQ,EAAE,IAAI,MAAM,QAAQ;EAClC,MAAM,SAAS,MAAM,WAAW,MAAM;AACtC,MAAI,CAAC,OACH,QAAO,EAAE,KAAK,EAAE,OAAO,OAAO,EAAE,IAAI;EAGtC,MAAM,aAAa,MAAM,eAAe,OAAO;EAC/C,MAAM,iBAAiB,OAAO,aAAa,OAAO,KAAK,IAAI,GAAG,OAAO,WAAW,OAAO,UAAU,GAAG;AAEpG,SAAO,EAAE,KAAK;GACZ,UAAU,OAAO;GACjB,UAAU,OAAO;GACjB,UAAU,OAAO;GACjB,aAAa,OAAO,eAAe;GACnC,WAAW,OAAO;GAClB;GACA,OAAO,WAAW;GACnB,CAAC;GACF;;AAGF,KAAI,GAAG,QAAQ,aAAa,OAAO,MAAM;EACvC,MAAM,QAAQ,EAAE,IAAI,MAAM,QAAQ;EAClC,MAAM,SAAS,MAAM,WAAW,MAAM;AACtC,MAAI,CAAC,OAAQ,QAAO,EAAE,KAAK,MAAM,IAAI;EACrC,MAAM,aAAa,MAAM,eAAe,OAAO;AAC/C,SAAO,EAAE,KAAK,MAAM,WAAW,QAAQ,MAAM,IAAI;GACjD;;AAKJ,SAAgB,oBAAoB,eAAqB,MAAoC;CAC3F,MAAM,EAAE,YAAY;CACpB,MAAM,QAAQ,cAAc,mBAAmB,QAAQ,CAAC;;AAGxD,eAAc,KAAK,eAAe,OAAO,MAAM;EAC7C,MAAM,eAAe,aAAa,EAAE,eAAe,EAAE,IAAI,OAAO,gBAAgB,IAAI,KAAA,GAAW,CAAC;AAChG,MAAI,CAAC,aAAc,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,kBAAkB;GAAE,EAAE,IAAI;EAE1F,IAAI;AACJ,MAAI;AACF,UAAQ,MAAM,EAAE,IAAI,MAAM;UACpB;AACN,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO,EAAE,SAAS,gBAAgB;IAAE,EAAE,IAAI;;EAGvE,MAAM,OAAO,OAAO,KAAK,SAAS,WAAW,KAAK,KAAK,MAAM,GAAG;AAChE,MAAI,CAAC,KACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,gBAAgB;GAAE,EAAE,IAAI;EAGvE,MAAM,aAAa,OAAO,KAAK,eAAe,WAAW,KAAK,WAAW,MAAM,GAAG,KAAA;EAClF,MAAM,UAAU,OAAO,KAAK,YAAY,WAAW,KAAK,QAAQ,MAAM,GAAG,KAAA;EAGzE,MAAM,gBAAgB,MAAM,6BAA6B,SAAS,YAAY,QAAQ;AACtF,MAAI,CAAC,cACH,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,4BAA4B;GAAE,EAAE,IAAI;EAGnF,MAAM,QAAQ,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,KAAA;EAC5D,MAAM,WAAW,KAAK,aAAa,OAAO,OAAO,OAAO,KAAK,aAAa,WAAW,KAAK,WAAW,KAAA;EACrG,MAAM,cAAc,OAAO,KAAK,gBAAgB,WAAW,KAAK,YAAY,MAAM,IAAI,KAAA,IAAY,KAAA;AAElG,MAAI;AACF,SAAM,aAAa,mBAAmB,QAAQ,CAAC;GAC/C,MAAM,SAAS,MAAM,MAAM,OAAO;IAChC;IACA;IACA;IACA;IACA;IACA;IACA;IACA,kBAAkB,iBAAiB,aAAa;IACjD,CAAC;GAEF,MAAM,SAAS,mBAAmB,QAAQ;GAC1C,MAAM,WAAW,gBAAgB,OAAO,OAAO,OAAO;AAEtD,UAAO,EAAE,KAAK;IACZ,IAAI;IACJ,SAAS;KACP,IAAI,OAAO;KACX,OAAO,OAAO;KACd,UAAU,SAAS;KACnB,QAAQ,SAAS;KACjB,cAAc,SAAS;KACvB,kBAAkB,SAAS;KAC3B,WAAW,OAAO;KAClB,UAAU,OAAO;KACjB,UAAU,OAAO;KACjB,UAAU,OAAO;KAClB;IACF,EAAE,IAAI;WACA,KAAK;GACZ,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAChE,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO,EAAE,SAAS;IAAE,EAAE,IAAI;;GAEvD;;AAGF,eAAc,IAAI,eAAe,OAAO,MAAM;AAC5C,QAAM,aAAa,mBAAmB,QAAQ,CAAC;EAC/C,MAAM,SAAS,MAAM,cAAc;EACnC,MAAM,SAAS,mBAAmB,QAAQ;EAC1C,MAAM,MAAM,KAAK,KAAK;EAEtB,MAAM,QAAQ,OAAO,KAAK,MAAM;GAC9B,MAAM,WAAW,gBAAgB,EAAE,OAAO,OAAO;GACjD,MAAM,UAAU,OAAO,IAAI,KAAK,EAAE,UAAU,CAAC,SAAS;AACtD,UAAO;IACL,IAAI,EAAE;IACN,UAAU,EAAE;IACZ,uBAAuB,EAAE;IACzB,UAAU,SAAS;IACnB,QAAQ,SAAS;IACjB,cAAc,SAAS;IACvB,WAAW,EAAE;IACb,WAAW,EAAE;IACb,WAAW,EAAE;IACb,UAAU,EAAE;IACZ,SAAS,EAAE;IACX;IACA,aAAa,EAAE,eAAe;IAC9B,UAAU,EAAE;IACZ,UAAU,EAAE;IACb;IACD;AAEF,SAAO,EAAE,KAAK;GAAE,IAAI;GAAM,SAAS,EAAE,QAAQ,OAAO;GAAE,CAAC;GACvD;;AAGF,eAAc,IAAI,mBAAmB,OAAO,MAAM;EAChD,MAAM,KAAK,EAAE,IAAI,MAAM,KAAK;EAC5B,MAAM,SAAS,MAAM,QAAQ,GAAG;AAChC,MAAI,CAAC,OAAQ,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,aAAa;GAAE,EAAE,IAAI;EAE/E,MAAM,SAAS,mBAAmB,QAAQ;EAC1C,MAAM,WAAW,gBAAgB,OAAO,OAAO,OAAO;EACtD,MAAM,UAAU,KAAK,KAAK,IAAI,IAAI,KAAK,OAAO,UAAU,CAAC,SAAS;AAElE,SAAO,EAAE,KAAK;GACZ,IAAI;GACJ,SAAS;IACP,GAAG;IACH,OAAO,KAAA;IACP,UAAU,SAAS;IACnB,QAAQ,SAAS;IACjB,cAAc,SAAS;IACvB;IACD;GACF,CAAC;GACF;;AAGF,eAAc,OAAO,mBAAmB,OAAO,MAAM;EACnD,MAAM,KAAK,EAAE,IAAI,MAAM,KAAK;AAE5B,MAAI,CADY,MAAM,OAAO,GACjB,CAAE,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,aAAa;GAAE,EAAE,IAAI;AAChF,SAAO,EAAE,KAAK,EAAE,IAAI,MAAM,CAAC;GAC3B;;AAGF,eAAc,OAAO,eAAe,OAAO,MAAM;EAC/C,IAAI,OAAgC,EAAE;AACtC,MAAI;AACF,UAAQ,MAAM,EAAE,IAAI,MAAM;UACpB;AAIR,MAAI,KAAK,YAAY,MAAM;GACzB,MAAM,QAAQ,MAAM,eAAe;AACnC,UAAO,EAAE,KAAK;IAAE,IAAI;IAAM,SAAS,EAAE,cAAc,OAAO;IAAE,CAAC;;EAG/D,MAAM,MAAM,MAAM,QAAQ,KAAK,IAAI,GAAI,KAAK,IAAiB,QAAQ,MAAM,OAAO,MAAM,SAAS,GAAG,EAAE;AACtG,MAAI,IAAI,WAAW,EACjB,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,sCAAsC;GAAE,EAAE,IAAI;EAE7F,MAAM,QAAQ,MAAM,WAAW,IAAI;AACnC,SAAO,EAAE,KAAK;GAAE,IAAI;GAAM,SAAS,EAAE,cAAc,OAAO;GAAE,CAAC;GAC7D;;AAGF,eAAc,MAAM,mBAAmB,OAAO,MAAM;EAClD,MAAM,KAAK,EAAE,IAAI,MAAM,KAAK;EAC5B,IAAI;AACJ,MAAI;AACF,UAAQ,MAAM,EAAE,IAAI,MAAM;UACpB;AACN,UAAO,EAAE,KAAK;IAAE,IAAI;IAAO,OAAO,EAAE,SAAS,gBAAgB;IAAE,EAAE,IAAI;;EAGvE,MAAM,QAA4D,EAAE;AACpE,MAAI,OAAO,KAAK,gBAAgB,SAAU,OAAM,cAAc,KAAK;AACnE,MAAI,KAAK,aAAa,QAAQ,OAAO,KAAK,aAAa,SAAU,OAAM,WAAW,KAAK;EAEvF,MAAM,UAAU,MAAM,OAAO,IAAI,MAAM;AACvC,MAAI,CAAC,QAAS,QAAO,EAAE,KAAK;GAAE,IAAI;GAAO,OAAO,EAAE,SAAS,aAAa;GAAE,EAAE,IAAI;EAEhF,MAAM,SAAS,mBAAmB,QAAQ;EAC1C,MAAM,WAAW,gBAAgB,QAAQ,OAAO,OAAO;AAEvD,SAAO,EAAE,KAAK;GACZ,IAAI;GACJ,SAAS;IACP,IAAI,QAAQ;IACZ,WAAW,QAAQ;IACnB,UAAU,QAAQ;IAClB,UAAU,SAAS;IACpB;GACF,CAAC;GACF;;AAKJ,eAAe,6BACb,SACA,YACA,SACwB;CACxB,MAAM,MAAM,QAAQ;AAEpB,KAAI,WACF,KAAI;AACF,SAAO,MAAM,QAAQ,SAAS,0BAA0B,WAAW;SAC7D;CAMV,MAAM,EAAE,qBAAqB,MAAM,OAAO;CAC1C,MAAM,EAAE,0BAA0B,kBAAkB,0BAA0B,MAAM,OAClF;AAGF,KAAI,QAEF,QAAO,yBAAyB,KADb,iBAAiB,QACW,CAAC;CAGlD,MAAM,OAAO,iBAAiB,IAAI;AAClC,KAAI,KAAM,QAAO;AAGjB,QAAO,yBAAyB,KADd,sBAAsB,IACM,CAAC;;AAGjD,eAAe,eACb,GACA,OACA,QACA,UACA,SAAS,OACU;AAEnB,KAAI,CAAC,oBAAoB,OAAO,MAAM,CACpC,QAAO,EAAE,KAAK,gDAAgD,IAAI;AAGpE,KAAI;EAEF,MAAM,YAAY,MAAM,MAAM,sBAAsB,OAAO;AAC3D,MAAI,CAAC,UAAU,OAAO;AACpB,iBACE,uBACA;IAAE,SAAS,OAAO;IAAI,aAAa,OAAO,MAAM,MAAM,GAAG,EAAE;IAAE,QAAQ,UAAU;IAAQ;IAAU,EACjG,sCAAsC,UAAU,SACjD;GACD,MAAM,EAAE,wBAAwB,WAAW,MAAM,OAAO;AACxD,UAAO,IAAI,SAAS,OAAO,UAAU,OAA6B,EAAE;IAClE,QAAQ;IACR,SAAS,EAAE,gBAAgB,4BAA4B;IACxD,CAAC;;AAIJ,QAAM,mBAAmB,OAAO,GAAG;AAEnC,gBACE,gBACA;GAAE,SAAS,OAAO;GAAI,aAAa,OAAO,MAAM,MAAM,GAAG,EAAE;GAAE;GAAU,WAAW,OAAO;GAAW,EACpG,qBAAqB,OAAO,WAC7B;EAGD,MAAM,WAAW,MAAM,KAAK,OAAO,aAAa;EAChD,MAAM,SAAS,iBAAiB,OAAO,aAAa;EACpD,MAAM,YAAY,SAAS,MAAM,OAAO;EAExC,MAAM,cAAc,SAAS,qBAAqB,mBAAmB,OAAO,SAAS,CAAC,KAAK,yBAAyB,mBAAmB,OAAO,SAAS,CAAC;AAExJ,SAAO,IAAI,SAAS,WAAW;GAC7B,QAAQ;GACR,SAAS;IACP,gBAAgB,OAAO;IACvB,uBAAuB;IACvB,kBAAkB,OAAO,SAAS,KAAK;IACvC,iBAAiB;IACjB,0BAA0B;IAC1B,mBAAmB;IACnB,mBAAmB;IACpB;GACF,CAAC;WACM;AACR,sBAAoB,OAAO,MAAM"}
1
+ {"version":3,"file":"shares.js","names":[],"sources":["../../../../../src/gateway/hono/routes/shares.ts"],"sourcesContent":["import type { Context, 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, shareResponseContentType } from '../../../share/share-store.js';\nimport { getSiteShareStore } from '../../../share/site-share-store.js';\nimport { resolveSiteShareConfig } from '../../../share/site-share-config.js';\nimport { resolveShareUrl } from '../../../share/share-url.js';\nimport { consumeSharePublicLimit } from '../../../share/share-rate-limit.js';\nimport { logShareAudit } from '../../../share/share-audit.js';\nimport {\n renderShareLandingPage,\n renderShareExpiredPage,\n renderFolderLandingPage,\n} from '../../../share/share-landing.js';\nimport type { ShareExpiredReason } from '../../../share/share-landing.js';\nimport type { ShareConfig, ShareRecord } from '../../../share/share-types.js';\nimport { resolveGatewayEffectiveHost } from '../../../config/gateway-bind.js';\nimport { SHARE_CONFIG_DEFAULTS } from '../../../share/share-types.js';\nimport { createZipStream, planDirectoryFiles } from '../../../share/share-zip.js';\nimport {\n audienceDefaults,\n cleanupStagedSite,\n decideShareKind,\n forgetStagedSite,\n makeDescription,\n makeTitle,\n probeShareTarget,\n rememberStagedSite,\n stageSingleHtmlAsSite,\n type ShareAudience,\n type ShareAutoMode,\n} from '../../../share/share-auto.js';\nimport {\n deleteThumbnail,\n placeholderSvg,\n readThumbnail,\n scheduleThumbnail,\n thumbnailContentType,\n thumbnailExists,\n} from '../../../share/share-thumbnail.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 thumbnailRenderContext(service: GatewayService) {\n const cfg = { ...SHARE_CONFIG_DEFAULTS, ...resolveShareConfig(service) };\n const port = service.currentConfig.gateway.port ?? 18790;\n // Always use loopback for the internal renderer — never the public tunnel URL.\n const internalBaseUrl = cfg.thumbnail.internalGatewayUrl ?? `http://127.0.0.1:${port}`;\n return { config: cfg.thumbnail, internalBaseUrl };\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>();\nconst activeZipStreams = 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) activeDownloads.delete(token);\n else activeDownloads.set(token, current - 1);\n}\n\nfunction acquireZipSlot(token: string, limit: number): boolean {\n const current = activeZipStreams.get(token) ?? 0;\n if (current >= limit) return false;\n activeZipStreams.set(token, current + 1);\n return true;\n}\n\nfunction releaseZipSlot(token: string): void {\n const current = activeZipStreams.get(token) ?? 0;\n if (current <= 1) activeZipStreams.delete(token);\n else activeZipStreams.set(token, current - 1);\n}\n\n/**\n * Whether the browser can render this MIME natively (when served with\n * `Content-Disposition: inline`). Honours the per-deployment whitelist so\n * admins can block specific types.\n */\nfunction isPreviewableInline(mime: string, whitelist: string[]): boolean {\n return whitelist.includes(mime);\n}\n\n/**\n * Whether the type benefits from being rendered through the SPA preview page\n * (e.g. markdown — the browser would otherwise show raw source). Independent\n * of the inline whitelist: SPA preview pulls the bytes via the share API and\n * renders client-side, so admins control reach via TTL/maxViews, not MIME.\n */\nfunction isRichSpaPreviewable(mime: string): boolean {\n return (\n mime === 'text/markdown' ||\n mime === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' ||\n mime === 'application/vnd.openxmlformats-officedocument.presentationml.presentation'\n );\n}\n\nfunction rfc5987ContentDisposition(disposition: 'inline' | 'attachment', fileName: string): string {\n const ascii = fileName.replace(/[^\\x20-\\x7e]/g, '_').replace(/\"/g, '');\n const utf8 = encodeURIComponent(fileName);\n return `${disposition}; filename=\"${ascii}\"; filename*=UTF-8''${utf8}`;\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 downloadCount. */\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 if (!record) return c.html(renderShareExpiredPage('not_found'), 404);\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 if (record.kind === 'directory') {\n return renderDirectoryLanding(c, store, service, record, token);\n }\n\n // File: support direct download / inline preview shortcuts\n if (c.req.query('dl') === '1') {\n return handleFileDownload(c, store, record, clientIp);\n }\n if (c.req.query('inline') === '1') {\n const cfg = { ...SHARE_CONFIG_DEFAULTS, ...resolveShareConfig(service) };\n if (cfg.inlinePreviewMimes.includes(record.mimeType)) {\n return handleFileDownload(c, store, record, clientIp, true);\n }\n }\n\n const downloadPath = `/s/${token}/download`;\n const cfg = { ...SHARE_CONFIG_DEFAULTS, ...resolveShareConfig(service) };\n const previewable = isPreviewableInline(record.mimeType, cfg.inlinePreviewMimes);\n const richPreviewable = isRichSpaPreviewable(record.mimeType);\n const og = buildLandingOg(service, record);\n return c.html(\n renderShareLandingPage(record, downloadPath, {\n inlineUrl: previewable ? `/s/${token}?inline=1` : null,\n previewUrl: richPreviewable ? `/#/share/${encodeURIComponent(token)}` : null,\n og,\n }),\n );\n });\n\n /** Single-file download — POST so unfurl/scrapers don't consume views. */\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 if (!record) return c.html(renderShareExpiredPage('not_found'), 404);\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 if (record.kind === 'directory') {\n return c.html(renderShareExpiredPage('not_found'), 404);\n }\n return handleFileDownload(c, store, record, clientIp);\n });\n\n /** Directory child file (GET — preview counts as download per product). */\n app.get('/s/:token/file', 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) return c.html(renderShareExpiredPage('not_found'), 404);\n if (record.kind !== 'directory') return c.html(renderShareExpiredPage('not_found'), 404);\n\n const validation = store.validateAccess(record);\n if (!validation.valid) {\n return c.html(renderShareExpiredPage(validation.reason as ShareExpiredReason), 410);\n }\n\n const relPath = c.req.query('path') ?? '';\n const inline = c.req.query('inline') === '1' || c.req.query('dl') !== '1';\n return handleDirectoryFile(c, store, service, record, relPath, clientIp, inline);\n });\n\n /** Directory JSON listing. */\n app.get('/s/:token/tree', 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) return c.json({ ok: false, error: { message: 'not_found' } }, 404);\n if (record.kind !== 'directory') return c.json({ ok: false, error: { message: 'not_directory' } }, 400);\n\n const validation = store.validateAccess(record);\n if (!validation.valid) {\n return c.json({ ok: false, error: { message: validation.reason } }, 410);\n }\n\n const path = c.req.query('path') ?? '';\n // Tree HTML browser pages: render landing for the sub-path\n if ((c.req.header('accept') ?? '').includes('text/html')) {\n return renderDirectoryLanding(c, store, service, record, token, path);\n }\n try {\n const listing = await store.listDirectory(record, path);\n return c.json({ ok: true, payload: listing });\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 /** Folder ZIP download (whole share or sub-path). */\n app.get('/s/:token/zip', 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) return c.html(renderShareExpiredPage('not_found'), 404);\n if (record.kind !== 'directory') return c.html(renderShareExpiredPage('not_found'), 404);\n\n const validation = store.validateAccess(record);\n if (!validation.valid) {\n return c.html(renderShareExpiredPage(validation.reason as ShareExpiredReason), 410);\n }\n\n return handleDirectoryZip(c, store, service, record, c.req.query('path') ?? '', clientIp);\n });\n\n /** Metadata (for link preview cards / unfurl). Does NOT consume views. */\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) return c.json({ valid: false }, 404);\n\n const validation = store.validateAccess(record);\n const remainingViews = record.maxViews !== null ? Math.max(0, record.maxViews - record.downloadCount) : null;\n\n return c.json({\n kind: record.kind,\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 directory: record.directory ?? null,\n });\n });\n\n /** HEAD check. */\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 /** Thumbnail (jpeg/png/svg) — does NOT consume views. */\n app.get('/s/:token/thumbnail', 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 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 if (!validation.valid) return c.body(null, 410);\n\n const cached = await readThumbnail(token);\n if (cached) {\n return new Response(cached, {\n status: 200,\n headers: {\n 'Content-Type': thumbnailContentType(cached),\n 'Cache-Control': 'public, max-age=300',\n 'X-Content-Type-Options': 'nosniff',\n },\n });\n }\n // Schedule and return a placeholder so social-card scrapers always get an image.\n scheduleThumbnail({ scope: 'file', token, recordId: record.id }, thumbnailRenderContext(service));\n const placeholder = placeholderSvg(record.fileName, record.kind === 'directory' ? 'folder' : 'file');\n return new Response(placeholder, {\n status: 200,\n headers: {\n 'Content-Type': 'image/svg+xml; charset=utf-8',\n 'Cache-Control': 'public, max-age=10',\n 'X-Content-Type-Options': 'nosniff',\n },\n });\n });\n\n app.on('HEAD', '/s/:token/thumbnail', 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 if (!validation.valid) return c.body(null, 410);\n const ready = await thumbnailExists(token);\n return c.body(null, ready ? 200 : 202);\n });\n\n /** Site-share thumbnail. Shape mirrors /s/:token/thumbnail. */\n app.get('/site/:token/thumbnail', 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 const token = c.req.param('token');\n const siteStore = getSiteShareStore(resolveSiteShareConfig(service));\n const record = siteStore.getByToken(token);\n if (!record) return c.body(null, 404);\n const validation = siteStore.validateAccess(record);\n if (!validation.valid) return c.body(null, 410);\n\n const cached = await readThumbnail(token);\n if (cached) {\n return new Response(cached, {\n status: 200,\n headers: {\n 'Content-Type': thumbnailContentType(cached),\n 'Cache-Control': 'public, max-age=300',\n 'X-Content-Type-Options': 'nosniff',\n },\n });\n }\n scheduleThumbnail({ scope: 'site', token, recordId: record.id }, thumbnailRenderContext(service));\n const placeholder = placeholderSvg(record.description ?? 'site share', 'html');\n return new Response(placeholder, {\n status: 200,\n headers: {\n 'Content-Type': 'image/svg+xml; charset=utf-8',\n 'Cache-Control': 'public, max-age=10',\n 'X-Content-Type-Options': 'nosniff',\n },\n });\n });\n\n app.on('HEAD', '/site/:token/thumbnail', async (c) => {\n const token = c.req.param('token');\n const ready = await thumbnailExists(token);\n return c.body(null, ready ? 200 : 202);\n });\n\n // Wire share-store cleanup → drop on-disk thumbnail file.\n store.setCleanupHook((rec) => {\n void deleteThumbnail(rec.token);\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 const siteStoreEager = getSiteShareStore(resolveSiteShareConfig(service));\n // Register once: when a site share is revoked / expires, drop its staging dir (if any).\n siteStoreEager.setCleanupHook((rec) => {\n const dir = forgetStagedSite(rec.id);\n if (dir) void cleanupStagedSite(dir);\n });\n\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) return c.json({ ok: false, error: { message: 'Missing path' } }, 400);\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 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 const kind = body.kind === 'directory' || body.kind === 'file' ? body.kind : undefined;\n const directoryMode = body.directoryMode === 'zip-only' ? 'zip-only' : body.directoryMode === 'browse' ? 'browse' : undefined;\n const followSymlinks = body.followSymlinks === true;\n const maxFileCount = typeof body.maxFileCount === 'number' ? body.maxFileCount : undefined;\n const maxFolderSize = typeof body.maxFolderSize === 'number' ? body.maxFolderSize : undefined;\n const maxDepth = typeof body.maxDepth === 'number' ? body.maxDepth : 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 kind,\n directoryMode,\n followSymlinks,\n maxFileCount,\n maxFolderSize,\n maxDepth,\n });\n\n const urlCtx = getShareUrlContext(service);\n const resolved = resolveShareUrl(record.token, urlCtx);\n\n return c.json(\n {\n ok: true,\n payload: {\n id: record.id,\n token: record.token,\n kind: record.kind,\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 directory: record.directory ?? null,\n },\n },\n 201,\n );\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 /**\n * Smart share — picks file vs site, fills sensible defaults, returns the\n * payload the mobile share-sheet needs (title, description, thumbnailUrl,\n * reachability) in a single round-trip.\n */\n authenticated.post('/api/shares/auto', 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) return c.json({ ok: false, error: { message: 'Missing path' } }, 400);\n const sessionKey = typeof body.sessionKey === 'string' ? body.sessionKey.trim() : undefined;\n const agentId = typeof body.agentId === 'string' ? body.agentId.trim() : undefined;\n const mode = (typeof body.mode === 'string' && ['auto', 'force-file', 'force-site', 'force-zip'].includes(body.mode))\n ? (body.mode as ShareAutoMode)\n : 'auto';\n const audience: ShareAudience | undefined =\n body.audience === 'friend' || body.audience === 'colleague' || body.audience === 'public'\n ? body.audience\n : undefined;\n const title = typeof body.title === 'string' ? body.title : undefined;\n const description = typeof body.description === 'string' ? body.description : undefined;\n const ttlOverride = typeof body.ttlMs === 'number' ? body.ttlMs : undefined;\n const maxViewsOverride =\n body.maxViews === null ? null : typeof body.maxViews === 'number' ? body.maxViews : undefined;\n const wantThumbnail = body.thumbnail !== false;\n\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 let probe;\n try {\n probe = await probeShareTarget(workspaceRoot, path);\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 let decision;\n try {\n decision = decideShareKind(probe, mode);\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 const defaults = audienceDefaults(audience);\n const ttlMs = ttlOverride ?? defaults.ttlMs;\n const maxViews = maxViewsOverride !== undefined ? maxViewsOverride : defaults.maxViews;\n const tokenHash = hashGatewayToken(gatewayToken);\n const urlCtx = getShareUrlContext(service);\n\n try {\n if (decision.kind === 'site') {\n const siteStore = getSiteShareStore(resolveSiteShareConfig(service));\n // Single HTML file → stage into a temp dir as index.html so it can be\n // served as a site (recipient lands on a rendered page, not the\n // file-landing). The staging dir is auto-cleaned on revoke/expire.\n let sitePath = path;\n let stagedDir: string | null = null;\n if (probe.kind === 'file') {\n const staged = await stageSingleHtmlAsSite(workspaceRoot, probe.absolutePath);\n sitePath = staged.relativePath;\n stagedDir = staged.stagingDir;\n }\n const siteRec = await siteStore.create({\n kind: 'static',\n path: sitePath,\n ttlMs,\n description,\n subdomain: typeof body.subdomain === 'string' ? body.subdomain : undefined,\n spaFallback: true,\n rewriteMode: 'html-css',\n sessionKey,\n agentId,\n workspaceRoot,\n gatewayTokenHash: tokenHash,\n });\n if (stagedDir) rememberStagedSite(siteRec.id, stagedDir);\n const cfg = siteStore.getConfig();\n const gatewayPort = service.currentConfig.gateway.port ?? 18790;\n const gatewayHost = resolveGatewayEffectiveHost(service.currentConfig);\n const subdomainLabel = siteRec.subdomain ?? siteRec.token;\n const tunnelUp = !!(await import('../../../tunnel/tunnel-state.js')).loadTunnelState();\n const shareUrl = tunnelUp\n ? `https://${subdomainLabel}.${cfg.publicHostSuffix}/`\n : `http://${gatewayHost}:${gatewayPort}/site/${siteRec.token}/`;\n const reachability = tunnelUp ? 'public' : (gatewayHost === '127.0.0.1' || gatewayHost === 'localhost' || gatewayHost === '::1' ? 'local-only' : 'lan');\n const thumbnailUrl = wantThumbnail\n ? `${tunnelUp ? `https://${subdomainLabel}.${cfg.publicHostSuffix}` : `http://${gatewayHost}:${gatewayPort}`}/site/${siteRec.token}/thumbnail`\n : '';\n if (wantThumbnail) {\n scheduleThumbnail({ scope: 'site', token: siteRec.token, recordId: siteRec.id }, thumbnailRenderContext(service));\n siteStore.setThumbnailStatus(siteRec.id, 'pending');\n }\n const titleOut = makeTitle(probe.kind === 'directory' ? path.split('/').pop() || path : path, title);\n return c.json({\n ok: true,\n payload: {\n share: {\n id: siteRec.id,\n kind: 'site',\n title: titleOut,\n description: makeDescription({ audience, expiresAt: siteRec.expiresAt, override: description }),\n shareUrl,\n lanUrl: null,\n reachability,\n reachabilityHint: reachability === 'public' ? null : (reachability === 'lan' ? '当前局域网内可访问,开启远程隧道后对外网可达' : '当前仅本机可访问,开启远程隧道后对外可达'),\n expiresAt: siteRec.expiresAt,\n maxViews: null,\n },\n thumbnail: {\n url: thumbnailUrl,\n status: wantThumbnail ? 'pending' : 'unavailable',\n width: SHARE_CONFIG_DEFAULTS.thumbnail.viewportWidth,\n height: SHARE_CONFIG_DEFAULTS.thumbnail.viewportHeight,\n },\n routing: { reason: decision.reason, hint: decision.hint },\n },\n }, 201);\n }\n\n return await createFileShareResponse({\n c, service, store, probe, decision,\n ttlMs, maxViews, title, description, audience,\n workspaceRoot, tokenHash, urlCtx, wantThumbnail,\n });\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 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 kind: r.kind,\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 downloadCount: r.downloadCount,\n maxViews: r.maxViews,\n revoked: r.revoked,\n expired,\n description: r.description ?? null,\n fileSize: r.fileSize,\n mimeType: r.mimeType,\n directory: r.directory ?? null,\n };\n });\n\n return c.json({ ok: true, payload: { shares: items } });\n });\n\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 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 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 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// ── Directory landing / file / zip helpers ────────────────────────────────────\n\nasync function renderDirectoryLanding(\n c: Context,\n store: ReturnType<typeof getShareStore>,\n service: GatewayService,\n record: ShareRecord,\n token: string,\n subPath = '',\n): Promise<Response> {\n store.updateConfig(resolveShareConfig(service));\n const urls = {\n tree: (p: string) => (p ? `/s/${token}/tree?path=${encodeURIComponent(p)}` : `/s/${token}`),\n file: (p: string) => `/s/${token}/file?path=${encodeURIComponent(p)}`,\n zip: (p: string) => (p ? `/s/${token}/zip?path=${encodeURIComponent(p)}` : `/s/${token}/zip`),\n };\n\n let listing: import('../../../share/share-store.js').DirectoryListing | null = null;\n if (record.directory?.mode !== 'zip-only') {\n try {\n listing = await store.listDirectory(record, subPath);\n } catch (err) {\n logShareAudit(\n 'share.access_denied',\n { shareId: record.id, tokenPrefix: record.token.slice(0, 8), subPath, reason: String(err) },\n `Directory listing failed`,\n );\n return c.html(renderShareExpiredPage('not_found'), 404);\n }\n }\n const og = buildLandingOg(service, record);\n return c.html(renderFolderLandingPage(record, listing, urls, { og })) as unknown as Response;\n}\n\nfunction buildLandingOg(service: GatewayService, record: ShareRecord) {\n const resolved = resolveShareUrl(record.token, getShareUrlContext(service));\n // Only emit OG tags when the share is genuinely public — otherwise WeChat\n // and friends would silently fall back to \"no preview\" on unreachable URLs.\n if (resolved.reachability !== 'public') return undefined;\n return {\n absoluteShareUrl: resolved.shareUrl,\n absoluteThumbnailUrl: `${resolved.shareUrl}/thumbnail`,\n title: record.fileName,\n description: record.description ?? undefined,\n };\n}\n\nasync function handleDirectoryFile(\n c: Context,\n store: ReturnType<typeof getShareStore>,\n service: GatewayService,\n record: ShareRecord,\n relPath: string,\n clientIp: string,\n inline: boolean,\n): Promise<Response> {\n if (!acquireDownloadSlot(record.token)) {\n return new Response('Too many concurrent downloads for this share', { status: 429 });\n }\n\n try {\n const resolved = await store.resolveDirectoryChild(record, relPath);\n if (resolved.ok !== true) {\n logShareAudit(\n 'share.access_denied',\n { shareId: record.id, tokenPrefix: record.token.slice(0, 8), reason: resolved.reason, clientIp, relPath },\n `Directory child resolution failed: ${resolved.reason}`,\n );\n return c.html(renderShareExpiredPage('not_found'), 404);\n }\n const integrity = await store.validateFileIntegrity(record);\n if (!integrity.valid) {\n return c.html(renderShareExpiredPage((integrity.reason ?? 'file_deleted') as ShareExpiredReason), 410);\n }\n\n const fileStat = await stat(resolved.absolutePath);\n if (!fileStat.isFile()) {\n return c.html(renderShareExpiredPage('not_found'), 404);\n }\n\n // Re-check size against configured max\n store.updateConfig(resolveShareConfig(service));\n const cfg = store.getConfig();\n if (fileStat.size > cfg.maxFileSize) {\n return c.html(renderShareExpiredPage('not_found'), 410);\n }\n\n // Inline preview MIME guard\n const cfgPreview = { ...SHARE_CONFIG_DEFAULTS, ...resolveShareConfig(service) };\n const baseName = relPath.split('/').pop() || record.fileName;\n const { resolveMimeType } = await import('../../../share/share-store.js');\n const mime = resolveMimeType(baseName);\n const useInline = inline && cfgPreview.inlinePreviewMimes.includes(mime);\n\n store.incrementDownloadCount(record.id);\n logShareAudit(\n 'share.access',\n { shareId: record.id, tokenPrefix: record.token.slice(0, 8), clientIp, relPath, mode: useInline ? 'inline' : 'attachment' },\n `Directory file served: ${baseName}`,\n );\n\n const stream = createReadStream(resolved.absolutePath);\n const webStream = Readable.toWeb(stream) as ReadableStream;\n const disposition = rfc5987ContentDisposition(useInline ? 'inline' : 'attachment', baseName);\n\n return new Response(webStream, {\n status: 200,\n headers: {\n 'Content-Type': shareResponseContentType(mime),\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\nasync function handleDirectoryZip(\n c: Context,\n store: ReturnType<typeof getShareStore>,\n service: GatewayService,\n record: ShareRecord,\n subPath: string,\n clientIp: string,\n): Promise<Response> {\n store.updateConfig(resolveShareConfig(service));\n const cfg = store.getConfig();\n const zipLimit = cfg.directory.zipConcurrency;\n if (!acquireZipSlot(record.token, zipLimit)) {\n return new Response('Too many concurrent ZIP streams for this share', { status: 429 });\n }\n\n try {\n const resolved = await store.resolveDirectoryChild(record, subPath);\n if (resolved.ok !== true) {\n return c.html(renderShareExpiredPage('not_found'), 404);\n }\n const integrity = await store.validateFileIntegrity(record);\n if (!integrity.valid) {\n return c.html(renderShareExpiredPage((integrity.reason ?? 'file_deleted') as ShareExpiredReason), 410);\n }\n\n const files = await planDirectoryFiles(record, {\n rootRelativePath: subPath,\n maxFileCount: cfg.directory.maxFileCount,\n maxFolderSize: cfg.directory.maxFolderSize,\n followSymlinks: record.directory?.followSymlinks ?? false,\n maxDepth: record.directory?.maxDepth ?? cfg.directory.maxDepth,\n });\n\n store.incrementDownloadCount(record.id);\n logShareAudit(\n 'share.access',\n { shareId: record.id, tokenPrefix: record.token.slice(0, 8), clientIp, mode: 'zip', subPath, fileCount: files.length },\n `Directory zip served: ${record.fileName}${subPath ? '/' + subPath : ''}`,\n );\n\n const zipName = subPath\n ? `${record.fileName}-${subPath.replace(/[\\\\/]/g, '_')}.zip`\n : `${record.fileName}.zip`;\n const stream = createZipStream({ files });\n const webStream = Readable.toWeb(stream) as ReadableStream;\n\n return new Response(webStream, {\n status: 200,\n headers: {\n 'Content-Type': 'application/zip',\n 'Content-Disposition': rfc5987ContentDisposition('attachment', zipName),\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 } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return new Response(`zip build failed: ${message}`, { status: 500 });\n } finally {\n releaseZipSlot(record.token);\n }\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nasync function createFileShareResponse(args: {\n c: Context;\n service: GatewayService;\n store: ReturnType<typeof getShareStore>;\n probe: Awaited<ReturnType<typeof probeShareTarget>>;\n decision: ReturnType<typeof decideShareKind>;\n ttlMs: number;\n maxViews: number | null | undefined;\n title?: string;\n description?: string;\n audience?: ShareAudience;\n workspaceRoot: string;\n tokenHash: string;\n urlCtx: ReturnType<typeof getShareUrlContext>;\n wantThumbnail: boolean;\n}): Promise<Response> {\n const { c, service, store, probe, decision, ttlMs, maxViews, title, description, audience, workspaceRoot, tokenHash, urlCtx, wantThumbnail } = args;\n store.updateConfig(resolveShareConfig(service));\n const record = await store.create({\n path: relPathFromAbs(workspaceRoot, probe.absolutePath),\n workspaceRoot,\n gatewayTokenHash: tokenHash,\n ttlMs,\n maxViews: maxViews === undefined ? undefined : maxViews,\n description,\n kind: probe.kind === 'directory' ? 'directory' : 'file',\n directoryMode: decision.kind === 'zip' ? 'zip-only' : (probe.kind === 'directory' ? 'browse' : undefined),\n });\n const resolved = resolveShareUrl(record.token, urlCtx);\n const titleOut = makeTitle(record.fileName, title);\n const descOut = makeDescription({ audience, expiresAt: record.expiresAt, override: description });\n const thumbnailUrl = wantThumbnail\n ? `${resolved.shareUrl}/thumbnail`\n : '';\n if (wantThumbnail) {\n scheduleThumbnail({ scope: 'file', token: record.token, recordId: record.id }, thumbnailRenderContext(service));\n store.setThumbnailStatus(record.id, 'pending');\n }\n return c.json({\n ok: true,\n payload: {\n share: {\n id: record.id,\n kind: decision.kind,\n title: titleOut,\n description: descOut,\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 },\n thumbnail: {\n url: thumbnailUrl,\n status: wantThumbnail ? 'pending' : 'unavailable',\n width: SHARE_CONFIG_DEFAULTS.thumbnail.viewportWidth,\n height: SHARE_CONFIG_DEFAULTS.thumbnail.viewportHeight,\n },\n routing: { reason: decision.reason, hint: decision.hint },\n },\n }, 201) as unknown as Response;\n}\n\nfunction relPathFromAbs(workspaceRoot: string, abs: string): string {\n const root = workspaceRoot.replace(/[\\\\/]+$/, '');\n if (abs === root) return '';\n if (abs.startsWith(`${root}/`)) return abs.slice(root.length + 1);\n if (abs.startsWith(`${root}\\\\`)) return abs.slice(root.length + 1).replace(/\\\\/g, '/');\n // Fall back to basename — store.create will resolve again under workspace.\n return abs.split(/[\\\\/]/).pop() ?? abs;\n}\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 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 handleFileDownload(\n c: Context,\n store: ReturnType<typeof getShareStore>,\n record: ShareRecord,\n clientIp: string,\n inline = false,\n): Promise<Response> {\n if (!acquireDownloadSlot(record.token)) {\n return new Response('Too many concurrent downloads for this share', { status: 429 });\n }\n\n try {\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 return new Response(renderShareExpiredPage((integrity.reason ?? 'file_deleted') as ShareExpiredReason), {\n status: 410,\n headers: { 'Content-Type': 'text/html; charset=utf-8' },\n });\n }\n\n store.incrementDownloadCount(record.id);\n\n logShareAudit(\n 'share.access',\n { shareId: record.id, tokenPrefix: record.token.slice(0, 8), clientIp, downloadCount: record.downloadCount },\n `Share downloaded: ${record.fileName}`,\n );\n\n const fileStat = await stat(record.absolutePath);\n const stream = createReadStream(record.absolutePath);\n const webStream = Readable.toWeb(stream) as ReadableStream;\n const disposition = rfc5987ContentDisposition(inline ? 'inline' : 'attachment', record.fileName);\n\n return new Response(webStream, {\n status: 200,\n headers: {\n 'Content-Type': shareResponseContentType(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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"}
@@ -0,0 +1,3 @@
1
+ import type { Hono } from 'hono';
2
+ import type { AuthenticatedRouteDeps } from './deps.js';
3
+ export declare function registerSiteShareRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void;
@@ -0,0 +1,228 @@
1
+ import { resolveGatewayEffectiveHost } from "../../../config/gateway-bind.js";
2
+ import { resolveSiteShareConfig } from "../../../share/site-share-config.js";
3
+ import { getSiteShareStore } from "../../../share/site-share-store.js";
4
+ import { extractToken } from "../../auth.js";
5
+ import { createHash } from "node:crypto";
6
+ //#region src/gateway/hono/routes/site-shares.ts
7
+ function hashGatewayToken(token) {
8
+ return createHash("sha256").update(token, "utf8").digest("hex").slice(0, 12);
9
+ }
10
+ function buildShareUrls(record, publicHostSuffix, gatewayHost, gatewayPort) {
11
+ return {
12
+ publicUrl: `https://${record.subdomain ?? record.token}.${publicHostSuffix}/`,
13
+ subpathUrl: `http://${gatewayHost}:${gatewayPort}/site/${record.token}/`
14
+ };
15
+ }
16
+ function registerSiteShareRoutes(authenticated, deps) {
17
+ const { service } = deps;
18
+ const store = getSiteShareStore(resolveSiteShareConfig(service));
19
+ async function resolveWorkspaceRoot(sessionKey, agentId) {
20
+ const cfg = service.currentConfig;
21
+ if (sessionKey) try {
22
+ return await service.sessions.getEffectiveWorkspacePath(sessionKey);
23
+ } catch {}
24
+ const { getWorkspacePath } = await import("../../../config/workspace-path-helpers.js");
25
+ const { resolveAgentWorkspaceDir, normalizeAgentId, resolveDefaultAgentId } = await import("../../../agent/agent-scope.js");
26
+ if (agentId) return resolveAgentWorkspaceDir(cfg, normalizeAgentId(agentId));
27
+ const root = getWorkspacePath(cfg);
28
+ if (root) return root;
29
+ return resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg));
30
+ }
31
+ authenticated.post("/api/site-shares", async (c) => {
32
+ const gatewayToken = extractToken({ authorization: c.req.header("authorization") ?? void 0 });
33
+ if (!gatewayToken) return c.json({
34
+ ok: false,
35
+ error: { message: "Token required" }
36
+ }, 401);
37
+ let body;
38
+ try {
39
+ body = await c.req.json();
40
+ } catch {
41
+ return c.json({
42
+ ok: false,
43
+ error: { message: "Invalid JSON" }
44
+ }, 400);
45
+ }
46
+ const kind = body.kind === "static" || body.kind === "proxy" ? body.kind : null;
47
+ if (!kind) return c.json({
48
+ ok: false,
49
+ error: { message: "kind must be 'static' or 'proxy'" }
50
+ }, 400);
51
+ const sessionKey = typeof body.sessionKey === "string" ? body.sessionKey.trim() : void 0;
52
+ const agentId = typeof body.agentId === "string" ? body.agentId.trim() : void 0;
53
+ const path = typeof body.path === "string" ? body.path.trim() : void 0;
54
+ const upstreamUrl = typeof body.upstreamUrl === "string" ? body.upstreamUrl.trim() : void 0;
55
+ const ttlMs = typeof body.ttlMs === "number" ? body.ttlMs : void 0;
56
+ const description = typeof body.description === "string" ? body.description.trim() || void 0 : void 0;
57
+ const subdomain = typeof body.subdomain === "string" ? body.subdomain.trim() || void 0 : void 0;
58
+ const maxRequests = body.maxRequests === null ? null : typeof body.maxRequests === "number" ? body.maxRequests : void 0;
59
+ const spaFallback = typeof body.spaFallback === "boolean" ? body.spaFallback : void 0;
60
+ const rewriteMode = body.rewriteMode === "none" || body.rewriteMode === "html-only" || body.rewriteMode === "html-css" ? body.rewriteMode : void 0;
61
+ const forwardWebSocket = typeof body.forwardWebSocket === "boolean" ? body.forwardWebSocket : void 0;
62
+ let workspaceRoot = null;
63
+ if (kind === "static") {
64
+ workspaceRoot = await resolveWorkspaceRoot(sessionKey, agentId);
65
+ if (!workspaceRoot) return c.json({
66
+ ok: false,
67
+ error: { message: "Workspace not configured" }
68
+ }, 400);
69
+ }
70
+ try {
71
+ store.updateConfig(resolveSiteShareConfig(service));
72
+ const record = await store.create({
73
+ kind,
74
+ path,
75
+ upstreamUrl,
76
+ ttlMs,
77
+ description,
78
+ subdomain,
79
+ maxRequests,
80
+ spaFallback,
81
+ rewriteMode,
82
+ forwardWebSocket,
83
+ sessionKey,
84
+ agentId,
85
+ workspaceRoot,
86
+ gatewayTokenHash: hashGatewayToken(gatewayToken)
87
+ });
88
+ const cfg = store.getConfig();
89
+ const gw = service.currentConfig.gateway;
90
+ const urls = buildShareUrls(record, cfg.publicHostSuffix, resolveGatewayEffectiveHost(service.currentConfig), gw.port ?? 18790);
91
+ return c.json({
92
+ ok: true,
93
+ payload: {
94
+ id: record.id,
95
+ token: record.token,
96
+ subdomain: record.subdomain,
97
+ kind: record.source.kind,
98
+ createdAt: record.createdAt,
99
+ expiresAt: record.expiresAt,
100
+ description: record.description ?? null,
101
+ maxRequests: record.maxRequests,
102
+ publicUrl: urls.publicUrl,
103
+ subpathUrl: urls.subpathUrl
104
+ }
105
+ }, 201);
106
+ } catch (err) {
107
+ const message = err instanceof Error ? err.message : String(err);
108
+ return c.json({
109
+ ok: false,
110
+ error: { message }
111
+ }, 400);
112
+ }
113
+ });
114
+ authenticated.get("/api/site-shares", (c) => {
115
+ store.updateConfig(resolveSiteShareConfig(service));
116
+ const cfg = store.getConfig();
117
+ const gw = service.currentConfig.gateway;
118
+ const gatewayHost = resolveGatewayEffectiveHost(service.currentConfig);
119
+ const gatewayPort = gw.port ?? 18790;
120
+ const items = store.getAllShares().map((r) => {
121
+ const urls = buildShareUrls(r, cfg.publicHostSuffix, gatewayHost, gatewayPort);
122
+ const expired = Date.now() >= new Date(r.expiresAt).getTime();
123
+ return {
124
+ id: r.id,
125
+ token: r.token,
126
+ subdomain: r.subdomain,
127
+ kind: r.source.kind,
128
+ createdAt: r.createdAt,
129
+ expiresAt: r.expiresAt,
130
+ revoked: r.revoked,
131
+ expired,
132
+ description: r.description ?? null,
133
+ requestCount: r.requestCount,
134
+ uniqueClientCount: r.uniqueClientCount,
135
+ maxRequests: r.maxRequests,
136
+ source: r.source,
137
+ publicUrl: urls.publicUrl,
138
+ subpathUrl: urls.subpathUrl
139
+ };
140
+ });
141
+ return c.json({
142
+ ok: true,
143
+ payload: { shares: items }
144
+ });
145
+ });
146
+ authenticated.get("/api/site-shares/:id", (c) => {
147
+ const id = c.req.param("id");
148
+ const record = store.getById(id);
149
+ if (!record) return c.json({
150
+ ok: false,
151
+ error: { message: "Not found" }
152
+ }, 404);
153
+ const cfg = store.getConfig();
154
+ const gw = service.currentConfig.gateway;
155
+ const urls = buildShareUrls(record, cfg.publicHostSuffix, resolveGatewayEffectiveHost(service.currentConfig), gw.port ?? 18790);
156
+ return c.json({
157
+ ok: true,
158
+ payload: {
159
+ ...record,
160
+ ...urls,
161
+ expired: Date.now() >= new Date(record.expiresAt).getTime()
162
+ }
163
+ });
164
+ });
165
+ authenticated.delete("/api/site-shares/:id", (c) => {
166
+ const id = c.req.param("id");
167
+ if (!store.revoke(id)) return c.json({
168
+ ok: false,
169
+ error: { message: "Not found" }
170
+ }, 404);
171
+ return c.json({ ok: true });
172
+ });
173
+ authenticated.delete("/api/site-shares", async (c) => {
174
+ let body = {};
175
+ try {
176
+ body = await c.req.json();
177
+ } catch {}
178
+ if (body.expired === true) {
179
+ const count = store.revokeExpired();
180
+ return c.json({
181
+ ok: true,
182
+ payload: { revokedCount: count }
183
+ });
184
+ }
185
+ const ids = Array.isArray(body.ids) ? body.ids.filter((x) => typeof x === "string") : [];
186
+ if (ids.length === 0) return c.json({
187
+ ok: false,
188
+ error: { message: "Provide ids array or expired: true" }
189
+ }, 400);
190
+ const count = store.revokeMany(ids);
191
+ return c.json({
192
+ ok: true,
193
+ payload: { revokedCount: count }
194
+ });
195
+ });
196
+ authenticated.patch("/api/site-shares/:id", async (c) => {
197
+ const id = c.req.param("id");
198
+ let body;
199
+ try {
200
+ body = await c.req.json();
201
+ } catch {
202
+ return c.json({
203
+ ok: false,
204
+ error: { message: "Invalid JSON" }
205
+ }, 400);
206
+ }
207
+ const patch = {};
208
+ if (typeof body.extendTtlMs === "number") patch.extendTtlMs = body.extendTtlMs;
209
+ if (body.maxRequests === null || typeof body.maxRequests === "number") patch.maxRequests = body.maxRequests;
210
+ const updated = store.update(id, patch);
211
+ if (!updated) return c.json({
212
+ ok: false,
213
+ error: { message: "Not found" }
214
+ }, 404);
215
+ return c.json({
216
+ ok: true,
217
+ payload: {
218
+ id: updated.id,
219
+ expiresAt: updated.expiresAt,
220
+ maxRequests: updated.maxRequests
221
+ }
222
+ });
223
+ });
224
+ }
225
+ //#endregion
226
+ export { registerSiteShareRoutes };
227
+
228
+ //# sourceMappingURL=site-shares.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site-shares.js","names":[],"sources":["../../../../../src/gateway/hono/routes/site-shares.ts"],"sourcesContent":["import type { Hono } from 'hono';\nimport { createHash } from 'node:crypto';\n\nimport { extractToken } from '../../auth.js';\nimport { getSiteShareStore } from '../../../share/site-share-store.js';\nimport { resolveSiteShareConfig } from '../../../share/site-share-config.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\nimport { resolveGatewayEffectiveHost } from '../../../config/gateway-bind.js';\n\nfunction hashGatewayToken(token: string): string {\n return createHash('sha256').update(token, 'utf8').digest('hex').slice(0, 12);\n}\n\nfunction buildShareUrls(\n record: import('../../../share/site-share-types.js').SiteShareRecord,\n publicHostSuffix: string,\n gatewayHost: string,\n gatewayPort: number,\n): { publicUrl: string; subpathUrl: string } {\n const label = record.subdomain ?? record.token;\n const publicUrl = `https://${label}.${publicHostSuffix}/`;\n const subpathUrl = `http://${gatewayHost}:${gatewayPort}/site/${record.token}/`;\n return { publicUrl, subpathUrl };\n}\n\nexport function registerSiteShareRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service } = deps;\n const store = getSiteShareStore(resolveSiteShareConfig(service));\n\n async function resolveWorkspaceRoot(\n sessionKey: string | undefined,\n agentId: string | undefined,\n ): Promise<string | null> {\n const cfg = service.currentConfig;\n if (sessionKey) {\n try {\n return await service.sessions.getEffectiveWorkspacePath(sessionKey);\n } catch {\n /* fall through */\n }\n }\n const { getWorkspacePath } = await import('../../../config/workspace-path-helpers.js');\n const { resolveAgentWorkspaceDir, normalizeAgentId, resolveDefaultAgentId } = await import(\n '../../../agent/agent-scope.js'\n );\n if (agentId) {\n const normalized = normalizeAgentId(agentId);\n return resolveAgentWorkspaceDir(cfg, normalized);\n }\n const root = getWorkspacePath(cfg);\n if (root) return root;\n const defaultId = resolveDefaultAgentId(cfg);\n return resolveAgentWorkspaceDir(cfg, defaultId);\n }\n\n authenticated.post('/api/site-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 kind = body.kind === 'static' || body.kind === 'proxy' ? body.kind : null;\n if (!kind) return c.json({ ok: false, error: { message: \"kind must be 'static' or 'proxy'\" } }, 400);\n\n const sessionKey = typeof body.sessionKey === 'string' ? body.sessionKey.trim() : undefined;\n const agentId = typeof body.agentId === 'string' ? body.agentId.trim() : undefined;\n const path = typeof body.path === 'string' ? body.path.trim() : undefined;\n const upstreamUrl = typeof body.upstreamUrl === 'string' ? body.upstreamUrl.trim() : undefined;\n const ttlMs = typeof body.ttlMs === 'number' ? body.ttlMs : undefined;\n const description = typeof body.description === 'string' ? body.description.trim() || undefined : undefined;\n const subdomain = typeof body.subdomain === 'string' ? body.subdomain.trim() || undefined : undefined;\n const maxRequests =\n body.maxRequests === null\n ? null\n : typeof body.maxRequests === 'number'\n ? body.maxRequests\n : undefined;\n const spaFallback = typeof body.spaFallback === 'boolean' ? body.spaFallback : undefined;\n const rewriteMode =\n body.rewriteMode === 'none' || body.rewriteMode === 'html-only' || body.rewriteMode === 'html-css'\n ? body.rewriteMode\n : undefined;\n const forwardWebSocket = typeof body.forwardWebSocket === 'boolean' ? body.forwardWebSocket : undefined;\n\n let workspaceRoot: string | null = null;\n if (kind === 'static') {\n workspaceRoot = await resolveWorkspaceRoot(sessionKey, agentId);\n if (!workspaceRoot) {\n return c.json({ ok: false, error: { message: 'Workspace not configured' } }, 400);\n }\n }\n\n try {\n store.updateConfig(resolveSiteShareConfig(service));\n const record = await store.create({\n kind,\n path,\n upstreamUrl,\n ttlMs,\n description,\n subdomain,\n maxRequests,\n spaFallback,\n rewriteMode,\n forwardWebSocket,\n sessionKey,\n agentId,\n workspaceRoot,\n gatewayTokenHash: hashGatewayToken(gatewayToken),\n });\n\n const cfg = store.getConfig();\n const gw = service.currentConfig.gateway;\n const urls = buildShareUrls(record, cfg.publicHostSuffix, resolveGatewayEffectiveHost(service.currentConfig), gw.port ?? 18790);\n\n return c.json(\n {\n ok: true,\n payload: {\n id: record.id,\n token: record.token,\n subdomain: record.subdomain,\n kind: record.source.kind,\n createdAt: record.createdAt,\n expiresAt: record.expiresAt,\n description: record.description ?? null,\n maxRequests: record.maxRequests,\n publicUrl: urls.publicUrl,\n subpathUrl: urls.subpathUrl,\n },\n },\n 201,\n );\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 authenticated.get('/api/site-shares', (c) => {\n store.updateConfig(resolveSiteShareConfig(service));\n const cfg = store.getConfig();\n const gw = service.currentConfig.gateway;\n const gatewayHost = resolveGatewayEffectiveHost(service.currentConfig);\n const gatewayPort = gw.port ?? 18790;\n const items = store.getAllShares().map((r) => {\n const urls = buildShareUrls(r, cfg.publicHostSuffix, gatewayHost, gatewayPort);\n const expired = Date.now() >= new Date(r.expiresAt).getTime();\n return {\n id: r.id,\n token: r.token,\n subdomain: r.subdomain,\n kind: r.source.kind,\n createdAt: r.createdAt,\n expiresAt: r.expiresAt,\n revoked: r.revoked,\n expired,\n description: r.description ?? null,\n requestCount: r.requestCount,\n uniqueClientCount: r.uniqueClientCount,\n maxRequests: r.maxRequests,\n source: r.source,\n publicUrl: urls.publicUrl,\n subpathUrl: urls.subpathUrl,\n };\n });\n return c.json({ ok: true, payload: { shares: items } });\n });\n\n authenticated.get('/api/site-shares/:id', (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 const cfg = store.getConfig();\n const gw = service.currentConfig.gateway;\n const urls = buildShareUrls(record, cfg.publicHostSuffix, resolveGatewayEffectiveHost(service.currentConfig), gw.port ?? 18790);\n return c.json({\n ok: true,\n payload: {\n ...record,\n ...urls,\n expired: Date.now() >= new Date(record.expiresAt).getTime(),\n },\n });\n });\n\n authenticated.delete('/api/site-shares/:id', (c) => {\n const id = c.req.param('id');\n const ok = store.revoke(id);\n if (!ok) return c.json({ ok: false, error: { message: 'Not found' } }, 404);\n return c.json({ ok: true });\n });\n\n authenticated.delete('/api/site-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 */\n }\n if (body.expired === true) {\n const count = store.revokeExpired();\n return c.json({ ok: true, payload: { revokedCount: count } });\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 authenticated.patch('/api/site-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 const patch: { extendTtlMs?: number; maxRequests?: number | null } = {};\n if (typeof body.extendTtlMs === 'number') patch.extendTtlMs = body.extendTtlMs;\n if (body.maxRequests === null || typeof body.maxRequests === 'number') {\n patch.maxRequests = body.maxRequests 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 return c.json({ ok: true, payload: { id: updated.id, expiresAt: updated.expiresAt, maxRequests: updated.maxRequests } });\n });\n}\n"],"mappings":"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"}