@xopcai/xopc 0.0.84 → 0.0.86

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 (418) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
  3. package/dist/extensions/feishu/src/plugin.d.ts +2 -0
  4. package/dist/extensions/feishu/src/plugin.js +10 -0
  5. package/dist/extensions/feishu/src/plugin.js.map +1 -1
  6. package/dist/extensions/feishu/src/workflow-progress.d.ts +27 -0
  7. package/dist/extensions/feishu/src/workflow-progress.js +99 -0
  8. package/dist/extensions/feishu/src/workflow-progress.js.map +1 -0
  9. package/dist/extensions/telegram/src/plugin.d.ts +2 -0
  10. package/dist/extensions/telegram/src/plugin.js +11 -1
  11. package/dist/extensions/telegram/src/plugin.js.map +1 -1
  12. package/dist/extensions/telegram/src/routing-integration.js +2 -2
  13. package/dist/extensions/telegram/src/workflow-progress.d.ts +24 -0
  14. package/dist/extensions/telegram/src/workflow-progress.js +73 -0
  15. package/dist/extensions/telegram/src/workflow-progress.js.map +1 -0
  16. package/dist/extensions/telegram/xopc.extension.json +1 -1
  17. package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js +158 -0
  18. package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js.map +1 -0
  19. package/dist/extensions/weixin/src/api/api.js +2 -2
  20. package/dist/extensions/weixin/src/auth/accounts.js +1 -1
  21. package/dist/extensions/weixin/src/cdn/upload.js +1 -1
  22. package/dist/extensions/weixin/src/media/data-url.js +1 -1
  23. package/dist/extensions/weixin/src/messaging/debug-mode.js +1 -1
  24. package/dist/extensions/weixin/src/messaging/inbound.js +1 -1
  25. package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
  26. package/dist/extensions/weixin/src/plugin.d.ts +2 -0
  27. package/dist/extensions/weixin/src/plugin.js +11 -1
  28. package/dist/extensions/weixin/src/plugin.js.map +1 -1
  29. package/dist/extensions/weixin/src/storage/sync-buf.js +1 -1
  30. package/dist/extensions/weixin/src/workflow-progress.d.ts +26 -0
  31. package/dist/extensions/weixin/src/workflow-progress.js +99 -0
  32. package/dist/extensions/weixin/src/workflow-progress.js.map +1 -0
  33. package/dist/gateway/static/root/assets/agents-mS3_HpRI.js +222 -0
  34. package/dist/gateway/static/root/assets/apps-page-DrfytjOb.js +1 -0
  35. package/dist/gateway/static/root/assets/channels-settings-BG6b9KrW.js +1 -0
  36. package/dist/gateway/static/root/assets/channels-status-swr-Bs5kMCMI.js +8 -0
  37. package/dist/gateway/static/root/assets/createLucideIcon-DPHK1VkS.js +1 -0
  38. package/dist/gateway/static/root/assets/cron-api-BuVcZ5zR.js +1 -0
  39. package/dist/gateway/static/root/assets/cron-page-BMrloeFH.js +1 -0
  40. package/dist/gateway/static/root/assets/dist-BTWC-BTN.js +45 -0
  41. package/dist/gateway/static/root/assets/{dist-CqNMNhJM.js → dist-CKU1OOTf.js} +1 -1
  42. package/dist/gateway/static/root/assets/{extension-debug-page-gf2L0kY_.js → extension-debug-page-BdW_46sN.js} +1 -1
  43. package/dist/gateway/static/root/assets/extension-page-DW47KI82.js +1 -0
  44. package/dist/gateway/static/root/assets/extension-settings-page-B-W4x2xP.js +1 -0
  45. package/dist/gateway/static/root/assets/fetch-B2MYHbWg.js +1 -0
  46. package/dist/gateway/static/root/assets/{field-primitives-DTtlp-l8.js → field-primitives-DPG-oJmx.js} +1 -1
  47. package/dist/gateway/static/root/assets/heartbeat-config-api-C8dNts9i.js +1 -0
  48. package/dist/gateway/static/root/assets/index-BmVYculr.js +4700 -0
  49. package/dist/gateway/static/root/assets/index-ew_2L2We.css +1 -0
  50. package/dist/gateway/static/root/assets/logs-page-sTsVWz0X.js +1 -0
  51. package/dist/gateway/static/root/assets/sessions-page-FaG_Vlkb.js +1 -0
  52. package/dist/gateway/static/root/assets/settings-form-section-DuvRQW--.js +1 -0
  53. package/dist/gateway/static/root/assets/settings-page-Bet1OerL.js +3 -0
  54. package/dist/gateway/static/root/assets/share-preview-page-BtG2kLDh.js +2 -0
  55. package/dist/gateway/static/root/assets/skills-page-DhUO235y.js +2 -0
  56. package/dist/gateway/static/root/assets/theme-store-DryYl3qD.js +1 -0
  57. package/dist/gateway/static/root/assets/url-BwNL6Rgk.js +3 -0
  58. package/dist/gateway/static/root/assets/utils-BY7bU1DT.js +1 -0
  59. package/dist/gateway/static/root/assets/voice-api-key-field-CGEydndO.js +1 -0
  60. package/dist/gateway/static/root/index.html +7 -6
  61. package/dist/package.js +1 -1
  62. package/dist/src/agent/agent-manager.js +7 -7
  63. package/dist/src/agent/bootstrap/load-bootstrap-files.js +1 -1
  64. package/dist/src/agent/context/workspace-seed.js +3 -3
  65. package/dist/src/agent/embedded/map-stream-events.js +6 -0
  66. package/dist/src/agent/embedded/map-stream-events.js.map +1 -1
  67. package/dist/src/agent/embedded/subscribe-session.js +24 -0
  68. package/dist/src/agent/embedded/subscribe-session.js.map +1 -1
  69. package/dist/src/agent/embedded/types.d.ts +19 -0
  70. package/dist/src/agent/goals/goal-locale.js +2 -2
  71. package/dist/src/agent/goals/goal-run-store.js +4 -4
  72. package/dist/src/agent/goals/persistent-goal-service.js +1 -1
  73. package/dist/src/agent/goals/post-turn.js +2 -2
  74. package/dist/src/agent/image/load-image-media.js +2 -2
  75. package/dist/src/agent/ipc/bus.js +1 -1
  76. package/dist/src/agent/ipc/inbox.js +2 -2
  77. package/dist/src/agent/ipc/socket.js +1 -1
  78. package/dist/src/agent/memory/builtin-memory-store.js +1 -1
  79. package/dist/src/agent/memory/dreaming/deep-promotion.js +1 -1
  80. package/dist/src/agent/memory/dreaming/events.js +1 -1
  81. package/dist/src/agent/memory/dreaming/last-run.js +1 -1
  82. package/dist/src/agent/memory/dreaming/light-sweep.js +1 -1
  83. package/dist/src/agent/memory/dreaming/preview.js +1 -1
  84. package/dist/src/agent/memory/dreaming/rem-patterns.js +1 -1
  85. package/dist/src/agent/memory/dreaming/short-term-store.js +1 -1
  86. package/dist/src/agent/memory/dreaming/utils.js +1 -1
  87. package/dist/src/agent/memory/plugin-discovery.js +1 -1
  88. package/dist/src/agent/models/manager.js +1 -1
  89. package/dist/src/agent/prompt/service-prompt-builder.js +2 -2
  90. package/dist/src/agent/reply/post-compaction-context.js +1 -1
  91. package/dist/src/agent/reply/startup-context.d.ts +3 -0
  92. package/dist/src/agent/reply/startup-context.js +25 -2
  93. package/dist/src/agent/reply/startup-context.js.map +1 -1
  94. package/dist/src/agent/reply/workspace-boundary-read.js +1 -1
  95. package/dist/src/agent/sandbox/path-policy.js +2 -2
  96. package/dist/src/agent/service/build-direct-message-content.js +1 -1
  97. package/dist/src/agent/service.d.ts +1 -0
  98. package/dist/src/agent/service.js +10 -4
  99. package/dist/src/agent/service.js.map +1 -1
  100. package/dist/src/agent/session/session-inspector.js +1 -1
  101. package/dist/src/agent/skills/config.js +1 -1
  102. package/dist/src/agent/skills/hub-hash.js +2 -2
  103. package/dist/src/agent/skills/hub-lock.js +1 -1
  104. package/dist/src/agent/skills/hub-pull.js +3 -3
  105. package/dist/src/agent/skills/index.js +1 -1
  106. package/dist/src/agent/skills/managed-store.js +1 -1
  107. package/dist/src/agent/skills/scanner.js +1 -1
  108. package/dist/src/agent/skills/skill-manage-ops.js +1 -1
  109. package/dist/src/agent/skills/skill-manager.js +1 -1
  110. package/dist/src/agent/tools/create-share-tool.d.ts +27 -0
  111. package/dist/src/agent/tools/create-share-tool.js +237 -0
  112. package/dist/src/agent/tools/create-share-tool.js.map +1 -0
  113. package/dist/src/agent/tools/dreaming-tool.js +1 -1
  114. package/dist/src/agent/tools/factory.js +35 -1
  115. package/dist/src/agent/tools/factory.js.map +1 -1
  116. package/dist/src/agent/tools/image-generate-tool.js +1 -1
  117. package/dist/src/agent/tools/index.d.ts +2 -0
  118. package/dist/src/agent/tools/index.js +3 -1
  119. package/dist/src/agent/tools/send-media.js +1 -1
  120. package/dist/src/agent/tools/skill-manage-tool.js +1 -1
  121. package/dist/src/agent/tools/workflow-tool.d.ts +41 -0
  122. package/dist/src/agent/tools/workflow-tool.js +271 -0
  123. package/dist/src/agent/tools/workflow-tool.js.map +1 -0
  124. package/dist/src/agent/tools/write.js +1 -1
  125. package/dist/src/agent/workflow/builtins/audit-repo.d.ts +13 -0
  126. package/dist/src/agent/workflow/builtins/audit-repo.js +156 -0
  127. package/dist/src/agent/workflow/builtins/audit-repo.js.map +1 -0
  128. package/dist/src/agent/workflow/builtins/debug-incident.d.ts +13 -0
  129. package/dist/src/agent/workflow/builtins/debug-incident.js +155 -0
  130. package/dist/src/agent/workflow/builtins/debug-incident.js.map +1 -0
  131. package/dist/src/agent/workflow/builtins/index.d.ts +17 -0
  132. package/dist/src/agent/workflow/builtins/index.js +38 -0
  133. package/dist/src/agent/workflow/builtins/index.js.map +1 -0
  134. package/dist/src/agent/workflow/builtins/multi-perspective-review.d.ts +14 -0
  135. package/dist/src/agent/workflow/builtins/multi-perspective-review.js +149 -0
  136. package/dist/src/agent/workflow/builtins/multi-perspective-review.js.map +1 -0
  137. package/dist/src/agent/workflow/builtins/pr-review.d.ts +12 -0
  138. package/dist/src/agent/workflow/builtins/pr-review.js +156 -0
  139. package/dist/src/agent/workflow/builtins/pr-review.js.map +1 -0
  140. package/dist/src/agent/workflow/builtins/research.d.ts +13 -0
  141. package/dist/src/agent/workflow/builtins/research.js +160 -0
  142. package/dist/src/agent/workflow/builtins/research.js.map +1 -0
  143. package/dist/src/agent/workflow/catalog.d.ts +56 -0
  144. package/dist/src/agent/workflow/catalog.js +159 -0
  145. package/dist/src/agent/workflow/catalog.js.map +1 -0
  146. package/dist/src/agent/workflow/channel-capability.d.ts +76 -0
  147. package/dist/src/agent/workflow/channel-capability.js +1 -0
  148. package/dist/src/agent/workflow/index.d.ts +11 -0
  149. package/dist/src/agent/workflow/index.js +10 -0
  150. package/dist/src/agent/workflow/last-run-memory.d.ts +42 -0
  151. package/dist/src/agent/workflow/last-run-memory.js +60 -0
  152. package/dist/src/agent/workflow/last-run-memory.js.map +1 -0
  153. package/dist/src/agent/workflow/parser.d.ts +20 -0
  154. package/dist/src/agent/workflow/parser.js +146 -0
  155. package/dist/src/agent/workflow/parser.js.map +1 -0
  156. package/dist/src/agent/workflow/progress-broker.d.ts +80 -0
  157. package/dist/src/agent/workflow/progress-broker.js +263 -0
  158. package/dist/src/agent/workflow/progress-broker.js.map +1 -0
  159. package/dist/src/agent/workflow/runtime.d.ts +31 -0
  160. package/dist/src/agent/workflow/runtime.js +301 -0
  161. package/dist/src/agent/workflow/runtime.js.map +1 -0
  162. package/dist/src/agent/workflow/snapshot.d.ts +18 -0
  163. package/dist/src/agent/workflow/snapshot.js +144 -0
  164. package/dist/src/agent/workflow/snapshot.js.map +1 -0
  165. package/dist/src/agent/workflow/structured-output-tool.d.ts +33 -0
  166. package/dist/src/agent/workflow/structured-output-tool.js +58 -0
  167. package/dist/src/agent/workflow/structured-output-tool.js.map +1 -0
  168. package/dist/src/agent/workflow/subagent-runner.d.ts +42 -0
  169. package/dist/src/agent/workflow/subagent-runner.js +104 -0
  170. package/dist/src/agent/workflow/subagent-runner.js.map +1 -0
  171. package/dist/src/agent/workflow/types.d.ts +145 -0
  172. package/dist/src/agent/workflow/types.js +1 -0
  173. package/dist/src/auth/credentials.js +3 -3
  174. package/dist/src/auth/profiles/store.js +1 -1
  175. package/dist/src/auth/sync-provider-auth.js +1 -1
  176. package/dist/src/browser/cache-dir-policy.js +1 -1
  177. package/dist/src/browser/cdp-local-launcher.js +2 -2
  178. package/dist/src/browser/providers/browser-ext-install.js +4 -4
  179. package/dist/src/browser/providers/cloakbrowser.js +4 -4
  180. package/dist/src/browser/providers/playwright-doctor.js +1 -1
  181. package/dist/src/browser/stealth.js +1 -1
  182. package/dist/src/channels/attachments/inbound-persist.js +1 -1
  183. package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
  184. package/dist/src/channels/outbound/persist-store.js +1 -1
  185. package/dist/src/channels/pairing/allow-from-file.js +1 -1
  186. package/dist/src/channels/pairing/pairing-store.js +2 -2
  187. package/dist/src/chat-commands/builtins/config.js +2 -2
  188. package/dist/src/chat-commands/builtins/model.js +40 -23
  189. package/dist/src/chat-commands/builtins/model.js.map +1 -1
  190. package/dist/src/chat-commands/builtins/system.js +30 -15
  191. package/dist/src/chat-commands/builtins/system.js.map +1 -1
  192. package/dist/src/chat-commands/builtins/workflow.d.ts +18 -0
  193. package/dist/src/chat-commands/builtins/workflow.js +172 -0
  194. package/dist/src/chat-commands/builtins/workflow.js.map +1 -0
  195. package/dist/src/chat-commands/context.js +1 -1
  196. package/dist/src/chat-commands/format-output.d.ts +28 -0
  197. package/dist/src/chat-commands/format-output.js +45 -0
  198. package/dist/src/chat-commands/format-output.js.map +1 -0
  199. package/dist/src/chat-commands/index.d.ts +1 -0
  200. package/dist/src/chat-commands/index.js +3 -1
  201. package/dist/src/chat-commands/index.js.map +1 -1
  202. package/dist/src/cli/commands/config.js +2 -2
  203. package/dist/src/cli/commands/doctor/checks/config-health.js +1 -1
  204. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  205. package/dist/src/cli/commands/doctor/checks/session-integrity.js +1 -1
  206. package/dist/src/cli/commands/doctor/checks/state-integrity.js +1 -1
  207. package/dist/src/cli/commands/doctor/checks/workspace-status.js +1 -1
  208. package/dist/src/cli/commands/extension-dev.js +1 -1
  209. package/dist/src/cli/commands/extension-marketplace.js +1 -1
  210. package/dist/src/cli/commands/extension-pack.js +1 -1
  211. package/dist/src/cli/commands/gateway/lifecycle.js +10 -4
  212. package/dist/src/cli/commands/gateway/lifecycle.js.map +1 -1
  213. package/dist/src/cli/commands/gateway/shared.js +1 -1
  214. package/dist/src/cli/commands/image.js +1 -1
  215. package/dist/src/cli/commands/init.js +4 -4
  216. package/dist/src/cli/commands/onboard.js +2 -2
  217. package/dist/src/cli/commands/tunnel.js +2 -2
  218. package/dist/src/cli/utils/gateway-client.js +1 -1
  219. package/dist/src/cli/utils/init-workspace-core.js +2 -2
  220. package/dist/src/config/agent-profile.js +1 -1
  221. package/dist/src/config/gateway-bind.js +1 -1
  222. package/dist/src/config/index.js +5 -5
  223. package/dist/src/config/loader.js +2 -2
  224. package/dist/src/config/models-json.js +2 -2
  225. package/dist/src/config/paths-state.js +1 -1
  226. package/dist/src/config/profile.js +2 -2
  227. package/dist/src/config/public-url.d.ts +28 -0
  228. package/dist/src/config/public-url.js +103 -0
  229. package/dist/src/config/public-url.js.map +1 -0
  230. package/dist/src/config/schema.d.ts +82 -0
  231. package/dist/src/config/schema.js +130 -1
  232. package/dist/src/config/schema.js.map +1 -1
  233. package/dist/src/config/workspace-path.js +1 -1
  234. package/dist/src/cron/executor.js +2 -2
  235. package/dist/src/cron/persistence.js +1 -1
  236. package/dist/src/cron/run-log-store.js +1 -1
  237. package/dist/src/daemon/constants.js +1 -1
  238. package/dist/src/daemon/install-plan.js +3 -3
  239. package/dist/src/daemon/install-plan.js.map +1 -1
  240. package/dist/src/daemon/launchd.js +2 -2
  241. package/dist/src/daemon/schtasks.js +38 -1
  242. package/dist/src/daemon/schtasks.js.map +1 -1
  243. package/dist/src/daemon/systemd.js +2 -2
  244. package/dist/src/extensions/bundle-mcp.js +1 -1
  245. package/dist/src/extensions/discover-extensions.js +1 -1
  246. package/dist/src/extensions/health.js +1 -1
  247. package/dist/src/extensions/loader.js +1 -1
  248. package/dist/src/extensions/lockfile.js +2 -2
  249. package/dist/src/gateway/agents-admin.js +2 -2
  250. package/dist/src/gateway/file-path-classifier.js +2 -2
  251. package/dist/src/gateway/heartbeat/service.js +1 -1
  252. package/dist/src/gateway/hono/app.js +33 -2
  253. package/dist/src/gateway/hono/app.js.map +1 -1
  254. package/dist/src/gateway/hono/lib/config-payload.js +1 -1
  255. package/dist/src/gateway/hono/lib/extension-store.js +2 -2
  256. package/dist/src/gateway/hono/lib/static-ui.js +2 -2
  257. package/dist/src/gateway/hono/oauth.js +1 -1
  258. package/dist/src/gateway/hono/routes/agents.js +1 -1
  259. package/dist/src/gateway/hono/routes/auth-registry-extensions.js +1 -1
  260. package/dist/src/gateway/hono/routes/config-patch/misc.js +1 -1
  261. package/dist/src/gateway/hono/routes/dreaming.js +1 -1
  262. package/dist/src/gateway/hono/routes/host-fs.js +2 -2
  263. package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
  264. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  265. package/dist/src/gateway/hono/routes/models.js +1 -1
  266. package/dist/src/gateway/hono/routes/shares.js +631 -34
  267. package/dist/src/gateway/hono/routes/shares.js.map +1 -1
  268. package/dist/src/gateway/hono/routes/site-shares.d.ts +3 -0
  269. package/dist/src/gateway/hono/routes/site-shares.js +228 -0
  270. package/dist/src/gateway/hono/routes/site-shares.js.map +1 -0
  271. package/dist/src/gateway/hono/routes/tunnel.js +97 -8
  272. package/dist/src/gateway/hono/routes/tunnel.js.map +1 -1
  273. package/dist/src/gateway/hono/routes/workspace.js +5 -5
  274. package/dist/src/gateway/hono/sse.js +2 -2
  275. package/dist/src/gateway/host.d.ts +3 -1
  276. package/dist/src/gateway/host.js +3 -1
  277. package/dist/src/gateway/host.js.map +1 -1
  278. package/dist/src/gateway/lock.js +3 -3
  279. package/dist/src/gateway/ports.d.ts +6 -0
  280. package/dist/src/gateway/ports.js +38 -2
  281. package/dist/src/gateway/ports.js.map +1 -1
  282. package/dist/src/gateway/public-url.d.ts +8 -0
  283. package/dist/src/gateway/public-url.js +10 -0
  284. package/dist/src/gateway/public-url.js.map +1 -0
  285. package/dist/src/gateway/security/origin-check.d.ts +9 -1
  286. package/dist/src/gateway/security/origin-check.js +4 -0
  287. package/dist/src/gateway/security/origin-check.js.map +1 -1
  288. package/dist/src/gateway/server.js +15 -0
  289. package/dist/src/gateway/server.js.map +1 -1
  290. package/dist/src/gateway/service/agent-runner.js +2 -2
  291. package/dist/src/gateway/service/marketplace-service.js +2 -2
  292. package/dist/src/gateway/service/run-gateway-agent.js +2 -2
  293. package/dist/src/gateway/service.js +3 -2
  294. package/dist/src/gateway/service.js.map +1 -1
  295. package/dist/src/gateway/workspace-fs-file-list.js +1 -1
  296. package/dist/src/heartbeat/index.js +1 -1
  297. package/dist/src/i18n/goals-bundle.js +1 -1
  298. package/dist/src/i18n/index.d.ts +1 -0
  299. package/dist/src/i18n/index.js +2 -1
  300. package/dist/src/i18n/locales/share-tool.en.js +15 -0
  301. package/dist/src/i18n/locales/share-tool.en.js.map +1 -0
  302. package/dist/src/i18n/locales/share-tool.zh.js +15 -0
  303. package/dist/src/i18n/locales/share-tool.zh.js.map +1 -0
  304. package/dist/src/i18n/share-tool-bundle.d.ts +20 -0
  305. package/dist/src/i18n/share-tool-bundle.js +56 -0
  306. package/dist/src/i18n/share-tool-bundle.js.map +1 -0
  307. package/dist/src/infra/gateway-processes.js +1 -0
  308. package/dist/src/infra/gateway-processes.js.map +1 -1
  309. package/dist/src/infra/restart.js +2 -2
  310. package/dist/src/infra/update-check.js +1 -1
  311. package/dist/src/infra/update-lock.js +3 -3
  312. package/dist/src/infra/update-runner.js +1 -1
  313. package/dist/src/infra/update-startup.js +2 -2
  314. package/dist/src/infra/write-file-atomic.js +2 -2
  315. package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
  316. package/dist/src/providers/index.js +2 -2
  317. package/dist/src/providers/model-registry.js +1 -1
  318. package/dist/src/session/config-store.js +2 -2
  319. package/dist/src/session/parity/jsonl-transcript-io.js +2 -2
  320. package/dist/src/session/parity/sessions-json-file.js +1 -1
  321. package/dist/src/session/parity/transcript-file-lock.js +2 -2
  322. package/dist/src/session/parity/transcript-paths.js +1 -1
  323. package/dist/src/session/search-index-cache.js +1 -1
  324. package/dist/src/session/search-index.js +1 -1
  325. package/dist/src/session/session-title.js +3 -2
  326. package/dist/src/session/session-title.js.map +1 -1
  327. package/dist/src/session/store.js +5 -5
  328. package/dist/src/share/share-auto.d.ts +74 -0
  329. package/dist/src/share/share-auto.js +247 -0
  330. package/dist/src/share/share-auto.js.map +1 -0
  331. package/dist/src/share/share-config.js +63 -4
  332. package/dist/src/share/share-config.js.map +1 -1
  333. package/dist/src/share/share-landing.d.ts +28 -2
  334. package/dist/src/share/share-landing.js +155 -34
  335. package/dist/src/share/share-landing.js.map +1 -1
  336. package/dist/src/share/share-store.d.ts +48 -4
  337. package/dist/src/share/share-store.js +322 -51
  338. package/dist/src/share/share-store.js.map +1 -1
  339. package/dist/src/share/share-thumbnail.d.ts +35 -0
  340. package/dist/src/share/share-thumbnail.js +277 -0
  341. package/dist/src/share/share-thumbnail.js.map +1 -0
  342. package/dist/src/share/share-types.d.ts +68 -10
  343. package/dist/src/share/share-types.js +18 -1
  344. package/dist/src/share/share-types.js.map +1 -1
  345. package/dist/src/share/share-url.js +1 -1
  346. package/dist/src/share/share-zip.d.ts +35 -0
  347. package/dist/src/share/share-zip.js +303 -0
  348. package/dist/src/share/share-zip.js.map +1 -0
  349. package/dist/src/share/site-proxy.d.ts +35 -0
  350. package/dist/src/share/site-proxy.js +234 -0
  351. package/dist/src/share/site-proxy.js.map +1 -0
  352. package/dist/src/share/site-share-config.d.ts +11 -0
  353. package/dist/src/share/site-share-config.js +103 -0
  354. package/dist/src/share/site-share-config.js.map +1 -0
  355. package/dist/src/share/site-share-router.d.ts +23 -0
  356. package/dist/src/share/site-share-router.js +147 -0
  357. package/dist/src/share/site-share-router.js.map +1 -0
  358. package/dist/src/share/site-share-store.d.ts +53 -0
  359. package/dist/src/share/site-share-store.js +400 -0
  360. package/dist/src/share/site-share-store.js.map +1 -0
  361. package/dist/src/share/site-share-types.d.ts +103 -0
  362. package/dist/src/share/site-share-types.js +41 -0
  363. package/dist/src/share/site-share-types.js.map +1 -0
  364. package/dist/src/share/site-static-serve.d.ts +10 -0
  365. package/dist/src/share/site-static-serve.js +145 -0
  366. package/dist/src/share/site-static-serve.js.map +1 -0
  367. package/dist/src/tui/clipboard-image.js +3 -3
  368. package/dist/src/tui/theme-manager.js +1 -1
  369. package/dist/src/tui/tui-commands.js +18 -0
  370. package/dist/src/tui/tui-commands.js.map +1 -1
  371. package/dist/src/tui/tui-keybindings-file.js +1 -1
  372. package/dist/src/tui/tui-scoped-models.js +2 -2
  373. package/dist/src/tui/tui-settings.js +1 -1
  374. package/dist/src/tui/tui-workflow-slash.d.ts +32 -0
  375. package/dist/src/tui/tui-workflow-slash.js +63 -0
  376. package/dist/src/tui/tui-workflow-slash.js.map +1 -0
  377. package/dist/src/tui/tui.js +2 -2
  378. package/dist/src/tunnel/enable-lan-pairing.js +1 -1
  379. package/dist/src/tunnel/frpc-binary.js +3 -3
  380. package/dist/src/tunnel/frpc-config.js +1 -1
  381. package/dist/src/tunnel/frpc-extract.js +1 -1
  382. package/dist/src/tunnel/index.js +2 -2
  383. package/dist/src/tunnel/pair-context.d.ts +7 -1
  384. package/dist/src/tunnel/pair-context.js +25 -9
  385. package/dist/src/tunnel/pair-context.js.map +1 -1
  386. package/dist/src/tunnel/pair-url.d.ts +14 -1
  387. package/dist/src/tunnel/pair-url.js +14 -1
  388. package/dist/src/tunnel/pair-url.js.map +1 -1
  389. package/dist/src/tunnel/tunnel-service.js +2 -2
  390. package/dist/src/tunnel/tunnel-state.js +1 -1
  391. package/dist/src/utils/logger/audit.js +1 -1
  392. package/dist/src/utils/logger/log-store.js +1 -1
  393. package/dist/src/utils/logger/rotation.js +1 -1
  394. package/dist/src/voice/tts/audio.js +1 -1
  395. package/dist/src/voice/tts/providers/edge-speech.js +2 -2
  396. package/package.json +3 -2
  397. package/dist/gateway/static/root/assets/agents-tR-nNP04.js +0 -222
  398. package/dist/gateway/static/root/assets/apps-page-BDw6SP-d.js +0 -1
  399. package/dist/gateway/static/root/assets/button-KafIU8dx.js +0 -1
  400. package/dist/gateway/static/root/assets/channels-settings-DEFd-jj1.js +0 -1
  401. package/dist/gateway/static/root/assets/channels-status-swr-DI5FHdGe.js +0 -8
  402. package/dist/gateway/static/root/assets/cron-api-BSqY8LwW.js +0 -1
  403. package/dist/gateway/static/root/assets/cron-page-D7lVDjcR.js +0 -1
  404. package/dist/gateway/static/root/assets/dist-C57OMHW8.js +0 -48
  405. package/dist/gateway/static/root/assets/extension-page-CQo2Xsmg.js +0 -1
  406. package/dist/gateway/static/root/assets/extension-settings-page-CZf0WoZg.js +0 -1
  407. package/dist/gateway/static/root/assets/fetch-2iRFmd3n.js +0 -3
  408. package/dist/gateway/static/root/assets/heartbeat-config-api-B0drdQEJ.js +0 -1
  409. package/dist/gateway/static/root/assets/index-0Gt3TG4j.js +0 -4693
  410. package/dist/gateway/static/root/assets/index-BuFldCsB.css +0 -1
  411. package/dist/gateway/static/root/assets/logs-page-DMuORLfC.js +0 -1
  412. package/dist/gateway/static/root/assets/sessions-page-_UO8g6NN.js +0 -1
  413. package/dist/gateway/static/root/assets/settings-form-section-DkmHkknc.js +0 -1
  414. package/dist/gateway/static/root/assets/settings-page-Cz8FoW_A.js +0 -3
  415. package/dist/gateway/static/root/assets/skills-page-HrUOxF7H.js +0 -2
  416. package/dist/gateway/static/root/assets/theme-store-D01dJt95.js +0 -1
  417. package/dist/gateway/static/root/assets/utils-BFwcR6pL.js +0 -1
  418. package/dist/gateway/static/root/assets/voice-api-key-field-JF8-aqc5.js +0 -1
@@ -8,51 +8,179 @@ function formatFileSize(bytes) {
8
8
  function escapeHtml(text) {
9
9
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
10
10
  }
11
- /** Render the download confirmation landing page (does not consume viewCount). */
12
- function renderShareLandingPage(record, downloadPath) {
11
+ function commonCss() {
12
+ return `
13
+ *{margin:0;padding:0;box-sizing:border-box}
14
+ body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#f8fafc;color:#1e293b;min-height:100vh;display:flex;align-items:flex-start;justify-content:center;padding:1rem}
15
+ .card{background:#fff;border:1px solid #e2e8f0;border-radius:12px;padding:2rem;max-width:720px;width:100%;box-shadow:0 1px 3px rgba(0,0,0,.06);margin:1rem 0}
16
+ .icon{font-size:2.5rem;margin-bottom:1rem;text-align:center}
17
+ .name{font-size:1.125rem;font-weight:600;word-break:break-all;text-align:center;margin-bottom:.5rem}
18
+ .meta{font-size:.875rem;color:#64748b;text-align:center;margin-bottom:.25rem}
19
+ .desc{font-size:.875rem;color:#475569;margin:1rem 0;padding:.75rem;background:#f1f5f9;border-radius:8px}
20
+ .btn{display:inline-block;padding:.6rem 1.4rem;background:#2563eb;color:#fff;text-decoration:none;border-radius:8px;font-weight:500;font-size:.95rem;border:none;cursor:pointer;transition:background .15s;margin:.25rem}
21
+ .btn.secondary{background:#475569}
22
+ .btn:hover{filter:brightness(1.1)}
23
+ .footer{margin-top:1.5rem;font-size:.75rem;color:#94a3b8;text-align:center}
24
+ .actions{text-align:center;margin-top:1.5rem}
25
+ .crumb{font-size:.85rem;color:#475569;margin-bottom:1rem}
26
+ .crumb a{color:#2563eb;text-decoration:none}
27
+ .crumb a:hover{text-decoration:underline}
28
+ .tree{border-top:1px solid #e2e8f0;margin-top:1rem}
29
+ .row{display:flex;align-items:center;justify-content:space-between;padding:.55rem .25rem;border-bottom:1px solid #f1f5f9}
30
+ .row a{text-decoration:none;color:#1e293b;flex:1;min-width:0;display:flex;align-items:center;gap:.5rem}
31
+ .row a:hover{color:#2563eb}
32
+ .row .size{color:#94a3b8;font-size:.8rem;margin-left:.75rem;white-space:nowrap}
33
+ .row .act{display:flex;gap:.5rem;align-items:center}
34
+ .row .act a{font-size:.8rem;color:#2563eb;flex:none}
35
+ .empty{text-align:center;color:#94a3b8;padding:2rem}
36
+ `;
37
+ }
38
+ /** Render the file download confirmation landing page (does not consume downloadCount). */
39
+ function renderShareLandingPage(record, downloadPath, options) {
13
40
  const fileName = escapeHtml(record.fileName);
14
41
  const description = record.description ? escapeHtml(record.description) : "";
42
+ const size = formatFileSize(record.fileSize);
43
+ const expiresDate = new Date(record.expiresAt).toLocaleDateString("zh-CN", {
44
+ year: "numeric",
45
+ month: "long",
46
+ day: "numeric",
47
+ hour: "2-digit",
48
+ minute: "2-digit"
49
+ });
50
+ const buttons = [];
51
+ if (options?.previewUrl) buttons.push(`<a class="btn" href="${escapeHtml(options.previewUrl)}" target="_blank" rel="noopener">👁 在线预览</a>`);
52
+ if (options?.inlineUrl) buttons.push(`<a class="btn secondary" href="${escapeHtml(options.inlineUrl)}" target="_blank" rel="noopener">↗ 在线打开</a>`);
53
+ buttons.push(`<form method="POST" action="${escapeHtml(downloadPath)}" style="display:inline">
54
+ <button type="submit" class="btn ${buttons.length > 0 ? "secondary" : ""}">⬇ 下载文件</button>
55
+ </form>`);
15
56
  return `<!DOCTYPE html>
16
57
  <html lang="zh">
17
58
  <head>
18
59
  <meta charset="utf-8">
19
60
  <meta name="viewport" content="width=device-width,initial-scale=1">
20
61
  <meta name="referrer" content="no-referrer">
21
- <meta name="robots" content="noindex,nofollow">
62
+ <meta name="robots" content="noindex,follow">
63
+ ${renderOgTags({
64
+ title: escapeHtml(options?.og?.title ?? record.fileName),
65
+ description: escapeHtml(options?.og?.description ?? `${size} · 由 xopc 分享`),
66
+ url: options?.og?.absoluteShareUrl ?? null,
67
+ image: options?.og?.absoluteThumbnailUrl ?? null
68
+ })}
22
69
  <title>${fileName} — xopc Share</title>
23
- <style>
24
- *{margin:0;padding:0;box-sizing:border-box}
25
- body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#f8fafc;color:#1e293b;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:1rem}
26
- .card{background:#fff;border:1px solid #e2e8f0;border-radius:12px;padding:2rem;max-width:420px;width:100%;text-align:center;box-shadow:0 1px 3px rgba(0,0,0,.06)}
27
- .icon{font-size:2.5rem;margin-bottom:1rem}
28
- .name{font-size:1.125rem;font-weight:600;word-break:break-all;margin-bottom:.5rem}
29
- .meta{font-size:.875rem;color:#64748b;margin-bottom:.25rem}
30
- .desc{font-size:.875rem;color:#475569;margin:1rem 0;padding:.75rem;background:#f1f5f9;border-radius:8px;text-align:left}
31
- .btn{display:inline-block;margin-top:1.5rem;padding:.75rem 2rem;background:#2563eb;color:#fff;text-decoration:none;border-radius:8px;font-weight:500;font-size:1rem;border:none;cursor:pointer;transition:background .15s}
32
- .btn:hover{background:#1d4ed8}
33
- .footer{margin-top:1.5rem;font-size:.75rem;color:#94a3b8}
34
- </style>
70
+ <style>${commonCss()}</style>
35
71
  </head>
36
72
  <body>
37
73
  <div class="card">
38
74
  <div class="icon">📄</div>
39
75
  <div class="name">${fileName}</div>
40
- <div class="meta">${formatFileSize(record.fileSize)} · 有效期至 ${new Date(record.expiresAt).toLocaleDateString("zh-CN", {
76
+ <div class="meta">${size} · 有效期至 ${expiresDate}</div>
77
+ ${description ? `<div class="desc">${description}</div>` : ""}
78
+ <div class="actions">
79
+ ${buttons.join("\n")}
80
+ </div>
81
+ <div class="footer">Shared via xopc</div>
82
+ </div>
83
+ </body>
84
+ </html>`;
85
+ }
86
+ /** Emit OG / Twitter / WeChat-compatible meta tags. Only emitted when url+image present. */
87
+ function renderOgTags(opts) {
88
+ if (!opts.url || !opts.image) return "";
89
+ return [
90
+ `<meta property="og:type" content="website">`,
91
+ `<meta property="og:site_name" content="xopc Share">`,
92
+ `<meta property="og:title" content="${opts.title}">`,
93
+ `<meta property="og:description" content="${opts.description}">`,
94
+ `<meta property="og:image" content="${escapeHtml(opts.image)}">`,
95
+ `<meta property="og:image:width" content="1200">`,
96
+ `<meta property="og:image:height" content="630">`,
97
+ `<meta property="og:url" content="${escapeHtml(opts.url)}">`,
98
+ `<meta itemprop="name" content="${opts.title}">`,
99
+ `<meta itemprop="description" content="${opts.description}">`,
100
+ `<meta itemprop="image" content="${escapeHtml(opts.image)}">`,
101
+ `<meta name="twitter:card" content="summary_large_image">`
102
+ ].join("\n");
103
+ }
104
+ /** Render the folder landing page (browse mode shows a tree, zip-only shows a single button). */
105
+ function renderFolderLandingPage(record, listing, urls, options) {
106
+ const folderName = escapeHtml(record.fileName);
107
+ const description = record.description ? escapeHtml(record.description) : "";
108
+ const totalSize = formatFileSize(record.fileSize);
109
+ const entryCount = record.directory?.entryCount ?? 0;
110
+ const expiresDate = new Date(record.expiresAt).toLocaleDateString("zh-CN", {
41
111
  year: "numeric",
42
112
  month: "long",
43
113
  day: "numeric",
44
114
  hour: "2-digit",
45
115
  minute: "2-digit"
46
- })}</div>
116
+ });
117
+ const zipOnly = record.directory?.mode === "zip-only" || !listing;
118
+ const zipUrl = urls.zip("");
119
+ const treeHtml = zipOnly ? "" : renderTree(listing, urls);
120
+ const breadcrumbs = zipOnly ? "" : renderBreadcrumbs(listing.path, urls);
121
+ return `<!DOCTYPE html>
122
+ <html lang="zh">
123
+ <head>
124
+ <meta charset="utf-8">
125
+ <meta name="viewport" content="width=device-width,initial-scale=1">
126
+ <meta name="referrer" content="no-referrer">
127
+ <meta name="robots" content="noindex,follow">
128
+ ${renderOgTags({
129
+ title: escapeHtml(options?.og?.title ?? record.fileName),
130
+ description: escapeHtml(options?.og?.description ?? `${entryCount} 项 · ${totalSize}`),
131
+ url: options?.og?.absoluteShareUrl ?? null,
132
+ image: options?.og?.absoluteThumbnailUrl ?? null
133
+ })}
134
+ <title>${folderName} — xopc Share</title>
135
+ <style>${commonCss()}</style>
136
+ </head>
137
+ <body>
138
+ <div class="card">
139
+ <div class="icon">📁</div>
140
+ <div class="name">${folderName}</div>
141
+ <div class="meta">${entryCount} 项 · ${totalSize} · 有效期至 ${expiresDate}</div>
47
142
  ${description ? `<div class="desc">${description}</div>` : ""}
48
- <form method="POST" action="${escapeHtml(downloadPath)}">
49
- <button type="submit" class="btn">⬇ 下载文件</button>
50
- </form>
143
+ <div class="actions">
144
+ <a class="btn" href="${escapeHtml(zipUrl)}">⬇ 下载全部为 ZIP</a>
145
+ </div>
146
+ ${breadcrumbs}
147
+ ${treeHtml}
51
148
  <div class="footer">Shared via xopc</div>
52
149
  </div>
53
150
  </body>
54
151
  </html>`;
55
152
  }
153
+ function renderBreadcrumbs(currentPath, urls) {
154
+ if (!currentPath) return "<div class=\"crumb\"><a href=\"" + escapeHtml(urls.tree("")) + "\">/ 根目录</a></div>";
155
+ const segments = currentPath.split("/").filter(Boolean);
156
+ const links = [`<a href="${escapeHtml(urls.tree(""))}">/ 根目录</a>`];
157
+ let cumulative = "";
158
+ for (const seg of segments) {
159
+ cumulative = cumulative ? `${cumulative}/${seg}` : seg;
160
+ links.push(`<a href="${escapeHtml(urls.tree(cumulative))}">${escapeHtml(seg)}</a>`);
161
+ }
162
+ return `<div class="crumb">${links.join(" / ")}</div>`;
163
+ }
164
+ function renderTree(listing, urls) {
165
+ if (listing.entries.length === 0) return "<div class=\"tree\"><div class=\"empty\">空目录</div></div>";
166
+ return `<div class="tree">${listing.entries.map((entry) => renderTreeRow(entry, urls)).join("")}${listing.truncated ? "<div class=\"empty\">列表过长已截断(仍可通过 URL 访问子目录)</div>" : ""}</div>`;
167
+ }
168
+ function renderTreeRow(entry, urls) {
169
+ const safeName = escapeHtml(entry.name);
170
+ if (entry.isDirectory) return `<div class="row">
171
+ <a href="${escapeHtml(urls.tree(entry.path))}"><span>📁</span><span>${safeName}/</span></a>
172
+ <div class="act">
173
+ <a href="${escapeHtml(urls.zip(entry.path))}">ZIP</a>
174
+ </div>
175
+ </div>`;
176
+ return `<div class="row">
177
+ <a href="${escapeHtml(urls.file(entry.path))}"><span>📄</span><span>${safeName}</span></a>
178
+ <div class="act">
179
+ <span class="size">${formatFileSize(entry.size)}</span>
180
+ <a href="${escapeHtml(urls.file(entry.path))}?dl=1">下载</a>
181
+ </div>
182
+ </div>`;
183
+ }
56
184
  /** Render the "share no longer valid" page — does NOT reveal file name or path. */
57
185
  function renderShareExpiredPage(reason) {
58
186
  const reasons = {
@@ -62,6 +190,7 @@ function renderShareExpiredPage(reason) {
62
190
  not_found: "链接无效或已失效",
63
191
  file_deleted: "分享的文件已不存在"
64
192
  };
193
+ const reasonText = reasons[reason] ?? reasons.not_found;
65
194
  return `<!DOCTYPE html>
66
195
  <html lang="zh">
67
196
  <head>
@@ -70,27 +199,19 @@ function renderShareExpiredPage(reason) {
70
199
  <meta name="referrer" content="no-referrer">
71
200
  <meta name="robots" content="noindex,nofollow">
72
201
  <title>分享已失效 — xopc</title>
73
- <style>
74
- *{margin:0;padding:0;box-sizing:border-box}
75
- body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#f8fafc;color:#1e293b;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:1rem}
76
- .card{background:#fff;border:1px solid #e2e8f0;border-radius:12px;padding:2rem;max-width:420px;width:100%;text-align:center;box-shadow:0 1px 3px rgba(0,0,0,.06)}
77
- .icon{font-size:2.5rem;margin-bottom:1rem}
78
- h1{font-size:1.125rem;font-weight:600;margin-bottom:1rem}
79
- .reason{font-size:.875rem;color:#64748b;margin-bottom:1rem}
80
- .hint{font-size:.8125rem;color:#94a3b8;margin-top:1rem}
81
- </style>
202
+ <style>${commonCss()}</style>
82
203
  </head>
83
204
  <body>
84
- <div class="card">
205
+ <div class="card" style="max-width:420px;text-align:center">
85
206
  <div class="icon">⚠️</div>
86
- <h1>此分享链接已失效</h1>
87
- <div class="reason">${reasons[reason] ?? reasons.not_found}</div>
88
- <div class="hint">如需访问请联系分享者获取新链接</div>
207
+ <h1 style="font-size:1.125rem;font-weight:600;margin-bottom:1rem">此分享链接已失效</h1>
208
+ <div class="meta">${reasonText}</div>
209
+ <div class="footer">如需访问请联系分享者获取新链接</div>
89
210
  </div>
90
211
  </body>
91
212
  </html>`;
92
213
  }
93
214
  //#endregion
94
- export { renderShareExpiredPage, renderShareLandingPage };
215
+ export { renderFolderLandingPage, renderShareExpiredPage, renderShareLandingPage };
95
216
 
96
217
  //# sourceMappingURL=share-landing.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"share-landing.js","names":[],"sources":["../../../src/share/share-landing.ts"],"sourcesContent":["import type { ShareRecord } from './share-types.js';\n\nfunction formatFileSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1_048_576) return `${(bytes / 1024).toFixed(1)} KB`;\n if (bytes < 1_073_741_824) return `${(bytes / 1_048_576).toFixed(1)} MB`;\n return `${(bytes / 1_073_741_824).toFixed(1)} GB`;\n}\n\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n}\n\n/** Render the download confirmation landing page (does not consume viewCount). */\nexport function renderShareLandingPage(record: ShareRecord, downloadPath: string): string {\n const fileName = escapeHtml(record.fileName);\n const description = record.description ? escapeHtml(record.description) : '';\n const size = formatFileSize(record.fileSize);\n const expiresDate = new Date(record.expiresAt).toLocaleDateString('zh-CN', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n\n return `<!DOCTYPE html>\n<html lang=\"zh\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<meta name=\"referrer\" content=\"no-referrer\">\n<meta name=\"robots\" content=\"noindex,nofollow\">\n<title>${fileName} — xopc Share</title>\n<style>\n*{margin:0;padding:0;box-sizing:border-box}\nbody{font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;background:#f8fafc;color:#1e293b;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:1rem}\n.card{background:#fff;border:1px solid #e2e8f0;border-radius:12px;padding:2rem;max-width:420px;width:100%;text-align:center;box-shadow:0 1px 3px rgba(0,0,0,.06)}\n.icon{font-size:2.5rem;margin-bottom:1rem}\n.name{font-size:1.125rem;font-weight:600;word-break:break-all;margin-bottom:.5rem}\n.meta{font-size:.875rem;color:#64748b;margin-bottom:.25rem}\n.desc{font-size:.875rem;color:#475569;margin:1rem 0;padding:.75rem;background:#f1f5f9;border-radius:8px;text-align:left}\n.btn{display:inline-block;margin-top:1.5rem;padding:.75rem 2rem;background:#2563eb;color:#fff;text-decoration:none;border-radius:8px;font-weight:500;font-size:1rem;border:none;cursor:pointer;transition:background .15s}\n.btn:hover{background:#1d4ed8}\n.footer{margin-top:1.5rem;font-size:.75rem;color:#94a3b8}\n</style>\n</head>\n<body>\n<div class=\"card\">\n<div class=\"icon\">📄</div>\n<div class=\"name\">${fileName}</div>\n<div class=\"meta\">${size} · 有效期至 ${expiresDate}</div>\n${description ? `<div class=\"desc\">${description}</div>` : ''}\n<form method=\"POST\" action=\"${escapeHtml(downloadPath)}\">\n<button type=\"submit\" class=\"btn\">⬇ 下载文件</button>\n</form>\n<div class=\"footer\">Shared via xopc</div>\n</div>\n</body>\n</html>`;\n}\n\nexport type ShareExpiredReason = 'expired' | 'revoked' | 'max_views' | 'not_found' | 'file_deleted';\n\n/** Render the \"share no longer valid\" page — does NOT reveal file name or path. */\nexport function renderShareExpiredPage(reason: ShareExpiredReason): string {\n const reasons: Record<ShareExpiredReason, string> = {\n expired: '链接已过期',\n revoked: '链接已被创建者撤销',\n max_views: '已达最大访问次数',\n not_found: '链接无效或已失效',\n file_deleted: '分享的文件已不存在',\n };\n const reasonText = reasons[reason] ?? reasons.not_found;\n\n return `<!DOCTYPE html>\n<html lang=\"zh\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<meta name=\"referrer\" content=\"no-referrer\">\n<meta name=\"robots\" content=\"noindex,nofollow\">\n<title>分享已失效 — xopc</title>\n<style>\n*{margin:0;padding:0;box-sizing:border-box}\nbody{font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;background:#f8fafc;color:#1e293b;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:1rem}\n.card{background:#fff;border:1px solid #e2e8f0;border-radius:12px;padding:2rem;max-width:420px;width:100%;text-align:center;box-shadow:0 1px 3px rgba(0,0,0,.06)}\n.icon{font-size:2.5rem;margin-bottom:1rem}\nh1{font-size:1.125rem;font-weight:600;margin-bottom:1rem}\n.reason{font-size:.875rem;color:#64748b;margin-bottom:1rem}\n.hint{font-size:.8125rem;color:#94a3b8;margin-top:1rem}\n</style>\n</head>\n<body>\n<div class=\"card\">\n<div class=\"icon\">⚠️</div>\n<h1>此分享链接已失效</h1>\n<div class=\"reason\">${reasonText}</div>\n<div class=\"hint\">如需访问请联系分享者获取新链接</div>\n</div>\n</body>\n</html>`;\n}\n"],"mappings":";AAEA,SAAS,eAAe,OAAuB;AAC7C,KAAI,QAAQ,KAAM,QAAO,GAAG,MAAM;AAClC,KAAI,QAAQ,QAAW,QAAO,IAAI,QAAQ,MAAM,QAAQ,EAAE,CAAC;AAC3D,KAAI,QAAQ,WAAe,QAAO,IAAI,QAAQ,SAAW,QAAQ,EAAE,CAAC;AACpE,QAAO,IAAI,QAAQ,YAAe,QAAQ,EAAE,CAAC;;AAG/C,SAAS,WAAW,MAAsB;AACxC,QAAO,KACJ,QAAQ,MAAM,QAAQ,CACtB,QAAQ,MAAM,OAAO,CACrB,QAAQ,MAAM,OAAO,CACrB,QAAQ,MAAM,SAAS,CACvB,QAAQ,MAAM,QAAQ;;;AAI3B,SAAgB,uBAAuB,QAAqB,cAA8B;CACxF,MAAM,WAAW,WAAW,OAAO,SAAS;CAC5C,MAAM,cAAc,OAAO,cAAc,WAAW,OAAO,YAAY,GAAG;AAU1E,QAAO;;;;;;;SAOA,SAAS;;;;;;;;;;;;;;;;;oBAiBE,SAAS;oBAjCd,eAAe,OAAO,SAkCb,CAAC,UAjCH,IAAI,KAAK,OAAO,UAAU,CAAC,mBAAmB,SAAS;EACzE,MAAM;EACN,OAAO;EACP,KAAK;EACL,MAAM;EACN,QAAQ;EACT,CA2B2C,CAAC;EAC7C,cAAc,qBAAqB,YAAY,UAAU,GAAG;8BAChC,WAAW,aAAa,CAAC;;;;;;;;;AAYvD,SAAgB,uBAAuB,QAAoC;CACzE,MAAM,UAA8C;EAClD,SAAS;EACT,SAAS;EACT,WAAW;EACX,WAAW;EACX,cAAc;EACf;AAGD,QAAO;;;;;;;;;;;;;;;;;;;;;;sBAFY,QAAQ,WAAW,QAAQ,UAwBf"}
1
+ {"version":3,"file":"share-landing.js","names":[],"sources":["../../../src/share/share-landing.ts"],"sourcesContent":["import type { DirectoryListing, DirectoryListingEntry } from './share-store.js';\nimport type { ShareRecord } from './share-types.js';\n\nfunction formatFileSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1_048_576) return `${(bytes / 1024).toFixed(1)} KB`;\n if (bytes < 1_073_741_824) return `${(bytes / 1_048_576).toFixed(1)} MB`;\n return `${(bytes / 1_073_741_824).toFixed(1)} GB`;\n}\n\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n}\n\nfunction commonCss(): string {\n return `\n*{margin:0;padding:0;box-sizing:border-box}\nbody{font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;background:#f8fafc;color:#1e293b;min-height:100vh;display:flex;align-items:flex-start;justify-content:center;padding:1rem}\n.card{background:#fff;border:1px solid #e2e8f0;border-radius:12px;padding:2rem;max-width:720px;width:100%;box-shadow:0 1px 3px rgba(0,0,0,.06);margin:1rem 0}\n.icon{font-size:2.5rem;margin-bottom:1rem;text-align:center}\n.name{font-size:1.125rem;font-weight:600;word-break:break-all;text-align:center;margin-bottom:.5rem}\n.meta{font-size:.875rem;color:#64748b;text-align:center;margin-bottom:.25rem}\n.desc{font-size:.875rem;color:#475569;margin:1rem 0;padding:.75rem;background:#f1f5f9;border-radius:8px}\n.btn{display:inline-block;padding:.6rem 1.4rem;background:#2563eb;color:#fff;text-decoration:none;border-radius:8px;font-weight:500;font-size:.95rem;border:none;cursor:pointer;transition:background .15s;margin:.25rem}\n.btn.secondary{background:#475569}\n.btn:hover{filter:brightness(1.1)}\n.footer{margin-top:1.5rem;font-size:.75rem;color:#94a3b8;text-align:center}\n.actions{text-align:center;margin-top:1.5rem}\n.crumb{font-size:.85rem;color:#475569;margin-bottom:1rem}\n.crumb a{color:#2563eb;text-decoration:none}\n.crumb a:hover{text-decoration:underline}\n.tree{border-top:1px solid #e2e8f0;margin-top:1rem}\n.row{display:flex;align-items:center;justify-content:space-between;padding:.55rem .25rem;border-bottom:1px solid #f1f5f9}\n.row a{text-decoration:none;color:#1e293b;flex:1;min-width:0;display:flex;align-items:center;gap:.5rem}\n.row a:hover{color:#2563eb}\n.row .size{color:#94a3b8;font-size:.8rem;margin-left:.75rem;white-space:nowrap}\n.row .act{display:flex;gap:.5rem;align-items:center}\n.row .act a{font-size:.8rem;color:#2563eb;flex:none}\n.empty{text-align:center;color:#94a3b8;padding:2rem}\n`;\n}\n\nexport interface ShareLandingOgOptions {\n /** Absolute https/http URL of this landing page (for og:url). Omit if non-public. */\n absoluteShareUrl?: string | null;\n /** Absolute URL of the thumbnail image (for og:image). Omit if non-public. */\n absoluteThumbnailUrl?: string | null;\n /** Title override (default: fileName). */\n title?: string;\n /** Description override (default: type + size). */\n description?: string;\n}\n\n/** Render the file download confirmation landing page (does not consume downloadCount). */\nexport function renderShareLandingPage(\n record: ShareRecord,\n downloadPath: string,\n options?: {\n /** Path to open the file inline (?inline=1) for browser-native rendering. */\n inlineUrl?: string | null;\n /** SPA preview URL (for markdown / docs / code that benefit from rich rendering). */\n previewUrl?: string | null;\n /** Social card / unfurl metadata. */\n og?: ShareLandingOgOptions;\n },\n): string {\n const fileName = escapeHtml(record.fileName);\n const description = record.description ? escapeHtml(record.description) : '';\n const size = formatFileSize(record.fileSize);\n const expiresDate = new Date(record.expiresAt).toLocaleDateString('zh-CN', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n\n const buttons: string[] = [];\n if (options?.previewUrl) {\n buttons.push(\n `<a class=\"btn\" href=\"${escapeHtml(options.previewUrl)}\" target=\"_blank\" rel=\"noopener\">👁 在线预览</a>`,\n );\n }\n if (options?.inlineUrl) {\n buttons.push(\n `<a class=\"btn secondary\" href=\"${escapeHtml(options.inlineUrl)}\" target=\"_blank\" rel=\"noopener\">↗ 在线打开</a>`,\n );\n }\n buttons.push(\n `<form method=\"POST\" action=\"${escapeHtml(downloadPath)}\" style=\"display:inline\">\n<button type=\"submit\" class=\"btn ${buttons.length > 0 ? 'secondary' : ''}\">⬇ 下载文件</button>\n</form>`,\n );\n\n const ogTitle = escapeHtml(options?.og?.title ?? record.fileName);\n const ogDesc = escapeHtml(options?.og?.description ?? `${size} · 由 xopc 分享`);\n const ogTags = renderOgTags({\n title: ogTitle,\n description: ogDesc,\n url: options?.og?.absoluteShareUrl ?? null,\n image: options?.og?.absoluteThumbnailUrl ?? null,\n });\n\n return `<!DOCTYPE html>\n<html lang=\"zh\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<meta name=\"referrer\" content=\"no-referrer\">\n<meta name=\"robots\" content=\"noindex,follow\">\n${ogTags}\n<title>${fileName} — xopc Share</title>\n<style>${commonCss()}</style>\n</head>\n<body>\n<div class=\"card\">\n<div class=\"icon\">📄</div>\n<div class=\"name\">${fileName}</div>\n<div class=\"meta\">${size} · 有效期至 ${expiresDate}</div>\n${description ? `<div class=\"desc\">${description}</div>` : ''}\n<div class=\"actions\">\n${buttons.join('\\n')}\n</div>\n<div class=\"footer\">Shared via xopc</div>\n</div>\n</body>\n</html>`;\n}\n\n/** Emit OG / Twitter / WeChat-compatible meta tags. Only emitted when url+image present. */\nfunction renderOgTags(opts: { title: string; description: string; url: string | null; image: string | null }): string {\n if (!opts.url || !opts.image) return '';\n return [\n `<meta property=\"og:type\" content=\"website\">`,\n `<meta property=\"og:site_name\" content=\"xopc Share\">`,\n `<meta property=\"og:title\" content=\"${opts.title}\">`,\n `<meta property=\"og:description\" content=\"${opts.description}\">`,\n `<meta property=\"og:image\" content=\"${escapeHtml(opts.image)}\">`,\n `<meta property=\"og:image:width\" content=\"1200\">`,\n `<meta property=\"og:image:height\" content=\"630\">`,\n `<meta property=\"og:url\" content=\"${escapeHtml(opts.url)}\">`,\n `<meta itemprop=\"name\" content=\"${opts.title}\">`,\n `<meta itemprop=\"description\" content=\"${opts.description}\">`,\n `<meta itemprop=\"image\" content=\"${escapeHtml(opts.image)}\">`,\n `<meta name=\"twitter:card\" content=\"summary_large_image\">`,\n ].join('\\n');\n}\n\n/** Render the folder landing page (browse mode shows a tree, zip-only shows a single button). */\nexport function renderFolderLandingPage(\n record: ShareRecord,\n listing: DirectoryListing | null,\n urls: { tree: (path: string) => string; file: (path: string) => string; zip: (path: string) => string },\n options?: { og?: ShareLandingOgOptions },\n): string {\n const folderName = escapeHtml(record.fileName);\n const description = record.description ? escapeHtml(record.description) : '';\n const totalSize = formatFileSize(record.fileSize);\n const entryCount = record.directory?.entryCount ?? 0;\n const expiresDate = new Date(record.expiresAt).toLocaleDateString('zh-CN', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n const zipOnly = record.directory?.mode === 'zip-only' || !listing;\n const zipUrl = urls.zip('');\n\n const treeHtml = zipOnly ? '' : renderTree(listing!, urls);\n const breadcrumbs = zipOnly ? '' : renderBreadcrumbs(listing!.path, urls);\n\n const ogTitle = escapeHtml(options?.og?.title ?? record.fileName);\n const ogDesc = escapeHtml(options?.og?.description ?? `${entryCount} 项 · ${totalSize}`);\n const ogTags = renderOgTags({\n title: ogTitle,\n description: ogDesc,\n url: options?.og?.absoluteShareUrl ?? null,\n image: options?.og?.absoluteThumbnailUrl ?? null,\n });\n\n return `<!DOCTYPE html>\n<html lang=\"zh\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<meta name=\"referrer\" content=\"no-referrer\">\n<meta name=\"robots\" content=\"noindex,follow\">\n${ogTags}\n<title>${folderName} — xopc Share</title>\n<style>${commonCss()}</style>\n</head>\n<body>\n<div class=\"card\">\n<div class=\"icon\">📁</div>\n<div class=\"name\">${folderName}</div>\n<div class=\"meta\">${entryCount} 项 · ${totalSize} · 有效期至 ${expiresDate}</div>\n${description ? `<div class=\"desc\">${description}</div>` : ''}\n<div class=\"actions\">\n<a class=\"btn\" href=\"${escapeHtml(zipUrl)}\">⬇ 下载全部为 ZIP</a>\n</div>\n${breadcrumbs}\n${treeHtml}\n<div class=\"footer\">Shared via xopc</div>\n</div>\n</body>\n</html>`;\n}\n\nfunction renderBreadcrumbs(\n currentPath: string,\n urls: { tree: (path: string) => string },\n): string {\n if (!currentPath) return '<div class=\"crumb\"><a href=\"' + escapeHtml(urls.tree('')) + '\">/ 根目录</a></div>';\n const segments = currentPath.split('/').filter(Boolean);\n const links: string[] = [`<a href=\"${escapeHtml(urls.tree(''))}\">/ 根目录</a>`];\n let cumulative = '';\n for (const seg of segments) {\n cumulative = cumulative ? `${cumulative}/${seg}` : seg;\n links.push(`<a href=\"${escapeHtml(urls.tree(cumulative))}\">${escapeHtml(seg)}</a>`);\n }\n return `<div class=\"crumb\">${links.join(' / ')}</div>`;\n}\n\nfunction renderTree(\n listing: DirectoryListing,\n urls: { tree: (path: string) => string; file: (path: string) => string; zip: (path: string) => string },\n): string {\n if (listing.entries.length === 0) {\n return '<div class=\"tree\"><div class=\"empty\">空目录</div></div>';\n }\n const rows = listing.entries.map((entry) => renderTreeRow(entry, urls)).join('');\n const truncated = listing.truncated\n ? '<div class=\"empty\">列表过长已截断(仍可通过 URL 访问子目录)</div>'\n : '';\n return `<div class=\"tree\">${rows}${truncated}</div>`;\n}\n\nfunction renderTreeRow(\n entry: DirectoryListingEntry,\n urls: { tree: (path: string) => string; file: (path: string) => string; zip: (path: string) => string },\n): string {\n const safeName = escapeHtml(entry.name);\n if (entry.isDirectory) {\n return `<div class=\"row\">\n<a href=\"${escapeHtml(urls.tree(entry.path))}\"><span>📁</span><span>${safeName}/</span></a>\n<div class=\"act\">\n<a href=\"${escapeHtml(urls.zip(entry.path))}\">ZIP</a>\n</div>\n</div>`;\n }\n return `<div class=\"row\">\n<a href=\"${escapeHtml(urls.file(entry.path))}\"><span>📄</span><span>${safeName}</span></a>\n<div class=\"act\">\n<span class=\"size\">${formatFileSize(entry.size)}</span>\n<a href=\"${escapeHtml(urls.file(entry.path))}?dl=1\">下载</a>\n</div>\n</div>`;\n}\n\nexport type ShareExpiredReason = 'expired' | 'revoked' | 'max_views' | 'not_found' | 'file_deleted';\n\n/** Render the \"share no longer valid\" page — does NOT reveal file name or path. */\nexport function renderShareExpiredPage(reason: ShareExpiredReason): string {\n const reasons: Record<ShareExpiredReason, string> = {\n expired: '链接已过期',\n revoked: '链接已被创建者撤销',\n max_views: '已达最大访问次数',\n not_found: '链接无效或已失效',\n file_deleted: '分享的文件已不存在',\n };\n const reasonText = reasons[reason] ?? reasons.not_found;\n\n return `<!DOCTYPE html>\n<html lang=\"zh\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<meta name=\"referrer\" content=\"no-referrer\">\n<meta name=\"robots\" content=\"noindex,nofollow\">\n<title>分享已失效 — xopc</title>\n<style>${commonCss()}</style>\n</head>\n<body>\n<div class=\"card\" style=\"max-width:420px;text-align:center\">\n<div class=\"icon\">⚠️</div>\n<h1 style=\"font-size:1.125rem;font-weight:600;margin-bottom:1rem\">此分享链接已失效</h1>\n<div class=\"meta\">${reasonText}</div>\n<div class=\"footer\">如需访问请联系分享者获取新链接</div>\n</div>\n</body>\n</html>`;\n}\n"],"mappings":"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"}
@@ -1,18 +1,42 @@
1
1
  import type { ShareRecord, ShareConfig, CreateShareParams } from './share-types.js';
2
+ export interface DirectoryListingEntry {
3
+ name: string;
4
+ /** Workspace/share-relative POSIX path. */
5
+ path: string;
6
+ isDirectory: boolean;
7
+ size: number;
8
+ mtime: string;
9
+ mimeType: string;
10
+ }
11
+ export interface DirectoryListing {
12
+ /** Share-relative path of the listed dir ('' for root). */
13
+ path: string;
14
+ entries: DirectoryListingEntry[];
15
+ truncated: boolean;
16
+ }
2
17
  export declare class ShareStore {
3
18
  private shares;
4
19
  private tokenIndex;
5
- private viewCountDirty;
6
- private viewCountTimer;
20
+ private dirty;
21
+ private debounceTimer;
7
22
  private cleanupTimer;
8
23
  private config;
24
+ private listingCache;
25
+ /** Optional cleanup hook invoked when a share record is dropped (e.g. delete thumbnail). */
26
+ private onCleanup;
9
27
  constructor(config?: Partial<ShareConfig>);
10
28
  updateConfig(config: Partial<ShareConfig>): void;
29
+ /** Register a cleanup hook (idempotent — last wins). */
30
+ setCleanupHook(hook: (record: ShareRecord) => void): void;
11
31
  getConfig(): ShareConfig;
12
32
  create(params: CreateShareParams & {
13
33
  workspaceRoot: string;
14
34
  gatewayTokenHash: string;
15
35
  }): Promise<ShareRecord>;
36
+ private createFileShare;
37
+ private createDirectoryShare;
38
+ private buildRecord;
39
+ private persistAndAudit;
16
40
  getById(id: string): ShareRecord | null;
17
41
  getByToken(token: string): ShareRecord | null;
18
42
  /** Validate a share is still accessible for download. Returns null reason if valid. */
@@ -20,13 +44,30 @@ export declare class ShareStore {
20
44
  valid: boolean;
21
45
  reason?: string;
22
46
  };
23
- /** Increment view count (debounced persist). */
24
- incrementViewCount(id: string): void;
47
+ /** Increment download counter (used by directory & file downloads). Debounced persist. */
48
+ incrementDownloadCount(id: string): void;
25
49
  /** Check if the file still exists and inode matches. */
26
50
  validateFileIntegrity(record: ShareRecord): Promise<{
27
51
  valid: boolean;
28
52
  reason?: string;
29
53
  }>;
54
+ /**
55
+ * Resolve a child path inside a directory share. Returns the absolute path
56
+ * if it stays within both the share root and the workspace.
57
+ */
58
+ resolveDirectoryChild(record: ShareRecord, relativePath: string): Promise<{
59
+ ok: true;
60
+ absolutePath: string;
61
+ } | {
62
+ ok: false;
63
+ reason: string;
64
+ }>;
65
+ /** List a single directory level (cached, share-root-relative). */
66
+ listDirectory(record: ShareRecord, relativePath: string): Promise<DirectoryListing>;
67
+ /** Update thumbnail status. Persists immediately so generator restart is safe. */
68
+ setThumbnailStatus(id: string, status: 'pending' | 'ready' | 'failed'): void;
69
+ /** Drop the listing cache for a share (used on revoke/update). */
70
+ invalidateListingCache(shareId: string): void;
30
71
  revoke(id: string): boolean;
31
72
  revokeMany(ids: string[]): number;
32
73
  revokeExpired(): number;
@@ -44,5 +85,8 @@ export declare class ShareStore {
44
85
  shutdown(): void;
45
86
  private resolveAndValidatePath;
46
87
  }
88
+ export declare function resolveMimeType(fileName: string): string;
89
+ /** HTTP Content-Type with charset for text-like bodies (browser inline preview). */
90
+ export declare function shareResponseContentType(mime: string): string;
47
91
  export declare function getShareStore(config?: Partial<ShareConfig>): ShareStore;
48
92
  export declare function resetShareStoreForTests(): void;