@xopcai/xopc 0.0.86 → 0.0.88
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.
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/feishu/src/adapters/cli-login.js +3 -3
- package/dist/extensions/feishu/src/adapters/cli-login.js.map +1 -1
- package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
- package/dist/extensions/feishu/src/workflow-progress.js +1 -1
- package/dist/extensions/telegram/src/delivery-chat-id.d.ts +1 -1
- package/dist/extensions/telegram/src/delivery-chat-id.js +1 -1
- package/dist/extensions/telegram/src/delivery-chat-id.js.map +1 -1
- package/dist/extensions/telegram/src/plugin.js +1 -1
- package/dist/extensions/telegram/src/routing-integration.js +3 -2
- package/dist/extensions/telegram/src/routing-integration.js.map +1 -1
- package/dist/extensions/telegram/src/workflow-progress.js +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js +2 -2
- package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js.map +1 -1
- package/dist/extensions/weixin/src/api/api.js +3 -3
- package/dist/extensions/weixin/src/api/api.js.map +1 -1
- package/dist/extensions/weixin/src/auth/accounts.js +12 -12
- package/dist/extensions/weixin/src/auth/accounts.js.map +1 -1
- package/dist/extensions/weixin/src/cdn/upload.js +1 -1
- package/dist/extensions/weixin/src/delivery-to.js +2 -2
- package/dist/extensions/weixin/src/delivery-to.js.map +1 -1
- package/dist/extensions/weixin/src/media/data-url.js +1 -1
- package/dist/extensions/weixin/src/messaging/debug-mode.js +5 -5
- package/dist/extensions/weixin/src/messaging/debug-mode.js.map +1 -1
- package/dist/extensions/weixin/src/messaging/inbound.js +11 -11
- package/dist/extensions/weixin/src/messaging/inbound.js.map +1 -1
- package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
- package/dist/extensions/weixin/src/plugin.js +1 -1
- package/dist/extensions/weixin/src/storage/sync-buf.js +4 -4
- package/dist/extensions/weixin/src/storage/sync-buf.js.map +1 -1
- package/dist/extensions/weixin/src/workflow-progress.d.ts +1 -1
- package/dist/extensions/weixin/src/workflow-progress.js +1 -1
- package/dist/extensions/weixin/src/workflow-progress.js.map +1 -1
- package/dist/gateway/static/root/assets/agents-CRxETUZx.js +222 -0
- package/dist/gateway/static/root/assets/{apps-page-DrfytjOb.js → apps-page-wKWf3l57.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-DDbqVNkx.js +1 -0
- package/dist/gateway/static/root/assets/{channels-status-swr-Bs5kMCMI.js → channels-status-swr-DIsl75Y3.js} +1 -1
- package/dist/gateway/static/root/assets/copy-SxMW6Xpc.js +1 -0
- package/dist/gateway/static/root/assets/{cron-api-BuVcZ5zR.js → cron-api-N9hvuRrn.js} +1 -1
- package/dist/gateway/static/root/assets/{cron-page-BMrloeFH.js → cron-page-tlNGNxhP.js} +1 -1
- package/dist/gateway/static/root/assets/{dist-CKU1OOTf.js → dist-CJwfHYvT.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-BdW_46sN.js → extension-debug-page-BVJohZoZ.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-DW47KI82.js → extension-page-BT2tmElC.js} +1 -1
- package/dist/gateway/static/root/assets/extension-settings-page-BSS47c2j.js +1 -0
- package/dist/gateway/static/root/assets/{fetch-B2MYHbWg.js → fetch-BaFNUtkE.js} +1 -1
- package/dist/gateway/static/root/assets/{field-primitives-DPG-oJmx.js → field-primitives-QwYEq6Hz.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-config-api-C8dNts9i.js → heartbeat-config-api-BVSidEDJ.js} +1 -1
- package/dist/gateway/static/root/assets/index-CqZzHNEg.css +1 -0
- package/dist/gateway/static/root/assets/{index-BmVYculr.js → index-qNrVJp-y.js} +97 -95
- package/dist/gateway/static/root/assets/{logs-page-sTsVWz0X.js → logs-page-DDonPVLn.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-DKt-Wmib.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-DuvRQW--.js → settings-form-section-B8N3A3Zo.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-DcJjvvw4.js +3 -0
- package/dist/gateway/static/root/assets/{share-preview-page-BtG2kLDh.js → share-preview-page-Q7KqkO-u.js} +1 -1
- package/dist/gateway/static/root/assets/skills-page-DuJ4BTO3.js +2 -0
- package/dist/gateway/static/root/assets/{theme-store-DryYl3qD.js → theme-store-BbRc5ugR.js} +1 -1
- package/dist/gateway/static/root/assets/url-D6jvVYIA.js +7 -0
- package/dist/gateway/static/root/assets/{utils-BY7bU1DT.js → utils-CxDGduqK.js} +1 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-CTyHz7L_.js +1 -0
- package/dist/gateway/static/root/assets/workflows-page-GacJ41Fv.js +27 -0
- package/dist/gateway/static/root/index.html +6 -5
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.js +7 -7
- package/dist/src/agent/agent-scope.d.ts +4 -0
- package/dist/src/agent/agent-scope.js +53 -10
- package/dist/src/agent/agent-scope.js.map +1 -1
- package/dist/src/agent/bootstrap/filter-bootstrap-files.js +2 -1
- package/dist/src/agent/bootstrap/filter-bootstrap-files.js.map +1 -1
- package/dist/src/agent/bootstrap/load-bootstrap-files.js +1 -1
- package/dist/src/agent/child-agent-factory.d.ts +15 -0
- package/dist/src/agent/child-agent-factory.js +35 -2
- package/dist/src/agent/child-agent-factory.js.map +1 -1
- package/dist/src/agent/client-error-format.d.ts +20 -0
- package/dist/src/agent/client-error-format.js +97 -0
- package/dist/src/agent/client-error-format.js.map +1 -0
- package/dist/src/agent/context/workspace-seed.js +2 -2
- package/dist/src/agent/embedded/run-turn.js +23 -4
- package/dist/src/agent/embedded/run-turn.js.map +1 -1
- package/dist/src/agent/embedded/session-tool-result-guard.js +2 -1
- package/dist/src/agent/embedded/session-tool-result-guard.js.map +1 -1
- package/dist/src/agent/embedded/tool-result-truncation.js +2 -1
- package/dist/src/agent/embedded/tool-result-truncation.js.map +1 -1
- package/dist/src/agent/fallback/candidates.js +2 -2
- package/dist/src/agent/fallback/candidates.js.map +1 -1
- package/dist/src/agent/goals/goal-locale.d.ts +1 -1
- package/dist/src/agent/goals/goal-run-store.js +4 -4
- package/dist/src/agent/goals/persistent-goal-apis.d.ts +0 -2
- package/dist/src/agent/goals/persistent-goal-service.js +1 -2
- package/dist/src/agent/goals/persistent-goal-service.js.map +1 -1
- package/dist/src/agent/goals/post-turn.js +2 -2
- package/dist/src/agent/image/generation/normalization.js +2 -12
- package/dist/src/agent/image/generation/normalization.js.map +1 -1
- package/dist/src/agent/image/generation/provider-registry.d.ts +4 -8
- package/dist/src/agent/image/generation/provider-registry.js.map +1 -1
- package/dist/src/agent/image/generation/runtime.d.ts +2 -2
- package/dist/src/agent/image/generation/runtime.js.map +1 -1
- package/dist/src/agent/image/generation/types.d.ts +0 -18
- package/dist/src/agent/image/image-helpers.js +6 -1
- package/dist/src/agent/image/image-helpers.js.map +1 -1
- package/dist/src/agent/image/index.d.ts +1 -1
- package/dist/src/agent/image/load-image-media.js +2 -2
- package/dist/src/agent/inbound/inbound-loop.d.ts +5 -0
- package/dist/src/agent/inbound/inbound-loop.js +41 -10
- package/dist/src/agent/inbound/inbound-loop.js.map +1 -1
- package/dist/src/agent/inbound/turn-dispatcher.d.ts +4 -0
- package/dist/src/agent/inbound/turn-dispatcher.js +7 -5
- package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
- package/dist/src/agent/ipc/bus.js +1 -1
- package/dist/src/agent/ipc/inbox.js +2 -2
- package/dist/src/agent/ipc/socket.js +1 -1
- package/dist/src/agent/mcp/bundle-mcp-materialize.js +2 -1
- package/dist/src/agent/mcp/bundle-mcp-materialize.js.map +1 -1
- package/dist/src/agent/mcp/bundle-mcp-names.js +2 -1
- package/dist/src/agent/mcp/bundle-mcp-names.js.map +1 -1
- package/dist/src/agent/mcp/bundle-mcp-runtime.js +2 -1
- package/dist/src/agent/mcp/bundle-mcp-runtime.js.map +1 -1
- package/dist/src/agent/mcp/mcp-transport-config.js +2 -1
- package/dist/src/agent/mcp/mcp-transport-config.js.map +1 -1
- package/dist/src/agent/mcp/mcp-transport.js +2 -1
- package/dist/src/agent/mcp/mcp-transport.js.map +1 -1
- package/dist/src/agent/media-generation/runtime-shared.js +2 -9
- package/dist/src/agent/media-generation/runtime-shared.js.map +1 -1
- package/dist/src/agent/memory/builtin-memory-store.js +1 -1
- package/dist/src/agent/memory/dreaming/deep-promotion.js +1 -1
- package/dist/src/agent/memory/dreaming/events.js +1 -1
- package/dist/src/agent/memory/dreaming/last-run.js +1 -1
- package/dist/src/agent/memory/dreaming/light-sweep.js +1 -1
- package/dist/src/agent/memory/dreaming/preview.js +1 -1
- package/dist/src/agent/memory/dreaming/rem-patterns.js +1 -1
- package/dist/src/agent/memory/dreaming/short-term-store.js +1 -1
- package/dist/src/agent/memory/dreaming/utils.js +1 -1
- package/dist/src/agent/memory/plugin-discovery.js +1 -1
- package/dist/src/agent/messaging/command-handler.d.ts +6 -0
- package/dist/src/agent/messaging/command-handler.js +5 -0
- package/dist/src/agent/messaging/command-handler.js.map +1 -1
- package/dist/src/agent/models/manager.js +1 -1
- package/dist/src/agent/orchestration/llm-turn-retry.d.ts +2 -0
- package/dist/src/agent/orchestration/llm-turn-retry.js +9 -1
- package/dist/src/agent/orchestration/llm-turn-retry.js.map +1 -1
- package/dist/src/agent/prompt/safety.d.ts +0 -7
- package/dist/src/agent/prompt/safety.js +1 -20
- package/dist/src/agent/prompt/safety.js.map +1 -1
- package/dist/src/agent/prompt/service-prompt-builder.js +2 -2
- package/dist/src/agent/reply/post-compaction-context.js +1 -1
- package/dist/src/agent/reply/workspace-boundary-read.js +1 -1
- package/dist/src/agent/sandbox/path-policy.js +2 -2
- package/dist/src/agent/service/build-direct-message-content.js +2 -2
- package/dist/src/agent/service/build-direct-message-content.js.map +1 -1
- package/dist/src/agent/service/direct-turn-helpers.d.ts +3 -1
- package/dist/src/agent/service/direct-turn-helpers.js +6 -1
- package/dist/src/agent/service/direct-turn-helpers.js.map +1 -1
- package/dist/src/agent/service/process-direct-one-shot.d.ts +4 -0
- package/dist/src/agent/service/process-direct-one-shot.js +15 -2
- package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
- package/dist/src/agent/service/process-direct-streaming.d.ts +4 -0
- package/dist/src/agent/service/process-direct-streaming.js +53 -7
- package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
- package/dist/src/agent/service/webchat-tts.d.ts +1 -2
- package/dist/src/agent/service/webchat-tts.js +2 -2
- package/dist/src/agent/service/webchat-tts.js.map +1 -1
- package/dist/src/agent/service.d.ts +8 -0
- package/dist/src/agent/service.js +25 -5
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/session/session-inspector.js +1 -1
- package/dist/src/agent/skills/config.js +1 -1
- package/dist/src/agent/skills/hub-hash.js +2 -2
- package/dist/src/agent/skills/hub-lock.js +1 -1
- package/dist/src/agent/skills/hub-pull.js +2 -2
- package/dist/src/agent/skills/index.js +1 -1
- package/dist/src/agent/skills/managed-store.js +1 -1
- package/dist/src/agent/skills/scanner.js +1 -1
- package/dist/src/agent/skills/skill-manage-ops.js +1 -1
- package/dist/src/agent/skills/skill-manager.js +1 -1
- package/dist/src/agent/tools/create-share-tool.js +27 -20
- package/dist/src/agent/tools/create-share-tool.js.map +1 -1
- package/dist/src/agent/tools/dreaming-tool.js +1 -1
- package/dist/src/agent/tools/factory.js +2 -2
- package/dist/src/agent/tools/image-generate-tool.js +1 -1
- package/dist/src/agent/tools/index.d.ts +0 -1
- package/dist/src/agent/tools/index.js +4 -5
- package/dist/src/agent/tools/send-media.js +1 -1
- package/dist/src/agent/tools/shell.js +0 -13
- package/dist/src/agent/tools/shell.js.map +1 -1
- package/dist/src/agent/tools/skill-manage-tool.js +1 -1
- package/dist/src/agent/tools/workflow-tool.js +70 -16
- package/dist/src/agent/tools/workflow-tool.js.map +1 -1
- package/dist/src/agent/tools/write.js +1 -1
- package/dist/src/agent/workflow/agent-progress.d.ts +5 -0
- package/dist/src/agent/workflow/agent-progress.js +65 -0
- package/dist/src/agent/workflow/agent-progress.js.map +1 -0
- package/dist/src/agent/workflow/builtins/audit-repo.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/audit-repo.js +14 -0
- package/dist/src/agent/workflow/builtins/audit-repo.js.map +1 -1
- package/dist/src/agent/workflow/builtins/debug-incident.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/debug-incident.js +14 -0
- package/dist/src/agent/workflow/builtins/debug-incident.js.map +1 -1
- package/dist/src/agent/workflow/builtins/implementation-plan.d.ts +12 -0
- package/dist/src/agent/workflow/builtins/implementation-plan.js +175 -0
- package/dist/src/agent/workflow/builtins/implementation-plan.js.map +1 -0
- package/dist/src/agent/workflow/builtins/index.d.ts +3 -1
- package/dist/src/agent/workflow/builtins/index.js +11 -1
- package/dist/src/agent/workflow/builtins/index.js.map +1 -1
- package/dist/src/agent/workflow/builtins/multi-perspective-review.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/multi-perspective-review.js +14 -0
- package/dist/src/agent/workflow/builtins/multi-perspective-review.js.map +1 -1
- package/dist/src/agent/workflow/builtins/pr-review.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/pr-review.js +14 -0
- package/dist/src/agent/workflow/builtins/pr-review.js.map +1 -1
- package/dist/src/agent/workflow/builtins/release-check.d.ts +11 -0
- package/dist/src/agent/workflow/builtins/release-check.js +165 -0
- package/dist/src/agent/workflow/builtins/release-check.js.map +1 -0
- package/dist/src/agent/workflow/builtins/research.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/research.js +14 -0
- package/dist/src/agent/workflow/builtins/research.js.map +1 -1
- package/dist/src/agent/workflow/catalog.js +1 -1
- package/dist/src/agent/workflow/channel-capability.d.ts +3 -3
- package/dist/src/agent/workflow/index.d.ts +2 -1
- package/dist/src/agent/workflow/index.js +3 -2
- package/dist/src/agent/workflow/lint.d.ts +38 -0
- package/dist/src/agent/workflow/lint.js +74 -0
- package/dist/src/agent/workflow/lint.js.map +1 -0
- package/dist/src/agent/workflow/meta-locale.d.ts +12 -0
- package/dist/src/agent/workflow/meta-locale.js +62 -0
- package/dist/src/agent/workflow/meta-locale.js.map +1 -0
- package/dist/src/agent/workflow/parser.js +7 -1
- package/dist/src/agent/workflow/parser.js.map +1 -1
- package/dist/src/agent/workflow/runtime.d.ts +4 -1
- package/dist/src/agent/workflow/runtime.js +88 -8
- package/dist/src/agent/workflow/runtime.js.map +1 -1
- package/dist/src/agent/workflow/snapshot.js +2 -12
- package/dist/src/agent/workflow/snapshot.js.map +1 -1
- package/dist/src/agent/workflow/step-labels.d.ts +8 -0
- package/dist/src/agent/workflow/step-labels.js +48 -0
- package/dist/src/agent/workflow/step-labels.js.map +1 -0
- package/dist/src/agent/workflow/subagent-runner.js +46 -1
- package/dist/src/agent/workflow/subagent-runner.js.map +1 -1
- package/dist/src/agent/workflow/types.d.ts +76 -1
- package/dist/src/auth/credentials.d.ts +5 -0
- package/dist/src/auth/credentials.js +12 -3
- package/dist/src/auth/credentials.js.map +1 -1
- package/dist/src/auth/profiles/store.js +1 -1
- package/dist/src/auth/sync-provider-auth.js +1 -1
- package/dist/src/browser/cache-dir-policy.js +1 -1
- package/dist/src/browser/cdp-local-launcher.js +2 -2
- package/dist/src/browser/index.js +4 -4
- package/dist/src/browser/manager.d.ts +1 -3
- package/dist/src/browser/manager.js +0 -6
- package/dist/src/browser/manager.js.map +1 -1
- package/dist/src/browser/providers/browser-ext-install.d.ts +4 -4
- package/dist/src/browser/providers/browser-ext-install.js +41 -88
- package/dist/src/browser/providers/browser-ext-install.js.map +1 -1
- package/dist/src/browser/providers/cloakbrowser.d.ts +0 -5
- package/dist/src/browser/providers/cloakbrowser.js +6 -59
- package/dist/src/browser/providers/cloakbrowser.js.map +1 -1
- package/dist/src/browser/providers/playwright-doctor.js +1 -1
- package/dist/src/browser/stealth.js +1 -1
- package/dist/src/channels/attachments/inbound-persist.js +1 -1
- package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
- package/dist/src/channels/attachments/voice-stt-webchat.js +10 -8
- package/dist/src/channels/attachments/voice-stt-webchat.js.map +1 -1
- package/dist/src/channels/outbound/persist-store.js +1 -1
- package/dist/src/channels/pairing/allow-from-file.js +9 -9
- package/dist/src/channels/pairing/allow-from-file.js.map +1 -1
- package/dist/src/channels/pairing/pairing-store.js +7 -7
- package/dist/src/channels/pairing/pairing-store.js.map +1 -1
- package/dist/src/chat-commands/builtins/config.js +2 -2
- package/dist/src/chat-commands/builtins/session.js +1 -1
- package/dist/src/chat-commands/builtins/session.js.map +1 -1
- package/dist/src/chat-commands/builtins/tts.js +2 -2
- package/dist/src/chat-commands/builtins/tts.js.map +1 -1
- package/dist/src/chat-commands/context.d.ts +3 -0
- package/dist/src/chat-commands/context.js +22 -4
- package/dist/src/chat-commands/context.js.map +1 -1
- package/dist/src/chat-commands/session-key.d.ts +4 -37
- package/dist/src/chat-commands/session-key.js +49 -85
- package/dist/src/chat-commands/session-key.js.map +1 -1
- package/dist/src/chat-commands/types.d.ts +2 -0
- package/dist/src/cli/commands/agent/interactive.js +2 -2
- package/dist/src/cli/commands/agent/interactive.js.map +1 -1
- package/dist/src/cli/commands/agent/sessions.js +2 -2
- package/dist/src/cli/commands/agent/sessions.js.map +1 -1
- package/dist/src/cli/commands/agent.js +4 -5
- package/dist/src/cli/commands/agent.js.map +1 -1
- package/dist/src/cli/commands/channels.js +1 -5
- package/dist/src/cli/commands/channels.js.map +1 -1
- package/dist/src/cli/commands/config.js +1 -1
- package/dist/src/cli/commands/doctor/checks/config-health.js +1 -1
- package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
- package/dist/src/cli/commands/doctor/checks/session-integrity.js +1 -1
- package/dist/src/cli/commands/doctor/checks/state-integrity.js +1 -1
- package/dist/src/cli/commands/doctor/checks/workspace-status.js +1 -1
- package/dist/src/cli/commands/extension-dev.js +1 -1
- package/dist/src/cli/commands/extension-marketplace.js +1 -1
- package/dist/src/cli/commands/extension-pack.js +1 -1
- package/dist/src/cli/commands/gateway/lifecycle-core.js +1 -1
- package/dist/src/cli/commands/gateway/lifecycle-core.js.map +1 -1
- package/dist/src/cli/commands/gateway/logs.d.ts +9 -0
- package/dist/src/cli/commands/gateway/logs.js +50 -17
- package/dist/src/cli/commands/gateway/logs.js.map +1 -1
- package/dist/src/cli/commands/image.js +23 -22
- package/dist/src/cli/commands/image.js.map +1 -1
- package/dist/src/cli/commands/init.js +4 -4
- package/dist/src/cli/commands/onboard.js +1 -1
- package/dist/src/cli/commands/session/utils.js +2 -2
- package/dist/src/cli/commands/session/utils.js.map +1 -1
- package/dist/src/cli/commands/update.js +26 -46
- package/dist/src/cli/commands/update.js.map +1 -1
- package/dist/src/cli/utils/init-workspace-core.js +2 -2
- package/dist/src/cli/utils/session.d.ts +0 -5
- package/dist/src/cli/utils/session.js +1 -6
- package/dist/src/cli/utils/session.js.map +1 -1
- package/dist/src/commands/agents.config.js +1 -1
- package/dist/src/commands/agents.config.js.map +1 -1
- package/dist/src/config/agent-profile.js +6 -28
- package/dist/src/config/agent-profile.js.map +1 -1
- package/dist/src/config/agent-typed-models.d.ts +18 -0
- package/dist/src/config/agent-typed-models.js +53 -0
- package/dist/src/config/agent-typed-models.js.map +1 -0
- package/dist/src/config/gateway-bind.js +1 -1
- package/dist/src/config/index.js +6 -6
- package/dist/src/config/loader.js +2 -2
- package/dist/src/config/model-input.js +2 -5
- package/dist/src/config/model-input.js.map +1 -1
- package/dist/src/config/models-json.js +2 -2
- package/dist/src/config/paths-state.js +1 -1
- package/dist/src/config/profile.js +2 -2
- package/dist/src/config/schema.d.ts +253 -217
- package/dist/src/config/schema.js +91 -40
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/config/voice.d.ts +3 -28
- package/dist/src/config/voice.js +27 -261
- package/dist/src/config/voice.js.map +1 -1
- package/dist/src/config/workspace-path-helpers.d.ts +1 -2
- package/dist/src/config/workspace-path-helpers.js.map +1 -1
- package/dist/src/config/workspace-path.js +1 -1
- package/dist/src/cron/executor.js +2 -2
- package/dist/src/cron/persistence.js +1 -1
- package/dist/src/cron/run-log-store.js +1 -1
- package/dist/src/daemon/constants.js +1 -1
- package/dist/src/daemon/install-plan.js +27 -3
- package/dist/src/daemon/install-plan.js.map +1 -1
- package/dist/src/daemon/launchd.d.ts +8 -0
- package/dist/src/daemon/launchd.js +7 -14
- package/dist/src/daemon/launchd.js.map +1 -1
- package/dist/src/daemon/schtasks.d.ts +25 -0
- package/dist/src/daemon/schtasks.js +168 -48
- package/dist/src/daemon/schtasks.js.map +1 -1
- package/dist/src/daemon/service.js +5 -4
- package/dist/src/daemon/service.js.map +1 -1
- package/dist/src/daemon/systemd.d.ts +6 -0
- package/dist/src/daemon/systemd.js +20 -5
- package/dist/src/daemon/systemd.js.map +1 -1
- package/dist/src/extensions/activation-context.js +0 -1
- package/dist/src/extensions/activation-context.js.map +1 -1
- package/dist/src/extensions/bundle-mcp.js +1 -1
- package/dist/src/extensions/discover-extensions.js +1 -1
- package/dist/src/extensions/health.js +1 -1
- package/dist/src/extensions/loader.js +1 -1
- package/dist/src/extensions/lockfile.js +2 -2
- package/dist/src/extensions/normalize-manifest.js +0 -1
- package/dist/src/extensions/normalize-manifest.js.map +1 -1
- package/dist/src/extensions/types/manifest.d.ts +0 -2
- package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
- package/dist/src/gateway/agent-builtin-tools.js +1 -0
- package/dist/src/gateway/agent-builtin-tools.js.map +1 -1
- package/dist/src/gateway/agents-admin.d.ts +9 -0
- package/dist/src/gateway/agents-admin.js +28 -4
- package/dist/src/gateway/agents-admin.js.map +1 -1
- package/dist/src/gateway/config-tools-web.js +3 -2
- package/dist/src/gateway/config-tools-web.js.map +1 -1
- package/dist/src/gateway/file-path-classifier.js +2 -2
- package/dist/src/gateway/heartbeat/service.js +2 -2
- package/dist/src/gateway/heartbeat/service.js.map +1 -1
- package/dist/src/gateway/hono/app.js +1 -1
- package/dist/src/gateway/hono/lib/agent-model.d.ts +25 -10
- package/dist/src/gateway/hono/lib/agent-model.js +60 -36
- package/dist/src/gateway/hono/lib/agent-model.js.map +1 -1
- package/dist/src/gateway/hono/lib/config-payload.js +29 -6
- package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
- package/dist/src/gateway/hono/lib/extension-store.js +2 -2
- package/dist/src/gateway/hono/lib/mask-secret-length.d.ts +6 -0
- package/dist/src/gateway/hono/lib/mask-secret-length.js +16 -0
- package/dist/src/gateway/hono/lib/mask-secret-length.js.map +1 -0
- package/dist/src/gateway/hono/lib/safe-providers-config.d.ts +1 -1
- package/dist/src/gateway/hono/lib/safe-providers-config.js +2 -1
- package/dist/src/gateway/hono/lib/safe-providers-config.js.map +1 -1
- package/dist/src/gateway/hono/lib/safe-voice-config.js +16 -54
- package/dist/src/gateway/hono/lib/safe-voice-config.js.map +1 -1
- package/dist/src/gateway/hono/lib/static-ui.js +2 -2
- package/dist/src/gateway/hono/oauth.js +1 -1
- package/dist/src/gateway/hono/routes/agents.js +2 -2
- package/dist/src/gateway/hono/routes/auth-registry-extensions.js +1 -1
- package/dist/src/gateway/hono/routes/config-patch/agents.js +25 -7
- package/dist/src/gateway/hono/routes/config-patch/agents.js.map +1 -1
- package/dist/src/gateway/hono/routes/config-patch/channels.js +0 -11
- package/dist/src/gateway/hono/routes/config-patch/channels.js.map +1 -1
- package/dist/src/gateway/hono/routes/config-patch/gateway.js +3 -2
- package/dist/src/gateway/hono/routes/config-patch/gateway.js.map +1 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js +8 -3
- package/dist/src/gateway/hono/routes/config-patch/misc.js.map +1 -1
- package/dist/src/gateway/hono/routes/config.js +59 -0
- package/dist/src/gateway/hono/routes/config.js.map +1 -1
- package/dist/src/gateway/hono/routes/dreaming.js +1 -1
- package/dist/src/gateway/hono/routes/goals.js +1 -1
- package/dist/src/gateway/hono/routes/goals.js.map +1 -1
- package/dist/src/gateway/hono/routes/host-fs.js +2 -2
- package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
- package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
- package/dist/src/gateway/hono/routes/models.js +75 -12
- package/dist/src/gateway/hono/routes/models.js.map +1 -1
- package/dist/src/gateway/hono/routes/sessions.js +28 -7
- package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
- package/dist/src/gateway/hono/routes/shares.js +15 -13
- package/dist/src/gateway/hono/routes/shares.js.map +1 -1
- package/dist/src/gateway/hono/routes/tunnel.js +1 -1
- package/dist/src/gateway/hono/routes/update.js +4 -2
- package/dist/src/gateway/hono/routes/update.js.map +1 -1
- package/dist/src/gateway/hono/routes/voice.js +75 -0
- package/dist/src/gateway/hono/routes/voice.js.map +1 -1
- package/dist/src/gateway/hono/routes/workflows.d.ts +3 -0
- package/dist/src/gateway/hono/routes/workflows.js +347 -0
- package/dist/src/gateway/hono/routes/workflows.js.map +1 -0
- package/dist/src/gateway/hono/routes/workspace.js +4 -4
- package/dist/src/gateway/hono/sse.js +16 -33
- package/dist/src/gateway/hono/sse.js.map +1 -1
- package/dist/src/gateway/lock.js +11 -11
- package/dist/src/gateway/lock.js.map +1 -1
- package/dist/src/gateway/ports.js +6 -6
- package/dist/src/gateway/ports.js.map +1 -1
- package/dist/src/gateway/resolve-webchat-session-key.d.ts +19 -0
- package/dist/src/gateway/resolve-webchat-session-key.js +46 -0
- package/dist/src/gateway/resolve-webchat-session-key.js.map +1 -0
- package/dist/src/gateway/service/agent-runner.js +2 -2
- package/dist/src/gateway/service/marketplace-service.js +2 -2
- package/dist/src/gateway/service/run-gateway-agent.js +9 -11
- package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
- package/dist/src/gateway/service/sessions-api.d.ts +3 -0
- package/dist/src/gateway/service/sessions-api.js +8 -0
- package/dist/src/gateway/service/sessions-api.js.map +1 -1
- package/dist/src/gateway/service.d.ts +3 -2
- package/dist/src/gateway/service.js +9 -8
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/session-reset-service.d.ts +20 -0
- package/dist/src/gateway/session-reset-service.js +54 -0
- package/dist/src/gateway/session-reset-service.js.map +1 -0
- package/dist/src/gateway/startup-readiness.d.ts +1 -1
- package/dist/src/gateway/startup-readiness.js +1 -0
- package/dist/src/gateway/startup-readiness.js.map +1 -1
- package/dist/src/gateway/workspace-fs-file-list.js +1 -1
- package/dist/src/heartbeat/index.js +1 -1
- package/dist/src/infra/gateway-processes.js +2 -2
- package/dist/src/infra/gateway-processes.js.map +1 -1
- package/dist/src/infra/restart.js +2 -2
- package/dist/src/infra/run-command.d.ts +16 -0
- package/dist/src/infra/run-command.js +67 -0
- package/dist/src/infra/run-command.js.map +1 -0
- package/dist/src/infra/update-check.js +1 -1
- package/dist/src/infra/update-global.d.ts +45 -0
- package/dist/src/infra/update-global.js +224 -0
- package/dist/src/infra/update-global.js.map +1 -0
- package/dist/src/infra/update-lock.js +3 -3
- package/dist/src/infra/update-runner.js +1 -1
- package/dist/src/infra/update-startup.js +2 -2
- package/dist/src/infra/write-file-atomic.js +2 -2
- package/dist/src/mcp/channel-shared.js +2 -1
- package/dist/src/mcp/channel-shared.js.map +1 -1
- package/dist/src/providers/auth-runtime/auth-profile-store.js +2 -2
- package/dist/src/providers/auth-runtime/auth-profile-store.js.map +1 -1
- package/dist/src/providers/auth-runtime/resolve-auth.js +1 -12
- package/dist/src/providers/auth-runtime/resolve-auth.js.map +1 -1
- package/dist/src/providers/auth-runtime/types.d.ts +6 -12
- package/dist/src/providers/index.js +2 -2
- package/dist/src/providers/model-registry.js +1 -1
- package/dist/src/routing/agent-session-key.d.ts +58 -0
- package/dist/src/routing/agent-session-key.js +164 -0
- package/dist/src/routing/agent-session-key.js.map +1 -0
- package/dist/src/routing/index.d.ts +1 -1
- package/dist/src/routing/index.js +4 -2
- package/dist/src/routing/index.js.map +1 -1
- package/dist/src/routing/resolve-route.d.ts +15 -0
- package/dist/src/routing/resolve-route.js +41 -20
- package/dist/src/routing/resolve-route.js.map +1 -1
- package/dist/src/routing/resolve-tui-session-key.d.ts +25 -0
- package/dist/src/routing/resolve-tui-session-key.js +54 -0
- package/dist/src/routing/resolve-tui-session-key.js.map +1 -0
- package/dist/src/routing/session-key-utils.d.ts +24 -0
- package/dist/src/routing/session-key-utils.js +92 -0
- package/dist/src/routing/session-key-utils.js.map +1 -0
- package/dist/src/routing/session-key.d.ts +19 -49
- package/dist/src/routing/session-key.js +143 -116
- package/dist/src/routing/session-key.js.map +1 -1
- package/dist/src/session/config-store.js +2 -2
- package/dist/src/session/index.d.ts +6 -0
- package/dist/src/session/index.js +7 -1
- package/dist/src/session/init-session-turn.d.ts +30 -0
- package/dist/src/session/init-session-turn.js +102 -0
- package/dist/src/session/init-session-turn.js.map +1 -0
- package/dist/src/session/lifecycle-timestamps.d.ts +8 -0
- package/dist/src/session/lifecycle-timestamps.js +16 -0
- package/dist/src/session/lifecycle-timestamps.js.map +1 -0
- package/dist/src/session/manager.d.ts +7 -1
- package/dist/src/session/manager.js +8 -1
- package/dist/src/session/manager.js.map +1 -1
- package/dist/src/session/parity/jsonl-transcript-io.js +2 -2
- package/dist/src/session/parity/sessions-json-file.js +1 -1
- package/dist/src/session/parity/transcript-file-lock.js +2 -2
- package/dist/src/session/parity/transcript-paths.js +2 -2
- package/dist/src/session/parity/transcript-paths.js.map +1 -1
- package/dist/src/session/parity/xopc-session-disk-entry.d.ts +6 -0
- package/dist/src/session/reset-policy.d.ts +32 -0
- package/dist/src/session/reset-policy.js +65 -0
- package/dist/src/session/reset-policy.js.map +1 -0
- package/dist/src/session/reset-triggers.d.ts +20 -0
- package/dist/src/session/reset-triggers.js +63 -0
- package/dist/src/session/reset-triggers.js.map +1 -0
- package/dist/src/session/reset-type.d.ts +12 -0
- package/dist/src/session/reset-type.js +25 -0
- package/dist/src/session/reset-type.js.map +1 -0
- package/dist/src/session/resolve-session.d.ts +30 -0
- package/dist/src/session/resolve-session.js +93 -0
- package/dist/src/session/resolve-session.js.map +1 -0
- package/dist/src/session/search-index-cache.js +1 -1
- package/dist/src/session/search-index.js +1 -1
- package/dist/src/session/session-title.js +3 -2
- package/dist/src/session/session-title.js.map +1 -1
- package/dist/src/session/store.d.ts +11 -4
- package/dist/src/session/store.js +62 -11
- package/dist/src/session/store.js.map +1 -1
- package/dist/src/session/transcript-events.js +2 -1
- package/dist/src/session/transcript-events.js.map +1 -1
- package/dist/src/share/share-auto.js +2 -2
- package/dist/src/share/share-store.js +3 -3
- package/dist/src/share/share-thumbnail.js +2 -2
- package/dist/src/share/share-url.d.ts +33 -0
- package/dist/src/share/share-url.js +56 -14
- package/dist/src/share/share-url.js.map +1 -1
- package/dist/src/share/share-zip.js +1 -1
- package/dist/src/share/site-share-store.js +3 -3
- package/dist/src/share/site-static-serve.js +1 -1
- package/dist/src/tui/backends/embedded-backend.js +4 -9
- package/dist/src/tui/backends/embedded-backend.js.map +1 -1
- package/dist/src/tui/backends/gateway-sse-backend.js +1 -1
- package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
- package/dist/src/tui/clipboard-image.js +3 -3
- package/dist/src/tui/components/chat-log.js +3 -3
- package/dist/src/tui/components/chat-log.js.map +1 -1
- package/dist/src/tui/theme-manager.js +1 -1
- package/dist/src/tui/theme.d.ts +0 -2
- package/dist/src/tui/theme.js +1 -3
- package/dist/src/tui/theme.js.map +1 -1
- package/dist/src/tui/tui-agent-events.js +2 -1
- package/dist/src/tui/tui-agent-events.js.map +1 -1
- package/dist/src/tui/tui-commands.d.ts +3 -0
- package/dist/src/tui/tui-commands.js +45 -10
- package/dist/src/tui/tui-commands.js.map +1 -1
- package/dist/src/tui/tui-keybindings-file.js +2 -22
- package/dist/src/tui/tui-keybindings-file.js.map +1 -1
- package/dist/src/tui/tui-scoped-models.js +2 -2
- package/dist/src/tui/tui-session-actions.d.ts +28 -0
- package/dist/src/tui/tui-session-actions.js +88 -0
- package/dist/src/tui/tui-session-actions.js.map +1 -0
- package/dist/src/tui/tui-settings.js +1 -1
- package/dist/src/tui/tui.js +54 -49
- package/dist/src/tui/tui.js.map +1 -1
- package/dist/src/tunnel/frpc-binary.js +3 -3
- package/dist/src/tunnel/frpc-config.js +1 -1
- package/dist/src/tunnel/frpc-extract.js +1 -1
- package/dist/src/tunnel/tunnel-state.js +1 -1
- package/dist/src/utils/logger/audit.js +1 -1
- package/dist/src/utils/logger/log-store.js +1 -1
- package/dist/src/utils/logger/rotation.js +1 -1
- package/dist/src/utils/string-coerce.d.ts +2 -0
- package/dist/src/utils/string-coerce.js +10 -1
- package/dist/src/utils/string-coerce.js.map +1 -1
- package/dist/src/voice/metadata/builtin.d.ts +2 -0
- package/dist/src/voice/metadata/builtin.js +420 -0
- package/dist/src/voice/metadata/builtin.js.map +1 -0
- package/dist/src/voice/metadata/index.d.ts +4 -0
- package/dist/src/voice/metadata/index.js +3 -0
- package/dist/src/voice/metadata/registry.d.ts +5 -0
- package/dist/src/voice/metadata/registry.js +34 -0
- package/dist/src/voice/metadata/registry.js.map +1 -0
- package/dist/src/voice/metadata/types.d.ts +41 -0
- package/dist/src/voice/metadata/types.js +1 -0
- package/dist/src/voice/stt/config-slice.d.ts +2 -5
- package/dist/src/voice/stt/config-slice.js +5 -26
- package/dist/src/voice/stt/config-slice.js.map +1 -1
- package/dist/src/voice/stt/list-providers.d.ts +3 -3
- package/dist/src/voice/stt/list-providers.js +41 -6
- package/dist/src/voice/stt/list-providers.js.map +1 -1
- package/dist/src/voice/stt/types.d.ts +1 -18
- package/dist/src/voice/stt/types.js +4 -2
- package/dist/src/voice/stt/types.js.map +1 -1
- package/dist/src/voice/tts/audio.js +1 -1
- package/dist/src/voice/tts/config-slice.d.ts +3 -7
- package/dist/src/voice/tts/config-slice.js +7 -38
- package/dist/src/voice/tts/config-slice.js.map +1 -1
- package/dist/src/voice/tts/list-providers.d.ts +3 -3
- package/dist/src/voice/tts/list-providers.js +41 -6
- package/dist/src/voice/tts/list-providers.js.map +1 -1
- package/dist/src/voice/tts/merge-config.js +2 -48
- package/dist/src/voice/tts/merge-config.js.map +1 -1
- package/dist/src/voice/tts/providers/alibaba-speech.js +1 -1
- package/dist/src/voice/tts/providers/alibaba-speech.js.map +1 -1
- package/dist/src/voice/tts/providers/edge-speech.js +2 -2
- package/dist/src/voice/tts/types.d.ts +1 -29
- package/dist/src/voice/tts/types.js +19 -17
- package/dist/src/voice/tts/types.js.map +1 -1
- package/dist/src/workflows/domain/command.d.ts +18 -0
- package/dist/src/workflows/domain/command.js +1 -0
- package/dist/src/workflows/domain/definition.d.ts +62 -0
- package/dist/src/workflows/domain/definition.js +1 -0
- package/dist/src/workflows/domain/event.d.ts +67 -0
- package/dist/src/workflows/domain/event.js +1 -0
- package/dist/src/workflows/domain/index.d.ts +5 -0
- package/dist/src/workflows/domain/index.js +2 -0
- package/dist/src/workflows/domain/result.d.ts +65 -0
- package/dist/src/workflows/domain/result.js +1 -0
- package/dist/src/workflows/domain/run.d.ts +120 -0
- package/dist/src/workflows/domain/run.js +14 -0
- package/dist/src/workflows/domain/run.js.map +1 -0
- package/dist/src/workflows/engine/index.d.ts +2 -0
- package/dist/src/workflows/engine/index.js +3 -0
- package/dist/src/workflows/engine/projector.d.ts +3 -0
- package/dist/src/workflows/engine/projector.js +205 -0
- package/dist/src/workflows/engine/projector.js.map +1 -0
- package/dist/src/workflows/engine/workflow-engine.d.ts +31 -0
- package/dist/src/workflows/engine/workflow-engine.js +188 -0
- package/dist/src/workflows/engine/workflow-engine.js.map +1 -0
- package/dist/src/workflows/index.d.ts +6 -0
- package/dist/src/workflows/index.js +11 -0
- package/dist/src/workflows/runtime/index.d.ts +1 -0
- package/dist/src/workflows/runtime/index.js +4 -0
- package/dist/src/workflows/runtime/script-runtime.d.ts +3 -0
- package/dist/src/workflows/runtime/script-runtime.js +3 -0
- package/dist/src/workflows/store/event-store.d.ts +17 -0
- package/dist/src/workflows/store/event-store.js +83 -0
- package/dist/src/workflows/store/event-store.js.map +1 -0
- package/dist/src/workflows/store/paths.d.ts +7 -0
- package/dist/src/workflows/store/paths.js +26 -0
- package/dist/src/workflows/store/paths.js.map +1 -0
- package/dist/src/workflows/store/run-store.d.ts +13 -0
- package/dist/src/workflows/store/run-store.js +68 -0
- package/dist/src/workflows/store/run-store.js.map +1 -0
- package/package.json +5 -8
- package/dist/gateway/static/root/assets/agents-mS3_HpRI.js +0 -222
- package/dist/gateway/static/root/assets/channels-settings-BG6b9KrW.js +0 -1
- package/dist/gateway/static/root/assets/extension-settings-page-B-W4x2xP.js +0 -1
- package/dist/gateway/static/root/assets/index-ew_2L2We.css +0 -1
- package/dist/gateway/static/root/assets/sessions-page-FaG_Vlkb.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-Bet1OerL.js +0 -3
- package/dist/gateway/static/root/assets/skills-page-DhUO235y.js +0 -2
- package/dist/gateway/static/root/assets/url-BwNL6Rgk.js +0 -3
- package/dist/gateway/static/root/assets/voice-api-key-field-CGEydndO.js +0 -1
- package/dist/src/agent/tools/browser-legacy-tools.d.ts +0 -17
- package/dist/src/agent/tools/browser-legacy-tools.js +0 -766
- package/dist/src/agent/tools/browser-legacy-tools.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessions.js","names":[],"sources":["../../../../../src/gateway/hono/routes/sessions.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport { buildSessionKey, parseSessionKey } from '../../../routing/session-key.js';\nimport { agentExists, getDefaultAgentId } from '../../../routing/resolve-route.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\nimport { messagesToClientHistory } from '../../../session/client-history.js';\nimport { computeUserRoundDeleteRange } from '../../../session/user-round-delete.js';\nimport { respondStartupUnavailable } from '../lib/startup-unavailable.js';\nimport type { StartupUnavailableGatewayMethod } from '../../startup-readiness.js';\n\ntype SessionsStartupMethod = StartupUnavailableGatewayMethod;\n\nfunction ensureGatewayReadyForSessions(\n c: Parameters<typeof respondStartupUnavailable>[0],\n service: AuthenticatedRouteDeps['service'],\n method: SessionsStartupMethod,\n): Response | null {\n if (service.isGatewayReady()) {\n return null;\n }\n return respondStartupUnavailable(c, method);\n}\n\nexport function registerSessionsRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service } = deps;\n\n // ========== Session REST API (/api/sessions) ==========\n\n // POST /api/sessions - Create new session (reuses empty sessions)\n authenticated.post('/api/sessions', async (c) => {\n const body = await c.req.json().catch(() => ({}));\n const channel = body.channel || 'webchat';\n const routingCfg = service.currentConfig;\n let agentId =\n typeof body.agentId === 'string' && body.agentId.trim()\n ? body.agentId.trim().toLowerCase()\n : getDefaultAgentId(routingCfg);\n if (!agentExists(agentId, routingCfg)) {\n agentId = getDefaultAgentId(routingCfg);\n }\n\n // If a specific chat_id is provided, use it (for advanced use cases)\n // Otherwise, try to find and reuse an existing empty session\n if (body.chat_id) {\n const sessionKey = buildSessionKey({\n agentId,\n source: channel,\n accountId: 'default',\n peerKind: 'direct',\n peerId: body.chat_id,\n });\n\n await service.sessionManagerInstance.saveMessages(sessionKey, []);\n const session = await service.sessions.getSession(sessionKey);\n return c.json({ session }, 201);\n }\n\n // Look for existing empty sessions to reuse\n const existingSessions = await service.sessions.listSessions({\n channel,\n limit: 50,\n sortBy: 'updatedAt',\n sortOrder: 'desc',\n });\n \n // Reuse an empty session only when it matches the requested agent (session key embeds agent id).\n const emptySession = existingSessions.items.find((s) => {\n if (s.messageCount !== 0) return false;\n const parsed = parseSessionKey(s.key);\n return parsed?.agentId === agentId;\n });\n \n if (emptySession) {\n // Return existing empty session instead of creating a new one\n const session = await service.sessions.getSession(emptySession.key);\n return c.json({ session, reused: true }, 200);\n }\n \n // No empty session found, create a new one\n const chatId = `chat_${Date.now()}`;\n const sessionKey = buildSessionKey({\n agentId,\n source: channel,\n accountId: 'default',\n peerKind: 'direct',\n peerId: chatId,\n });\n\n await service.sessionManagerInstance.saveMessages(sessionKey, []);\n\n const session = await service.sessions.getSession(sessionKey);\n return c.json({ session }, 201);\n });\n\n // GET /api/sessions - List sessions\n authenticated.get('/api/sessions', async (c) => {\n const blocked = ensureGatewayReadyForSessions(c, service, 'sessions.list');\n if (blocked) {\n return blocked;\n }\n const query = c.req.query();\n const result = await service.sessions.listSessions({\n status: query.status as any,\n search: query.search,\n channel: query.channel,\n limit: query.limit ? parseInt(query.limit) : undefined,\n offset: query.offset ? parseInt(query.offset) : undefined,\n });\n return c.json(result);\n });\n\n // GET /api/sessions/stats - Get session stats (must be before /:key)\n authenticated.get('/api/sessions/stats', async (c) => {\n const result = await service.sessions.stats();\n return c.json(result);\n });\n\n // GET /api/sessions/chat-ids - Get unique chat IDs from sessions (must be before /:key)\n authenticated.get('/api/sessions/chat-ids', async (c) => {\n const channel = c.req.query('channel');\n const chatIds = await service.sessions.chatIds(channel || undefined);\n return c.json({ ok: true, payload: { chatIds } });\n });\n\n // GET /api/sessions/:key/run — read-only active webchat agent run (for UI resume)\n authenticated.get('/api/sessions/:key/run', async (c) => {\n const blocked = ensureGatewayReadyForSessions(c, service, 'sessions.run');\n if (blocked) {\n return blocked;\n }\n const key = c.req.param('key');\n const session = await service.sessions.getSession(key);\n if (!session) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n return c.json({ ok: true, payload: service.sessions.getActiveRun(key) });\n });\n\n // GET /api/sessions/:key/agent-config — resolved session agent settings (thinking, etc.)\n authenticated.get('/api/sessions/:key/agent-config', async (c) => {\n const key = c.req.param('key');\n const payload = await service.sessions.getAgentConfig(key);\n return c.json({ ok: true, payload });\n });\n\n authenticated.patch('/api/sessions/:key/agent-config', async (c) => {\n const key = c.req.param('key');\n const body = await c.req.json().catch(() => ({}));\n const result = await service.sessions.patchAgentConfig(key, body);\n if (!result.ok) {\n return c.json({ ok: false, error: result.error }, 400);\n }\n return c.json({ ok: true });\n });\n\n // GET /api/sessions/:key/messages — flattened transcript for TUI / clients\n authenticated.get('/api/sessions/:key/messages', async (c) => {\n const blocked = ensureGatewayReadyForSessions(c, service, 'sessions.messages');\n if (blocked) {\n return blocked;\n }\n const key = c.req.param('key');\n const limitRaw = c.req.query('limit');\n const parsedLimit = limitRaw ? Number.parseInt(limitRaw, 10) : undefined;\n const limit =\n parsedLimit !== undefined && Number.isFinite(parsedLimit)\n ? Math.min(500, Math.max(1, parsedLimit))\n : undefined;\n\n const before = c.req.query('before')?.trim();\n const offsetRaw = c.req.query('offset');\n const parsedOffset = offsetRaw ? Number.parseInt(offsetRaw, 10) : 0;\n const offset = Number.isFinite(parsedOffset) ? Math.max(0, parsedOffset) : 0;\n\n const result = await service.sessions.getMessagePage(key, {\n limit,\n offset,\n ...(before ? { before } : {}),\n });\n if (!result) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n\n const messages = messagesToClientHistory(result.session.messages, { limit });\n return c.json({\n ok: true,\n payload: { messages },\n pagination: result.pagination,\n });\n });\n\n // GET /api/sessions/:key/history — UI chat history page from the newest tail.\n authenticated.get('/api/sessions/:key/history', async (c) => {\n const blocked = ensureGatewayReadyForSessions(c, service, 'sessions.history');\n if (blocked) {\n return blocked;\n }\n const key = c.req.param('key');\n const offsetRaw = c.req.query('offset');\n const limitRaw = c.req.query('limit');\n const before = c.req.query('before')?.trim();\n const parsedOffset = offsetRaw ? Number.parseInt(offsetRaw, 10) : 0;\n const parsedLimit = limitRaw ? Number.parseInt(limitRaw, 10) : 50;\n const offset = Number.isFinite(parsedOffset) ? Math.max(0, parsedOffset) : 0;\n const limit = Number.isFinite(parsedLimit) ? Math.min(200, Math.max(1, parsedLimit)) : 50;\n const result = await service.sessions.getMessagePage(key, {\n offset,\n limit,\n ...(before ? { before } : {}),\n });\n\n if (!result) {\n return c.json({ error: 'Session not found' }, 404);\n }\n\n return c.json(result);\n });\n\n // POST /api/sessions/:key/transcript/context — append persisted-only `kind: 'context'` row (not in LLM context)\n authenticated.post('/api/sessions/:key/transcript/context', async (c) => {\n const key = c.req.param('key');\n const meta = await service.sessionManagerInstance.getSessionMetadata(key);\n if (!meta) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n const body = await c.req.json().catch(() => ({}));\n const id = typeof body.id === 'string' && body.id.trim() ? body.id.trim() : undefined;\n const text = typeof body.text === 'string' ? body.text : undefined;\n const data =\n body.data !== undefined && typeof body.data === 'object' && body.data !== null && !Array.isArray(body.data)\n ? (body.data as Record<string, unknown>)\n : undefined;\n await service.sessionManagerInstance.appendTranscriptContextEntry(key, { id, text, data });\n return c.json({ ok: true });\n });\n\n // GET /api/sessions/:key/compaction/checkpoints — list pre-compaction snapshots (OpenClaw-style)\n authenticated.get('/api/sessions/:key/compaction/checkpoints', async (c) => {\n const key = c.req.param('key');\n const meta = await service.sessionManagerInstance.getSessionMetadata(key);\n if (!meta) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n const checkpoints = await service.sessions.listCompactionCheckpoints(key);\n return c.json({ ok: true, payload: { checkpoints } });\n });\n\n authenticated.get('/api/sessions/:key/compaction/checkpoints/:checkpointId', async (c) => {\n const key = c.req.param('key');\n const checkpointId = c.req.param('checkpointId');\n const checkpoint = await service.sessions.getCompactionCheckpoint(key, checkpointId);\n if (!checkpoint) {\n return c.json({ ok: false, error: 'Checkpoint not found' }, 404);\n }\n return c.json({ ok: true, payload: { checkpoint } });\n });\n\n authenticated.post('/api/sessions/:key/compaction/restore', async (c) => {\n const key = c.req.param('key');\n const body = await c.req.json().catch(() => ({}));\n const checkpointId = typeof body.checkpointId === 'string' ? body.checkpointId.trim() : '';\n if (!checkpointId) {\n return c.json({ ok: false, error: 'checkpointId required' }, 400);\n }\n try {\n await service.sessions.restoreCompactionCheckpoint(key, checkpointId);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (msg.includes('not found') || msg.includes('Invalid')) {\n return c.json({ ok: false, error: msg }, 404);\n }\n return c.json({ ok: false, error: msg }, 500);\n }\n const session = await service.sessions.getSession(key);\n if (!session) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n return c.json({ ok: true, session });\n });\n\n authenticated.post('/api/sessions/:key/compaction/run', async (c) => {\n const key = c.req.param('key');\n const session = await service.sessions.getSession(key);\n if (!session) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n const body = await c.req.json().catch(() => ({}));\n const instructions = typeof body.instructions === 'string' ? body.instructions : undefined;\n const force = typeof body.force === 'boolean' ? body.force : true;\n const result = await service.sessions.runCompaction(key, { instructions, force });\n return c.json({ ok: true, payload: { result } });\n });\n\n // GET /api/sessions/:key - Get single session (must be after /stats and /chat-ids)\n authenticated.get('/api/sessions/:key', async (c) => {\n const key = c.req.param('key');\n const includeRaw = c.req.query('include') ?? '';\n const includeSet = new Set(\n includeRaw\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean),\n );\n const includeTranscript = includeSet.has('transcript');\n const includeTranscriptRows = includeSet.has('transcriptRows');\n const offsetRaw = c.req.query('offset');\n const limitRaw = c.req.query('limit');\n const hasPagingQuery = offsetRaw !== undefined || limitRaw !== undefined;\n\n if (hasPagingQuery) {\n const blocked = ensureGatewayReadyForSessions(c, service, 'sessions.history');\n if (blocked) {\n return blocked;\n }\n const parsedOffset = offsetRaw ? Number.parseInt(offsetRaw, 10) : 0;\n const parsedLimit = limitRaw ? Number.parseInt(limitRaw, 10) : 50;\n const offset = Number.isFinite(parsedOffset) ? Math.max(0, parsedOffset) : 0;\n const limit = Number.isFinite(parsedLimit) ? Math.min(200, Math.max(1, parsedLimit)) : 50;\n const result = await service.sessions.getMessagePage(key, {\n offset,\n limit,\n includeTranscriptSummary: includeTranscript,\n includeTranscriptRows,\n });\n if (!result) {\n return c.json({ error: 'Session not found' }, 404);\n }\n return c.json(result);\n }\n\n const session = await service.sessions.getSession(key, {\n includeTranscriptSummary: includeTranscript,\n includeTranscriptRows,\n });\n if (!session) {\n return c.json({ error: 'Session not found' }, 404);\n }\n return c.json({ session });\n });\n\n // PATCH /api/sessions/:key - Partial metadata (name, tags, customData); OpenClaw-style patch subset\n authenticated.patch('/api/sessions/:key', async (c) => {\n const key = c.req.param('key');\n const body = await c.req.json().catch(() => ({}));\n const patch: {\n name?: string;\n tags?: string[];\n replaceTags?: boolean;\n customData?: Record<string, unknown>;\n } = {};\n if (typeof body.name === 'string') {\n patch.name = body.name;\n }\n if (Array.isArray(body.tags)) {\n patch.tags = body.tags;\n }\n if (typeof body.replaceTags === 'boolean') {\n patch.replaceTags = body.replaceTags;\n }\n if (body.customData !== undefined && typeof body.customData === 'object' && body.customData !== null) {\n patch.customData = body.customData as Record<string, unknown>;\n }\n const result = await service.sessions.patch(key, patch);\n if (result.ok === false) {\n return c.json({ ok: false, error: result.error }, 404);\n }\n const session = await service.sessions.getSession(key);\n return c.json({ ok: true, session });\n });\n\n // GET /api/sessions/:key/export - Export session (must be before /:key)\n authenticated.get('/api/sessions/:key/export', async (c) => {\n const key = c.req.param('key');\n const format = c.req.query('format') as any || 'json';\n const result = await service.sessions.export(key, format);\n return c.json(result);\n });\n\n // DELETE /api/sessions/:key/messages — delete LLM rows by range or by user turn.\n // `userRoundIndex` (0-based among user messages) removes the user row and every following\n // assistant / tool / toolResult row until the next user. Prefer this from the web console so\n // tool loops are not left orphaned after retry/delete.\n authenticated.delete('/api/sessions/:key/messages', async (c) => {\n const key = c.req.param('key');\n const body = await c.req.json().catch(() => ({}));\n const loaded = await service.sessionManagerInstance.loadMessages(key);\n if (!loaded) {\n return c.json({ error: 'Session not found' }, 404);\n }\n\n let startIndex = typeof body.startIndex === 'number' ? body.startIndex : -1;\n let count = typeof body.count === 'number' ? body.count : 0;\n const userRoundIndex =\n typeof body.userRoundIndex === 'number' ? body.userRoundIndex : undefined;\n\n if (userRoundIndex !== undefined) {\n const range = computeUserRoundDeleteRange(loaded, userRoundIndex);\n if (!range) {\n return c.json({ error: 'User round index out of range' }, 400);\n }\n startIndex = range.startIndex;\n count = range.count;\n }\n\n if (startIndex < 0 || count <= 0) {\n return c.json({ error: 'Invalid startIndex or count' }, 400);\n }\n if (startIndex >= loaded.length) {\n return c.json({ error: 'Index out of range' }, 400);\n }\n const deleteCount = Math.min(count, loaded.length - startIndex);\n const next = loaded.slice(0, startIndex).concat(loaded.slice(startIndex + deleteCount));\n await service.sessionManagerInstance.saveMessages(key, next);\n return c.json({ ok: true, deleted: deleteCount });\n });\n\n // DELETE /api/sessions/:key - Delete session\n authenticated.delete('/api/sessions/:key', async (c) => {\n const key = c.req.param('key');\n const result = await service.sessions.delete(key);\n return c.json(result);\n });\n\n // POST /api/sessions/:key/archive - Archive session\n authenticated.post('/api/sessions/:key/archive', async (c) => {\n const key = c.req.param('key');\n const result = await service.sessions.archive(key);\n return c.json(result);\n });\n\n // POST /api/sessions/:key/unarchive - Unarchive session\n authenticated.post('/api/sessions/:key/unarchive', async (c) => {\n const key = c.req.param('key');\n const result = await service.sessions.unarchive(key);\n return c.json(result);\n });\n\n // POST /api/sessions/:key/pin - Pin session\n authenticated.post('/api/sessions/:key/pin', async (c) => {\n const key = c.req.param('key');\n const result = await service.sessions.pin(key);\n return c.json(result);\n });\n\n // POST /api/sessions/:key/unpin - Unpin session\n authenticated.post('/api/sessions/:key/unpin', async (c) => {\n const key = c.req.param('key');\n const result = await service.sessions.unpin(key);\n return c.json(result);\n });\n\n // POST /api/sessions/:key/rename - Rename session\n authenticated.post('/api/sessions/:key/rename', async (c) => {\n const key = c.req.param('key');\n\n const body = await c.req.json();\n const { name } = body;\n const result = await service.sessions.rename(key, name);\n return c.json(result);\n });\n\n // ========== Subagent REST API (/api/subagents) ==========\n\n // GET /api/subagents - List subagent sessions\n authenticated.get('/api/subagents', async (c) => {\n const query = c.req.query();\n const result = await service.sessions.listSubagents({\n limit: query.limit ? parseInt(query.limit) : undefined,\n offset: query.offset ? parseInt(query.offset) : undefined,\n });\n return c.json(result);\n });\n\n // GET /api/subagents/:key - Get subagent session detail\n authenticated.get('/api/subagents/:key', async (c) => {\n const key = c.req.param('key');\n // Verify it's a subagent session\n if (!key.startsWith('subagent:')) {\n return c.json({ error: 'Not a subagent session' }, 400);\n }\n const includeRaw = c.req.query('include') ?? '';\n const includeSet = new Set(\n includeRaw\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean),\n );\n const session = await service.sessions.getSession(key, {\n includeTranscriptSummary: includeSet.has('transcript'),\n includeTranscriptRows: includeSet.has('transcriptRows'),\n });\n if (!session) {\n return c.json({ error: 'Subagent session not found' }, 404);\n }\n return c.json({ session });\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sessions.js","names":[],"sources":["../../../../../src/gateway/hono/routes/sessions.ts"],"sourcesContent":["import type { Hono } from 'hono';\n\nimport { buildSessionKey, parseSessionKey } from '../../../routing/session-key.js';\nimport { agentExists, getDefaultAgentId } from '../../../routing/resolve-route.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\nimport { messagesToClientHistory } from '../../../session/client-history.js';\nimport { computeUserRoundDeleteRange } from '../../../session/user-round-delete.js';\nimport { respondStartupUnavailable } from '../lib/startup-unavailable.js';\nimport type { StartupUnavailableGatewayMethod } from '../../startup-readiness.js';\n\ntype SessionsStartupMethod = StartupUnavailableGatewayMethod;\n\nfunction ensureGatewayReadyForSessions(\n c: Parameters<typeof respondStartupUnavailable>[0],\n service: AuthenticatedRouteDeps['service'],\n method: SessionsStartupMethod,\n): Response | null {\n if (service.isGatewayReady()) {\n return null;\n }\n return respondStartupUnavailable(c, method);\n}\n\nexport function registerSessionsRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service } = deps;\n\n // ========== Session REST API (/api/sessions) ==========\n\n // POST /api/sessions - Create new session (reuses empty sessions)\n authenticated.post('/api/sessions', async (c) => {\n const body = await c.req.json().catch(() => ({}));\n const channel = body.channel || 'webchat';\n const routingCfg = service.currentConfig;\n let agentId =\n typeof body.agentId === 'string' && body.agentId.trim()\n ? body.agentId.trim().toLowerCase()\n : getDefaultAgentId(routingCfg);\n if (!agentExists(agentId, routingCfg)) {\n agentId = getDefaultAgentId(routingCfg);\n }\n\n // If a specific chat_id is provided, use it (for advanced use cases)\n // Otherwise, try to find and reuse an existing empty session\n if (body.chat_id) {\n const sessionKey = buildSessionKey({\n agentId,\n source: channel,\n accountId: 'default',\n peerKind: 'direct',\n peerId: body.chat_id,\n });\n\n await service.sessionIndexInstance.saveMessages(sessionKey, []);\n const session = await service.sessions.getSession(sessionKey);\n return c.json({ session }, 201);\n }\n\n // Look for existing empty sessions to reuse\n const existingSessions = await service.sessions.listSessions({\n channel,\n limit: 50,\n sortBy: 'updatedAt',\n sortOrder: 'desc',\n });\n \n // Reuse an empty session only when it matches the requested agent (session key embeds agent id).\n const emptySession = existingSessions.items.find((s) => {\n if (s.messageCount !== 0) return false;\n const parsed = parseSessionKey(s.key);\n return parsed?.agentId === agentId;\n });\n \n if (emptySession) {\n // Return existing empty session instead of creating a new one\n const session = await service.sessions.getSession(emptySession.key);\n return c.json({ session, reused: true }, 200);\n }\n \n // No empty session found, create a new one\n const chatId = `chat_${Date.now()}`;\n const sessionKey = buildSessionKey({\n agentId,\n source: channel,\n accountId: 'default',\n peerKind: 'direct',\n peerId: chatId,\n });\n\n await service.sessionIndexInstance.saveMessages(sessionKey, []);\n\n const session = await service.sessions.getSession(sessionKey);\n return c.json({ session }, 201);\n });\n\n // GET /api/sessions - List sessions\n authenticated.get('/api/sessions', async (c) => {\n const blocked = ensureGatewayReadyForSessions(c, service, 'sessions.list');\n if (blocked) {\n return blocked;\n }\n const query = c.req.query();\n const result = await service.sessions.listSessions({\n status: query.status as any,\n search: query.search,\n channel: query.channel,\n limit: query.limit ? parseInt(query.limit) : undefined,\n offset: query.offset ? parseInt(query.offset) : undefined,\n });\n return c.json(result);\n });\n\n // GET /api/sessions/stats - Get session stats (must be before /:key)\n authenticated.get('/api/sessions/stats', async (c) => {\n const result = await service.sessions.stats();\n return c.json(result);\n });\n\n // GET /api/sessions/chat-ids - Get unique chat IDs from sessions (must be before /:key)\n authenticated.get('/api/sessions/chat-ids', async (c) => {\n const channel = c.req.query('channel');\n const chatIds = await service.sessions.chatIds(channel || undefined);\n return c.json({ ok: true, payload: { chatIds } });\n });\n\n // GET /api/sessions/:key/run — read-only active webchat agent run (for UI resume)\n authenticated.get('/api/sessions/:key/run', async (c) => {\n const blocked = ensureGatewayReadyForSessions(c, service, 'sessions.run');\n if (blocked) {\n return blocked;\n }\n const key = c.req.param('key');\n const session = await service.sessions.getSession(key);\n if (!session) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n return c.json({ ok: true, payload: service.sessions.getActiveRun(key) });\n });\n\n // GET /api/sessions/:key/agent-config — resolved session agent settings (thinking, etc.)\n authenticated.get('/api/sessions/:key/agent-config', async (c) => {\n const key = c.req.param('key');\n const payload = await service.sessions.getAgentConfig(key);\n return c.json({ ok: true, payload });\n });\n\n authenticated.patch('/api/sessions/:key/agent-config', async (c) => {\n const key = c.req.param('key');\n const body = await c.req.json().catch(() => ({}));\n const result = await service.sessions.patchAgentConfig(key, body);\n if (!result.ok) {\n return c.json({ ok: false, error: result.error }, 400);\n }\n return c.json({ ok: true });\n });\n\n // GET /api/sessions/:key/messages — flattened transcript for TUI / clients\n authenticated.get('/api/sessions/:key/messages', async (c) => {\n const blocked = ensureGatewayReadyForSessions(c, service, 'sessions.messages');\n if (blocked) {\n return blocked;\n }\n const key = c.req.param('key');\n const limitRaw = c.req.query('limit');\n const parsedLimit = limitRaw ? Number.parseInt(limitRaw, 10) : undefined;\n const limit =\n parsedLimit !== undefined && Number.isFinite(parsedLimit)\n ? Math.min(500, Math.max(1, parsedLimit))\n : undefined;\n\n const before = c.req.query('before')?.trim();\n const offsetRaw = c.req.query('offset');\n const parsedOffset = offsetRaw ? Number.parseInt(offsetRaw, 10) : 0;\n const offset = Number.isFinite(parsedOffset) ? Math.max(0, parsedOffset) : 0;\n\n const result = await service.sessions.getMessagePage(key, {\n limit,\n offset,\n ...(before ? { before } : {}),\n });\n if (!result) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n\n const messages = messagesToClientHistory(result.session.messages, { limit });\n return c.json({\n ok: true,\n payload: { messages },\n pagination: result.pagination,\n });\n });\n\n // GET /api/sessions/:key/history — UI chat history page from the newest tail.\n authenticated.get('/api/sessions/:key/history', async (c) => {\n const blocked = ensureGatewayReadyForSessions(c, service, 'sessions.history');\n if (blocked) {\n return blocked;\n }\n const key = c.req.param('key');\n const offsetRaw = c.req.query('offset');\n const limitRaw = c.req.query('limit');\n const before = c.req.query('before')?.trim();\n const parsedOffset = offsetRaw ? Number.parseInt(offsetRaw, 10) : 0;\n const parsedLimit = limitRaw ? Number.parseInt(limitRaw, 10) : 50;\n const offset = Number.isFinite(parsedOffset) ? Math.max(0, parsedOffset) : 0;\n const limit = Number.isFinite(parsedLimit) ? Math.min(200, Math.max(1, parsedLimit)) : 50;\n const result = await service.sessions.getMessagePage(key, {\n offset,\n limit,\n ...(before ? { before } : {}),\n });\n\n if (!result) {\n return c.json({ error: 'Session not found' }, 404);\n }\n\n return c.json(result);\n });\n\n // POST /api/sessions/:key/transcript/context — append persisted-only `kind: 'context'` row (not in LLM context)\n authenticated.post('/api/sessions/:key/transcript/context', async (c) => {\n const key = c.req.param('key');\n const meta = await service.sessionIndexInstance.getSessionMetadata(key);\n if (!meta) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n const body = await c.req.json().catch(() => ({}));\n const id = typeof body.id === 'string' && body.id.trim() ? body.id.trim() : undefined;\n const text = typeof body.text === 'string' ? body.text : undefined;\n const data =\n body.data !== undefined && typeof body.data === 'object' && body.data !== null && !Array.isArray(body.data)\n ? (body.data as Record<string, unknown>)\n : undefined;\n await service.sessionIndexInstance.appendTranscriptContextEntry(key, { id, text, data });\n return c.json({ ok: true });\n });\n\n // GET /api/sessions/:key/compaction/checkpoints — list pre-compaction snapshots (OpenClaw-style)\n authenticated.get('/api/sessions/:key/compaction/checkpoints', async (c) => {\n const key = c.req.param('key');\n const meta = await service.sessionIndexInstance.getSessionMetadata(key);\n if (!meta) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n const checkpoints = await service.sessions.listCompactionCheckpoints(key);\n return c.json({ ok: true, payload: { checkpoints } });\n });\n\n authenticated.get('/api/sessions/:key/compaction/checkpoints/:checkpointId', async (c) => {\n const key = c.req.param('key');\n const checkpointId = c.req.param('checkpointId');\n const checkpoint = await service.sessions.getCompactionCheckpoint(key, checkpointId);\n if (!checkpoint) {\n return c.json({ ok: false, error: 'Checkpoint not found' }, 404);\n }\n return c.json({ ok: true, payload: { checkpoint } });\n });\n\n authenticated.post('/api/sessions/:key/compaction/restore', async (c) => {\n const key = c.req.param('key');\n const body = await c.req.json().catch(() => ({}));\n const checkpointId = typeof body.checkpointId === 'string' ? body.checkpointId.trim() : '';\n if (!checkpointId) {\n return c.json({ ok: false, error: 'checkpointId required' }, 400);\n }\n try {\n await service.sessions.restoreCompactionCheckpoint(key, checkpointId);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (msg.includes('not found') || msg.includes('Invalid')) {\n return c.json({ ok: false, error: msg }, 404);\n }\n return c.json({ ok: false, error: msg }, 500);\n }\n const session = await service.sessions.getSession(key);\n if (!session) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n return c.json({ ok: true, session });\n });\n\n authenticated.post('/api/sessions/:key/compaction/run', async (c) => {\n const key = c.req.param('key');\n const session = await service.sessions.getSession(key);\n if (!session) {\n return c.json({ ok: false, error: 'Session not found' }, 404);\n }\n const body = await c.req.json().catch(() => ({}));\n const instructions = typeof body.instructions === 'string' ? body.instructions : undefined;\n const force = typeof body.force === 'boolean' ? body.force : true;\n const result = await service.sessions.runCompaction(key, { instructions, force });\n return c.json({ ok: true, payload: { result } });\n });\n\n // GET /api/sessions/:key - Get single session (must be after /stats and /chat-ids)\n authenticated.get('/api/sessions/:key', async (c) => {\n const key = c.req.param('key');\n const includeRaw = c.req.query('include') ?? '';\n const includeSet = new Set(\n includeRaw\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean),\n );\n const includeTranscript = includeSet.has('transcript');\n const includeTranscriptRows = includeSet.has('transcriptRows');\n const offsetRaw = c.req.query('offset');\n const limitRaw = c.req.query('limit');\n const hasPagingQuery = offsetRaw !== undefined || limitRaw !== undefined;\n\n if (hasPagingQuery) {\n const blocked = ensureGatewayReadyForSessions(c, service, 'sessions.history');\n if (blocked) {\n return blocked;\n }\n const parsedOffset = offsetRaw ? Number.parseInt(offsetRaw, 10) : 0;\n const parsedLimit = limitRaw ? Number.parseInt(limitRaw, 10) : 50;\n const offset = Number.isFinite(parsedOffset) ? Math.max(0, parsedOffset) : 0;\n const limit = Number.isFinite(parsedLimit) ? Math.min(200, Math.max(1, parsedLimit)) : 50;\n const result = await service.sessions.getMessagePage(key, {\n offset,\n limit,\n includeTranscriptSummary: includeTranscript,\n includeTranscriptRows,\n });\n if (!result) {\n return c.json({ error: 'Session not found' }, 404);\n }\n return c.json(result);\n }\n\n const session = await service.sessions.getSession(key, {\n includeTranscriptSummary: includeTranscript,\n includeTranscriptRows,\n });\n if (!session) {\n return c.json({ error: 'Session not found' }, 404);\n }\n return c.json({ session });\n });\n\n // PATCH /api/sessions/:key - Partial metadata (name, tags, customData); OpenClaw-style patch subset\n authenticated.patch('/api/sessions/:key', async (c) => {\n const key = c.req.param('key');\n const body = await c.req.json().catch(() => ({}));\n const patch: {\n name?: string;\n tags?: string[];\n replaceTags?: boolean;\n customData?: Record<string, unknown>;\n } = {};\n if (typeof body.name === 'string') {\n patch.name = body.name;\n }\n if (Array.isArray(body.tags)) {\n patch.tags = body.tags;\n }\n if (typeof body.replaceTags === 'boolean') {\n patch.replaceTags = body.replaceTags;\n }\n if (body.customData !== undefined && typeof body.customData === 'object' && body.customData !== null) {\n patch.customData = body.customData as Record<string, unknown>;\n }\n const result = await service.sessions.patch(key, patch);\n if (result.ok === false) {\n return c.json({ ok: false, error: result.error }, 404);\n }\n const session = await service.sessions.getSession(key);\n return c.json({ ok: true, session });\n });\n\n // GET /api/sessions/:key/export - Export session (must be before /:key)\n authenticated.get('/api/sessions/:key/export', async (c) => {\n const key = c.req.param('key');\n const format = c.req.query('format') as any || 'json';\n const result = await service.sessions.export(key, format);\n return c.json(result);\n });\n\n // DELETE /api/sessions/:key/messages — delete LLM rows by range or by user turn.\n // `userRoundIndex` (0-based among user messages) removes the user row and every following\n // assistant / tool / toolResult row until the next user. Prefer this from the web console so\n // tool loops are not left orphaned after retry/delete.\n authenticated.delete('/api/sessions/:key/messages', async (c) => {\n const key = c.req.param('key');\n const body = await c.req.json().catch(() => ({}));\n const loaded = await service.sessionIndexInstance.loadMessages(key);\n if (!loaded) {\n return c.json({ error: 'Session not found' }, 404);\n }\n\n let startIndex = typeof body.startIndex === 'number' ? body.startIndex : -1;\n let count = typeof body.count === 'number' ? body.count : 0;\n const userRoundIndex =\n typeof body.userRoundIndex === 'number' ? body.userRoundIndex : undefined;\n\n if (userRoundIndex !== undefined) {\n const range = computeUserRoundDeleteRange(loaded, userRoundIndex);\n if (!range) {\n return c.json({ error: 'User round index out of range' }, 400);\n }\n startIndex = range.startIndex;\n count = range.count;\n }\n\n if (startIndex < 0 || count <= 0) {\n return c.json({ error: 'Invalid startIndex or count' }, 400);\n }\n if (startIndex >= loaded.length) {\n return c.json({ error: 'Index out of range' }, 400);\n }\n const deleteCount = Math.min(count, loaded.length - startIndex);\n const next = loaded.slice(0, startIndex).concat(loaded.slice(startIndex + deleteCount));\n await service.sessionIndexInstance.saveMessages(key, next);\n return c.json({ ok: true, deleted: deleteCount });\n });\n\n // POST /api/sessions/:key/reset - Reset session (archive transcript, new session id; keep key + overrides)\n authenticated.post('/api/sessions/:key/reset', async (c) => {\n const blocked = ensureGatewayReadyForSessions(c, service, 'sessions.reset');\n if (blocked) {\n return blocked;\n }\n const key = c.req.param('key');\n const result = await service.sessions.reset(key);\n if (result.ok === false) {\n const status = result.error === 'Session not found' ? 404 : 400;\n return c.json({ ok: false, error: result.error }, status);\n }\n const session = await service.sessions.getSession(key);\n return c.json({\n ok: true,\n reset: true,\n sessionId: result.sessionId,\n previousSessionId: result.previousSessionId,\n session,\n });\n });\n\n // DELETE /api/sessions/:key - Delete session (removes key from index)\n authenticated.delete('/api/sessions/:key', async (c) => {\n const key = c.req.param('key');\n const result = await service.sessions.delete(key);\n return c.json(result);\n });\n\n // POST /api/sessions/:key/archive - Archive session\n authenticated.post('/api/sessions/:key/archive', async (c) => {\n const key = c.req.param('key');\n const result = await service.sessions.archive(key);\n return c.json(result);\n });\n\n // POST /api/sessions/:key/unarchive - Unarchive session\n authenticated.post('/api/sessions/:key/unarchive', async (c) => {\n const key = c.req.param('key');\n const result = await service.sessions.unarchive(key);\n return c.json(result);\n });\n\n // POST /api/sessions/:key/pin - Pin session\n authenticated.post('/api/sessions/:key/pin', async (c) => {\n const key = c.req.param('key');\n const result = await service.sessions.pin(key);\n return c.json(result);\n });\n\n // POST /api/sessions/:key/unpin - Unpin session\n authenticated.post('/api/sessions/:key/unpin', async (c) => {\n const key = c.req.param('key');\n const result = await service.sessions.unpin(key);\n return c.json(result);\n });\n\n // POST /api/sessions/:key/rename - Rename session\n authenticated.post('/api/sessions/:key/rename', async (c) => {\n const key = c.req.param('key');\n\n const body = await c.req.json();\n const { name } = body;\n const result = await service.sessions.rename(key, name);\n return c.json(result);\n });\n\n // ========== Subagent REST API (/api/subagents) ==========\n\n // GET /api/subagents - List subagent sessions\n authenticated.get('/api/subagents', async (c) => {\n const query = c.req.query();\n const result = await service.sessions.listSubagents({\n limit: query.limit ? parseInt(query.limit) : undefined,\n offset: query.offset ? parseInt(query.offset) : undefined,\n });\n return c.json(result);\n });\n\n // GET /api/subagents/:key - Get subagent session detail\n authenticated.get('/api/subagents/:key', async (c) => {\n const key = c.req.param('key');\n // Verify it's a subagent session\n if (!key.startsWith('subagent:')) {\n return c.json({ error: 'Not a subagent session' }, 400);\n }\n const includeRaw = c.req.query('include') ?? '';\n const includeSet = new Set(\n includeRaw\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean),\n );\n const session = await service.sessions.getSession(key, {\n includeTranscriptSummary: includeSet.has('transcript'),\n includeTranscriptRows: includeSet.has('transcriptRows'),\n });\n if (!session) {\n return c.json({ error: 'Subagent session not found' }, 404);\n }\n return c.json({ session });\n });\n}\n"],"mappings":"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"}
|
|
@@ -4,7 +4,8 @@ import { resolveSiteShareConfig } from "../../../share/site-share-config.js";
|
|
|
4
4
|
import { logShareAudit } from "../../../share/share-audit.js";
|
|
5
5
|
import { getShareStore, shareResponseContentType } from "../../../share/share-store.js";
|
|
6
6
|
import { getSiteShareStore } from "../../../share/site-share-store.js";
|
|
7
|
-
import { resolveShareUrl } from "../../../share/share-url.js";
|
|
7
|
+
import { resolveShareUrl, resolveSiteShareUrl } from "../../../share/share-url.js";
|
|
8
|
+
import { resolveReverseProxyPublicUrl } from "../../public-url.js";
|
|
8
9
|
import { audienceDefaults, cleanupStagedSite, decideShareKind, forgetStagedSite, makeDescription, makeTitle, probeShareTarget, rememberStagedSite, stageSingleHtmlAsSite } from "../../../share/share-auto.js";
|
|
9
10
|
import { deleteThumbnail, placeholderSvg, readThumbnail, scheduleThumbnail, thumbnailContentType, thumbnailExists } from "../../../share/share-thumbnail.js";
|
|
10
11
|
import { extractToken } from "../../auth.js";
|
|
@@ -12,8 +13,8 @@ import { getClientIpFromHeaders } from "../../security/loopback.js";
|
|
|
12
13
|
import { renderFolderLandingPage, renderShareExpiredPage, renderShareLandingPage } from "../../../share/share-landing.js";
|
|
13
14
|
import { consumeSharePublicLimit } from "../../../share/share-rate-limit.js";
|
|
14
15
|
import { createZipStream, planDirectoryFiles } from "../../../share/share-zip.js";
|
|
15
|
-
import { createReadStream } from "node:fs";
|
|
16
16
|
import { createHash } from "node:crypto";
|
|
17
|
+
import { createReadStream } from "node:fs";
|
|
17
18
|
import { stat } from "node:fs/promises";
|
|
18
19
|
import { Readable } from "node:stream";
|
|
19
20
|
//#region src/gateway/hono/routes/shares.ts
|
|
@@ -21,7 +22,8 @@ function getShareUrlContext(service) {
|
|
|
21
22
|
const gateway = service.currentConfig.gateway;
|
|
22
23
|
return {
|
|
23
24
|
gatewayHost: resolveGatewayEffectiveHost(service.currentConfig),
|
|
24
|
-
gatewayPort: gateway.port ?? 18790
|
|
25
|
+
gatewayPort: gateway.port ?? 18790,
|
|
26
|
+
reverseProxyPublicUrl: resolveReverseProxyPublicUrl(service.currentConfig)
|
|
25
27
|
};
|
|
26
28
|
}
|
|
27
29
|
function thumbnailRenderContext(service) {
|
|
@@ -529,13 +531,13 @@ function registerShareRoutes(authenticated, deps) {
|
|
|
529
531
|
});
|
|
530
532
|
if (stagedDir) rememberStagedSite(siteRec.id, stagedDir);
|
|
531
533
|
const cfg = siteStore.getConfig();
|
|
532
|
-
const gatewayPort = service.currentConfig.gateway.port ?? 18790;
|
|
533
|
-
const gatewayHost = resolveGatewayEffectiveHost(service.currentConfig);
|
|
534
534
|
const subdomainLabel = siteRec.subdomain ?? siteRec.token;
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
535
|
+
const resolved = resolveSiteShareUrl({
|
|
536
|
+
...urlCtx,
|
|
537
|
+
token: siteRec.token,
|
|
538
|
+
subdomainLabel,
|
|
539
|
+
publicHostSuffix: cfg.publicHostSuffix
|
|
540
|
+
});
|
|
539
541
|
if (wantThumbnail) {
|
|
540
542
|
scheduleThumbnail({
|
|
541
543
|
scope: "site",
|
|
@@ -557,15 +559,15 @@ function registerShareRoutes(authenticated, deps) {
|
|
|
557
559
|
expiresAt: siteRec.expiresAt,
|
|
558
560
|
override: description
|
|
559
561
|
}),
|
|
560
|
-
shareUrl,
|
|
562
|
+
shareUrl: resolved.shareUrl,
|
|
561
563
|
lanUrl: null,
|
|
562
|
-
reachability,
|
|
563
|
-
reachabilityHint:
|
|
564
|
+
reachability: resolved.reachability,
|
|
565
|
+
reachabilityHint: resolved.reachabilityHint,
|
|
564
566
|
expiresAt: siteRec.expiresAt,
|
|
565
567
|
maxViews: null
|
|
566
568
|
},
|
|
567
569
|
thumbnail: {
|
|
568
|
-
url: thumbnailUrl,
|
|
570
|
+
url: wantThumbnail ? resolved.thumbnailUrl : "",
|
|
569
571
|
status: wantThumbnail ? "pending" : "unavailable",
|
|
570
572
|
width: SHARE_CONFIG_DEFAULTS.thumbnail.viewportWidth,
|
|
571
573
|
height: SHARE_CONFIG_DEFAULTS.thumbnail.viewportHeight
|
|
@@ -1 +1 @@
|
|
|
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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"}
|
|
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, resolveSiteShareUrl } from '../../../share/share-url.js';\nimport { resolveReverseProxyPublicUrl } from '../../public-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 reverseProxyPublicUrl: resolveReverseProxyPublicUrl(service.currentConfig),\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 subdomainLabel = siteRec.subdomain ?? siteRec.token;\n const resolved = resolveSiteShareUrl({\n ...urlCtx,\n token: siteRec.token,\n subdomainLabel,\n publicHostSuffix: cfg.publicHostSuffix,\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: resolved.shareUrl,\n lanUrl: null,\n reachability: resolved.reachability,\n reachabilityHint: resolved.reachabilityHint,\n expiresAt: siteRec.expiresAt,\n maxViews: null,\n },\n thumbnail: {\n url: wantThumbnail ? resolved.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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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { init_public_url, validatePublicUrl } from "../../../config/public-url.js";
|
|
2
2
|
import { resolveGatewayEffectiveHost } from "../../../config/gateway-bind.js";
|
|
3
3
|
import { loadTunnelState } from "../../../tunnel/tunnel-state.js";
|
|
4
|
+
import { resolveReverseProxyPublicUrl } from "../../public-url.js";
|
|
4
5
|
import { extractToken } from "../../auth.js";
|
|
5
6
|
import { TUNNEL_CONSENT_REQUIRED_CODE, TunnelConsentError, assertTunnelMayStart, getTunnelConsentState } from "../../../tunnel/consent.js";
|
|
6
7
|
import { getTunnelRegistrationSecretMeta, readTunnelRegistrationSecretFromConfigOnly, resolveTunnelBrokerUrl } from "../../../tunnel/env.js";
|
|
@@ -10,7 +11,6 @@ import { getTunnelService, hashGatewayToken } from "../../../tunnel/tunnel-servi
|
|
|
10
11
|
import { configureTunnelFromGatewayConfig } from "../../../tunnel/gateway-lifecycle.js";
|
|
11
12
|
import { applyTunnelConsentToConfig, setTunnelEnabledInConfig } from "../../../tunnel/tunnel-config.js";
|
|
12
13
|
import { getClientIpFromHeaders } from "../../security/loopback.js";
|
|
13
|
-
import { resolveReverseProxyPublicUrl } from "../../public-url.js";
|
|
14
14
|
import { applyLanPairingGatewayPatch } from "../../../tunnel/enable-lan-pairing.js";
|
|
15
15
|
import { consumeTunnelMutationLimit } from "../../../tunnel/tunnel-rate-limit.js";
|
|
16
16
|
import "../../../tunnel/index.js";
|