@xopcai/xopc 0.0.83 → 0.0.85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/README.zh-CN.md +3 -1
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
- package/dist/extensions/feishu/src/plugin.d.ts +2 -0
- package/dist/extensions/feishu/src/plugin.js +10 -0
- package/dist/extensions/feishu/src/plugin.js.map +1 -1
- package/dist/extensions/feishu/src/workflow-progress.d.ts +27 -0
- package/dist/extensions/feishu/src/workflow-progress.js +99 -0
- package/dist/extensions/feishu/src/workflow-progress.js.map +1 -0
- package/dist/extensions/telegram/src/plugin.d.ts +2 -0
- package/dist/extensions/telegram/src/plugin.js +11 -1
- package/dist/extensions/telegram/src/plugin.js.map +1 -1
- package/dist/extensions/telegram/src/routing-integration.js +2 -2
- package/dist/extensions/telegram/src/workflow-progress.d.ts +24 -0
- package/dist/extensions/telegram/src/workflow-progress.js +73 -0
- package/dist/extensions/telegram/src/workflow-progress.js.map +1 -0
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js +158 -0
- package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js.map +1 -0
- package/dist/extensions/weixin/src/api/api.js +2 -2
- package/dist/extensions/weixin/src/auth/accounts.js +1 -1
- package/dist/extensions/weixin/src/cdn/upload.js +1 -1
- package/dist/extensions/weixin/src/media/data-url.js +1 -1
- package/dist/extensions/weixin/src/messaging/debug-mode.js +1 -1
- package/dist/extensions/weixin/src/messaging/inbound.js +1 -1
- package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
- package/dist/extensions/weixin/src/plugin.d.ts +2 -0
- package/dist/extensions/weixin/src/plugin.js +11 -1
- package/dist/extensions/weixin/src/plugin.js.map +1 -1
- package/dist/extensions/weixin/src/storage/sync-buf.js +1 -1
- package/dist/extensions/weixin/src/workflow-progress.d.ts +26 -0
- package/dist/extensions/weixin/src/workflow-progress.js +99 -0
- package/dist/extensions/weixin/src/workflow-progress.js.map +1 -0
- package/dist/gateway/static/root/assets/agents-D3_-kNlZ.js +222 -0
- package/dist/gateway/static/root/assets/apps-page-D7v7649T.js +1 -0
- package/dist/gateway/static/root/assets/channels-settings-nCaMb0a7.js +1 -0
- package/dist/gateway/static/root/assets/channels-status-swr-C1gZBcJV.js +8 -0
- package/dist/gateway/static/root/assets/createLucideIcon-DPHK1VkS.js +1 -0
- package/dist/gateway/static/root/assets/cron-api-CoYK0hlm.js +1 -0
- package/dist/gateway/static/root/assets/cron-page-DeGo-Vjc.js +1 -0
- package/dist/gateway/static/root/assets/dist-BTWC-BTN.js +45 -0
- package/dist/gateway/static/root/assets/{dist-BpQxde0t.js → dist-DaK4dsss.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-CY27wj_p.js → extension-debug-page-BZngZWbO.js} +1 -1
- package/dist/gateway/static/root/assets/extension-page-D6JSyV27.js +1 -0
- package/dist/gateway/static/root/assets/extension-settings-page-8PZcmWI7.js +1 -0
- package/dist/gateway/static/root/assets/fetch-B2MYHbWg.js +1 -0
- package/dist/gateway/static/root/assets/{field-primitives-fa_hiQcX.js → field-primitives-Zzl22MvN.js} +1 -1
- package/dist/gateway/static/root/assets/heartbeat-config-api-BtIcpG0O.js +1 -0
- package/dist/gateway/static/root/assets/index-D4vM3-P7.js +4700 -0
- package/dist/gateway/static/root/assets/index-ew_2L2We.css +1 -0
- package/dist/gateway/static/root/assets/logs-page-_d4UJ-qQ.js +1 -0
- package/dist/gateway/static/root/assets/sessions-page-5N4aF2Wk.js +1 -0
- package/dist/gateway/static/root/assets/settings-form-section-D_tgb8r2.js +1 -0
- package/dist/gateway/static/root/assets/settings-page-C18xBt4X.js +3 -0
- package/dist/gateway/static/root/assets/share-preview-page-D4EG_vM1.js +2 -0
- package/dist/gateway/static/root/assets/skills-page-sPAXhh8w.js +2 -0
- package/dist/gateway/static/root/assets/theme-store-DryYl3qD.js +1 -0
- package/dist/gateway/static/root/assets/url-BwNL6Rgk.js +3 -0
- package/dist/gateway/static/root/assets/utils-CYO9eTCM.js +1 -0
- package/dist/gateway/static/root/assets/voice-api-key-field-Ds51havm.js +1 -0
- package/dist/gateway/static/root/index.html +7 -6
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.js +7 -7
- package/dist/src/agent/bootstrap/load-bootstrap-files.js +1 -1
- package/dist/src/agent/context/workspace-seed.js +3 -3
- package/dist/src/agent/embedded/map-stream-events.js +6 -0
- package/dist/src/agent/embedded/map-stream-events.js.map +1 -1
- package/dist/src/agent/embedded/subscribe-session.js +24 -0
- package/dist/src/agent/embedded/subscribe-session.js.map +1 -1
- package/dist/src/agent/embedded/types.d.ts +19 -0
- package/dist/src/agent/goals/goal-locale.js +2 -2
- package/dist/src/agent/goals/goal-run-store.js +4 -4
- package/dist/src/agent/goals/persistent-goal-service.js +1 -1
- package/dist/src/agent/goals/post-turn.js +2 -2
- package/dist/src/agent/image/load-image-media.js +2 -2
- 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/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/models/manager.js +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/startup-context.d.ts +3 -0
- package/dist/src/agent/reply/startup-context.js +25 -2
- package/dist/src/agent/reply/startup-context.js.map +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 +1 -1
- package/dist/src/agent/service.d.ts +1 -0
- package/dist/src/agent/service.js +10 -4
- 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 +3 -3
- 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.d.ts +27 -0
- package/dist/src/agent/tools/create-share-tool.js +237 -0
- package/dist/src/agent/tools/create-share-tool.js.map +1 -0
- package/dist/src/agent/tools/dreaming-tool.js +1 -1
- package/dist/src/agent/tools/factory.js +35 -1
- package/dist/src/agent/tools/factory.js.map +1 -1
- package/dist/src/agent/tools/image-generate-tool.js +1 -1
- package/dist/src/agent/tools/index.d.ts +2 -0
- package/dist/src/agent/tools/index.js +3 -1
- package/dist/src/agent/tools/send-media.js +1 -1
- package/dist/src/agent/tools/skill-manage-tool.js +1 -1
- package/dist/src/agent/tools/workflow-tool.d.ts +41 -0
- package/dist/src/agent/tools/workflow-tool.js +271 -0
- package/dist/src/agent/tools/workflow-tool.js.map +1 -0
- package/dist/src/agent/tools/write.js +1 -1
- package/dist/src/agent/workflow/builtins/audit-repo.d.ts +9 -0
- package/dist/src/agent/workflow/builtins/audit-repo.js +115 -0
- package/dist/src/agent/workflow/builtins/audit-repo.js.map +1 -0
- package/dist/src/agent/workflow/builtins/index.d.ts +15 -0
- package/dist/src/agent/workflow/builtins/index.js +28 -0
- package/dist/src/agent/workflow/builtins/index.js.map +1 -0
- package/dist/src/agent/workflow/builtins/multi-perspective-review.d.ts +9 -0
- package/dist/src/agent/workflow/builtins/multi-perspective-review.js +113 -0
- package/dist/src/agent/workflow/builtins/multi-perspective-review.js.map +1 -0
- package/dist/src/agent/workflow/builtins/research.d.ts +9 -0
- package/dist/src/agent/workflow/builtins/research.js +129 -0
- package/dist/src/agent/workflow/builtins/research.js.map +1 -0
- package/dist/src/agent/workflow/catalog.d.ts +51 -0
- package/dist/src/agent/workflow/catalog.js +155 -0
- package/dist/src/agent/workflow/catalog.js.map +1 -0
- package/dist/src/agent/workflow/channel-capability.d.ts +76 -0
- package/dist/src/agent/workflow/channel-capability.js +1 -0
- package/dist/src/agent/workflow/index.d.ts +11 -0
- package/dist/src/agent/workflow/index.js +10 -0
- package/dist/src/agent/workflow/last-run-memory.d.ts +42 -0
- package/dist/src/agent/workflow/last-run-memory.js +60 -0
- package/dist/src/agent/workflow/last-run-memory.js.map +1 -0
- package/dist/src/agent/workflow/parser.d.ts +20 -0
- package/dist/src/agent/workflow/parser.js +137 -0
- package/dist/src/agent/workflow/parser.js.map +1 -0
- package/dist/src/agent/workflow/progress-broker.d.ts +80 -0
- package/dist/src/agent/workflow/progress-broker.js +263 -0
- package/dist/src/agent/workflow/progress-broker.js.map +1 -0
- package/dist/src/agent/workflow/runtime.d.ts +31 -0
- package/dist/src/agent/workflow/runtime.js +301 -0
- package/dist/src/agent/workflow/runtime.js.map +1 -0
- package/dist/src/agent/workflow/snapshot.d.ts +18 -0
- package/dist/src/agent/workflow/snapshot.js +144 -0
- package/dist/src/agent/workflow/snapshot.js.map +1 -0
- package/dist/src/agent/workflow/structured-output-tool.d.ts +33 -0
- package/dist/src/agent/workflow/structured-output-tool.js +58 -0
- package/dist/src/agent/workflow/structured-output-tool.js.map +1 -0
- package/dist/src/agent/workflow/subagent-runner.d.ts +42 -0
- package/dist/src/agent/workflow/subagent-runner.js +104 -0
- package/dist/src/agent/workflow/subagent-runner.js.map +1 -0
- package/dist/src/agent/workflow/types.d.ts +137 -0
- package/dist/src/agent/workflow/types.js +1 -0
- package/dist/src/auth/credentials.js +3 -3
- 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/providers/browser-ext-install.js +4 -4
- package/dist/src/browser/providers/cloakbrowser.js +4 -4
- 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/outbound/persist-store.js +1 -1
- package/dist/src/channels/pairing/allow-from-file.js +1 -1
- package/dist/src/channels/pairing/pairing-store.js +2 -2
- package/dist/src/chat-commands/builtins/config.js +2 -2
- package/dist/src/chat-commands/builtins/model.js +40 -23
- package/dist/src/chat-commands/builtins/model.js.map +1 -1
- package/dist/src/chat-commands/builtins/system.js +30 -15
- package/dist/src/chat-commands/builtins/system.js.map +1 -1
- package/dist/src/chat-commands/builtins/workflow.d.ts +18 -0
- package/dist/src/chat-commands/builtins/workflow.js +167 -0
- package/dist/src/chat-commands/builtins/workflow.js.map +1 -0
- package/dist/src/chat-commands/context.js +1 -1
- package/dist/src/chat-commands/format-output.d.ts +28 -0
- package/dist/src/chat-commands/format-output.js +45 -0
- package/dist/src/chat-commands/format-output.js.map +1 -0
- package/dist/src/chat-commands/index.d.ts +1 -0
- package/dist/src/chat-commands/index.js +3 -1
- package/dist/src/chat-commands/index.js.map +1 -1
- package/dist/src/cli/command-catalog.js +110 -8
- package/dist/src/cli/command-catalog.js.map +1 -1
- package/dist/src/cli/command-loaders.js +2 -0
- package/dist/src/cli/command-loaders.js.map +1 -1
- package/dist/src/cli/command-manifest.js +9 -1
- package/dist/src/cli/command-manifest.js.map +1 -1
- package/dist/src/cli/commands/config.js +71 -20
- package/dist/src/cli/commands/config.js.map +1 -1
- package/dist/src/cli/commands/cron-cli.d.ts +2 -0
- package/dist/src/cli/commands/cron-cli.js +15 -0
- package/dist/src/cli/commands/cron-cli.js.map +1 -0
- package/dist/src/cli/commands/cron.d.ts +4 -1
- package/dist/src/cli/commands/cron.js +76 -41
- package/dist/src/cli/commands/cron.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/channel-config.js +1 -1
- package/dist/src/cli/commands/doctor/checks/channel-config.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/config-health.js +2 -2
- package/dist/src/cli/commands/doctor/checks/config-health.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/cron-health.js +1 -1
- package/dist/src/cli/commands/doctor/checks/cron-health.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/gateway-health.js +2 -2
- package/dist/src/cli/commands/doctor/checks/gateway-health.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/gateway-service.js +2 -2
- package/dist/src/cli/commands/doctor/checks/gateway-service.js.map +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 +2 -2
- package/dist/src/cli/commands/doctor/checks/state-integrity.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/workspace-status.js +4 -4
- package/dist/src/cli/commands/doctor/checks/workspace-status.js.map +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/index.d.ts +1 -1
- package/dist/src/cli/commands/gateway/index.js +2 -2
- package/dist/src/cli/commands/gateway/lifecycle.js +10 -4
- package/dist/src/cli/commands/gateway/lifecycle.js.map +1 -1
- package/dist/src/cli/commands/gateway/service.d.ts +4 -0
- package/dist/src/cli/commands/gateway/service.js +17 -2
- package/dist/src/cli/commands/gateway/service.js.map +1 -1
- package/dist/src/cli/commands/gateway/shared.js +1 -1
- package/dist/src/cli/commands/gateway/subcommands.js +1 -4
- package/dist/src/cli/commands/gateway/subcommands.js.map +1 -1
- package/dist/src/cli/commands/image.js +1 -1
- package/dist/src/cli/commands/init.js +31 -4
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/models.d.ts +4 -1
- package/dist/src/cli/commands/models.js +86 -74
- package/dist/src/cli/commands/models.js.map +1 -1
- package/dist/src/cli/commands/onboard.js +4 -2
- package/dist/src/cli/commands/onboard.js.map +1 -1
- package/dist/src/cli/commands/profile.d.ts +3 -5
- package/dist/src/cli/commands/profile.js +31 -31
- package/dist/src/cli/commands/profile.js.map +1 -1
- package/dist/src/cli/commands/setup.js +6 -1
- package/dist/src/cli/commands/setup.js.map +1 -1
- package/dist/src/cli/commands/tunnel.js +2 -2
- package/dist/src/cli/gateway-run-argv.js +15 -5
- package/dist/src/cli/gateway-run-argv.js.map +1 -1
- package/dist/src/cli/utils/gateway-client.js +1 -1
- package/dist/src/cli/utils/init-workspace-core.js +2 -2
- package/dist/src/config/agent-profile.js +1 -1
- package/dist/src/config/gateway-bind.js +1 -1
- package/dist/src/config/index.js +5 -5
- package/dist/src/config/loader.js +2 -2
- 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/public-url.d.ts +28 -0
- package/dist/src/config/public-url.js +103 -0
- package/dist/src/config/public-url.js.map +1 -0
- package/dist/src/config/schema.d.ts +82 -0
- package/dist/src/config/schema.js +130 -1
- package/dist/src/config/schema.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 +3 -3
- package/dist/src/daemon/install-plan.js.map +1 -1
- package/dist/src/daemon/launchd.js +2 -2
- package/dist/src/daemon/schtasks.js +38 -1
- package/dist/src/daemon/schtasks.js.map +1 -1
- package/dist/src/daemon/systemd.js +2 -2
- 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/gateway/agents-admin.js +2 -2
- package/dist/src/gateway/file-path-classifier.js +2 -2
- package/dist/src/gateway/heartbeat/service.js +1 -1
- package/dist/src/gateway/hono/app.js +33 -2
- package/dist/src/gateway/hono/app.js.map +1 -1
- package/dist/src/gateway/hono/lib/config-payload.js +1 -1
- package/dist/src/gateway/hono/lib/extension-store.js +2 -2
- 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 +1 -1
- package/dist/src/gateway/hono/routes/auth-registry-extensions.js +1 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js +1 -1
- package/dist/src/gateway/hono/routes/dreaming.js +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 +1 -1
- package/dist/src/gateway/hono/routes/shares.js +631 -34
- package/dist/src/gateway/hono/routes/shares.js.map +1 -1
- package/dist/src/gateway/hono/routes/site-shares.d.ts +3 -0
- package/dist/src/gateway/hono/routes/site-shares.js +228 -0
- package/dist/src/gateway/hono/routes/site-shares.js.map +1 -0
- package/dist/src/gateway/hono/routes/tunnel.js +97 -8
- package/dist/src/gateway/hono/routes/tunnel.js.map +1 -1
- package/dist/src/gateway/hono/routes/workspace.js +5 -5
- package/dist/src/gateway/hono/sse.js +2 -2
- package/dist/src/gateway/host.d.ts +3 -1
- package/dist/src/gateway/host.js +3 -1
- package/dist/src/gateway/host.js.map +1 -1
- package/dist/src/gateway/lock.js +3 -3
- package/dist/src/gateway/ports.d.ts +6 -0
- package/dist/src/gateway/ports.js +38 -2
- package/dist/src/gateway/ports.js.map +1 -1
- package/dist/src/gateway/public-url.d.ts +8 -0
- package/dist/src/gateway/public-url.js +10 -0
- package/dist/src/gateway/public-url.js.map +1 -0
- package/dist/src/gateway/security/origin-check.d.ts +9 -1
- package/dist/src/gateway/security/origin-check.js +4 -0
- package/dist/src/gateway/security/origin-check.js.map +1 -1
- package/dist/src/gateway/server.js +15 -0
- package/dist/src/gateway/server.js.map +1 -1
- 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 +2 -2
- package/dist/src/gateway/service.js +3 -2
- package/dist/src/gateway/service.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/i18n/goals-bundle.js +1 -1
- package/dist/src/i18n/index.d.ts +1 -0
- package/dist/src/i18n/index.js +2 -1
- package/dist/src/i18n/locales/share-tool.en.js +15 -0
- package/dist/src/i18n/locales/share-tool.en.js.map +1 -0
- package/dist/src/i18n/locales/share-tool.zh.js +15 -0
- package/dist/src/i18n/locales/share-tool.zh.js.map +1 -0
- package/dist/src/i18n/share-tool-bundle.d.ts +20 -0
- package/dist/src/i18n/share-tool-bundle.js +56 -0
- package/dist/src/i18n/share-tool-bundle.js.map +1 -0
- package/dist/src/infra/gateway-processes.js +1 -0
- package/dist/src/infra/gateway-processes.js.map +1 -1
- package/dist/src/infra/restart.js +2 -2
- package/dist/src/infra/update-check.js +1 -1
- 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/providers/auth-runtime/auth-profile-store.js +1 -1
- package/dist/src/providers/index.js +2 -2
- package/dist/src/providers/model-registry.js +1 -1
- package/dist/src/session/config-store.js +2 -2
- 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 +1 -1
- 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.js +5 -5
- package/dist/src/share/share-auto.d.ts +74 -0
- package/dist/src/share/share-auto.js +247 -0
- package/dist/src/share/share-auto.js.map +1 -0
- package/dist/src/share/share-config.js +63 -4
- package/dist/src/share/share-config.js.map +1 -1
- package/dist/src/share/share-landing.d.ts +28 -2
- package/dist/src/share/share-landing.js +155 -34
- package/dist/src/share/share-landing.js.map +1 -1
- package/dist/src/share/share-store.d.ts +48 -4
- package/dist/src/share/share-store.js +322 -51
- package/dist/src/share/share-store.js.map +1 -1
- package/dist/src/share/share-thumbnail.d.ts +35 -0
- package/dist/src/share/share-thumbnail.js +277 -0
- package/dist/src/share/share-thumbnail.js.map +1 -0
- package/dist/src/share/share-types.d.ts +68 -10
- package/dist/src/share/share-types.js +18 -1
- package/dist/src/share/share-types.js.map +1 -1
- package/dist/src/share/share-url.js +1 -1
- package/dist/src/share/share-zip.d.ts +35 -0
- package/dist/src/share/share-zip.js +303 -0
- package/dist/src/share/share-zip.js.map +1 -0
- package/dist/src/share/site-proxy.d.ts +35 -0
- package/dist/src/share/site-proxy.js +234 -0
- package/dist/src/share/site-proxy.js.map +1 -0
- package/dist/src/share/site-share-config.d.ts +11 -0
- package/dist/src/share/site-share-config.js +103 -0
- package/dist/src/share/site-share-config.js.map +1 -0
- package/dist/src/share/site-share-router.d.ts +23 -0
- package/dist/src/share/site-share-router.js +147 -0
- package/dist/src/share/site-share-router.js.map +1 -0
- package/dist/src/share/site-share-store.d.ts +53 -0
- package/dist/src/share/site-share-store.js +400 -0
- package/dist/src/share/site-share-store.js.map +1 -0
- package/dist/src/share/site-share-types.d.ts +103 -0
- package/dist/src/share/site-share-types.js +41 -0
- package/dist/src/share/site-share-types.js.map +1 -0
- package/dist/src/share/site-static-serve.d.ts +10 -0
- package/dist/src/share/site-static-serve.js +145 -0
- package/dist/src/share/site-static-serve.js.map +1 -0
- package/dist/src/tui/clipboard-image.js +3 -3
- package/dist/src/tui/theme-manager.js +1 -1
- package/dist/src/tui/tui-commands.js +18 -0
- package/dist/src/tui/tui-commands.js.map +1 -1
- package/dist/src/tui/tui-keybindings-file.js +1 -1
- package/dist/src/tui/tui-scoped-models.js +2 -2
- package/dist/src/tui/tui-settings.js +1 -1
- package/dist/src/tui/tui-workflow-slash.d.ts +32 -0
- package/dist/src/tui/tui-workflow-slash.js +63 -0
- package/dist/src/tui/tui-workflow-slash.js.map +1 -0
- package/dist/src/tui/tui.js +2 -2
- package/dist/src/tunnel/enable-lan-pairing.js +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/index.js +2 -2
- package/dist/src/tunnel/pair-context.d.ts +7 -1
- package/dist/src/tunnel/pair-context.js +25 -9
- package/dist/src/tunnel/pair-context.js.map +1 -1
- package/dist/src/tunnel/pair-url.d.ts +14 -1
- package/dist/src/tunnel/pair-url.js +14 -1
- package/dist/src/tunnel/pair-url.js.map +1 -1
- package/dist/src/tunnel/tunnel-service.js +2 -2
- 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/voice/tts/audio.js +1 -1
- package/dist/src/voice/tts/providers/edge-speech.js +2 -2
- package/package.json +3 -2
- package/dist/gateway/static/root/assets/agents-CrpYTHJS.js +0 -222
- package/dist/gateway/static/root/assets/apps-page-1mcKh5Rh.js +0 -1
- package/dist/gateway/static/root/assets/button-KafIU8dx.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-zd6QNKPx.js +0 -1
- package/dist/gateway/static/root/assets/channels-status-swr-uRAuhiUo.js +0 -8
- package/dist/gateway/static/root/assets/cron-api-O2Q_ruV6.js +0 -1
- package/dist/gateway/static/root/assets/cron-page-By09AQD-.js +0 -1
- package/dist/gateway/static/root/assets/dist-C57OMHW8.js +0 -48
- package/dist/gateway/static/root/assets/extension-page-C-Ed5ZmP.js +0 -1
- package/dist/gateway/static/root/assets/extension-settings-page-raLux7E7.js +0 -1
- package/dist/gateway/static/root/assets/fetch-2iRFmd3n.js +0 -3
- package/dist/gateway/static/root/assets/heartbeat-config-api-BVl5VHvL.js +0 -1
- package/dist/gateway/static/root/assets/index-BuFldCsB.css +0 -1
- package/dist/gateway/static/root/assets/index-Y-iqo-gL.js +0 -4693
- package/dist/gateway/static/root/assets/logs-page-BdH2n7ZW.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-Vpchzdp-.js +0 -1
- package/dist/gateway/static/root/assets/settings-form-section-Kk1yAGBl.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-KBm0u6Dz.js +0 -3
- package/dist/gateway/static/root/assets/skills-page-BjeXXaOn.js +0 -2
- package/dist/gateway/static/root/assets/theme-store-D01dJt95.js +0 -1
- package/dist/gateway/static/root/assets/utils-DpTxN4AF.js +0 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-CwO8Cf01.js +0 -1
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { resolveMimeType } from "./share-store.js";
|
|
2
|
+
import { basename, join, resolve } from "node:path";
|
|
3
|
+
import { readdirSync } from "node:fs";
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
5
|
+
import { copyFile, mkdir, rm, stat } from "node:fs/promises";
|
|
6
|
+
//#region src/share/share-auto.ts
|
|
7
|
+
/**
|
|
8
|
+
* Smart-share decision layer.
|
|
9
|
+
*
|
|
10
|
+
* The mobile app calls POST /api/shares/auto with a path + audience and
|
|
11
|
+
* lets the server pick file-share vs site-share, pick sane TTLs, and bundle
|
|
12
|
+
* the metadata the share-sheet needs (title, description, thumbnail URL,
|
|
13
|
+
* reachability).
|
|
14
|
+
*/
|
|
15
|
+
/** Convert an audience hint into TTL/maxViews defaults. */
|
|
16
|
+
function audienceDefaults(audience) {
|
|
17
|
+
switch (audience) {
|
|
18
|
+
case "public": return {
|
|
19
|
+
ttlMs: 1440 * 6e4,
|
|
20
|
+
maxViews: 100
|
|
21
|
+
};
|
|
22
|
+
case "colleague": return {
|
|
23
|
+
ttlMs: 10080 * 6e4,
|
|
24
|
+
maxViews: null
|
|
25
|
+
};
|
|
26
|
+
default: return {
|
|
27
|
+
ttlMs: 4320 * 6e4,
|
|
28
|
+
maxViews: null
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** Run filesystem probes needed by the routing logic. */
|
|
33
|
+
async function probeShareTarget(workspaceRoot, relPath) {
|
|
34
|
+
const trimmed = relPath.trim().replace(/\\/g, "/").replace(/^\/+/, "");
|
|
35
|
+
if (!trimmed) throw new Error("Empty path");
|
|
36
|
+
if (trimmed.includes("..") || trimmed.includes("\0")) throw new Error("Invalid path");
|
|
37
|
+
const abs = resolve(workspaceRoot, trimmed);
|
|
38
|
+
const st = await stat(abs);
|
|
39
|
+
if (st.isDirectory()) {
|
|
40
|
+
let hasIndexHtml = false;
|
|
41
|
+
try {
|
|
42
|
+
hasIndexHtml = readdirSync(abs).includes("index.html");
|
|
43
|
+
} catch {}
|
|
44
|
+
return {
|
|
45
|
+
absolutePath: abs,
|
|
46
|
+
kind: "directory",
|
|
47
|
+
size: 0,
|
|
48
|
+
mimeType: "application/x-directory",
|
|
49
|
+
hasIndexHtml
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (!st.isFile()) throw new Error("Path is not a regular file or directory");
|
|
53
|
+
const fileName = basename(abs);
|
|
54
|
+
return {
|
|
55
|
+
absolutePath: abs,
|
|
56
|
+
kind: "file",
|
|
57
|
+
size: st.size,
|
|
58
|
+
mimeType: resolveMimeType(fileName),
|
|
59
|
+
hasIndexHtml: false
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/** Pure decision function — exported for tests. */
|
|
63
|
+
function decideShareKind(probe, mode) {
|
|
64
|
+
if (mode === "force-file") return forced(probe.kind === "directory" ? "zip" : "file", probe);
|
|
65
|
+
if (mode === "force-site") {
|
|
66
|
+
if (probe.kind !== "directory" && probe.mimeType !== "text/html") throw new Error("force-site requires a directory or a single HTML file");
|
|
67
|
+
return {
|
|
68
|
+
kind: "site",
|
|
69
|
+
isDirectory: probe.kind === "directory",
|
|
70
|
+
reason: "forced",
|
|
71
|
+
hint: "已按指定方式作为站点分享"
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (mode === "force-zip") {
|
|
75
|
+
if (probe.kind !== "directory") throw new Error("force-zip requires a directory");
|
|
76
|
+
return {
|
|
77
|
+
kind: "zip",
|
|
78
|
+
isDirectory: true,
|
|
79
|
+
reason: "forced",
|
|
80
|
+
hint: "已按指定方式打包为 ZIP 分享"
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if (probe.kind === "directory") {
|
|
84
|
+
if (probe.hasIndexHtml) return {
|
|
85
|
+
kind: "site",
|
|
86
|
+
isDirectory: true,
|
|
87
|
+
reason: "html-with-assets",
|
|
88
|
+
hint: "目录包含 index.html,已通过站点分享渲染"
|
|
89
|
+
};
|
|
90
|
+
return {
|
|
91
|
+
kind: "file",
|
|
92
|
+
isDirectory: true,
|
|
93
|
+
reason: "directory-browse",
|
|
94
|
+
hint: "目录较多文件,通过分享页可浏览或下载 ZIP"
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if (probe.mimeType === "text/html") return {
|
|
98
|
+
kind: "site",
|
|
99
|
+
isDirectory: false,
|
|
100
|
+
reason: "html-single-file",
|
|
101
|
+
hint: "HTML 文件,分享后朋友点开即渲染"
|
|
102
|
+
};
|
|
103
|
+
if (probe.mimeType.startsWith("image/") && probe.size < 10 * 1024 * 1024) return {
|
|
104
|
+
kind: "file",
|
|
105
|
+
isDirectory: false,
|
|
106
|
+
reason: "small-image",
|
|
107
|
+
hint: "图片文件,朋友可直接预览或保存"
|
|
108
|
+
};
|
|
109
|
+
return {
|
|
110
|
+
kind: "file",
|
|
111
|
+
isDirectory: false,
|
|
112
|
+
reason: "large-binary",
|
|
113
|
+
hint: "通过分享链接由朋友点开下载"
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function forced(kind, probe) {
|
|
117
|
+
return {
|
|
118
|
+
kind,
|
|
119
|
+
isDirectory: probe.kind === "directory",
|
|
120
|
+
reason: "forced",
|
|
121
|
+
hint: "按指定方式分享"
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/** Build the user-facing title (trimmed, no extension). */
|
|
125
|
+
function makeTitle(fileName, override) {
|
|
126
|
+
if (override && override.trim()) return clip(override.trim(), 60);
|
|
127
|
+
const idx = fileName.lastIndexOf(".");
|
|
128
|
+
return clip((idx > 0 ? fileName.slice(0, idx) : fileName) || fileName, 60);
|
|
129
|
+
}
|
|
130
|
+
/** Build the user-facing description (audience + TTL hint baked in). */
|
|
131
|
+
function makeDescription(opts) {
|
|
132
|
+
if (opts.override && opts.override.trim()) return clip(opts.override.trim(), 140);
|
|
133
|
+
const now = Date.now();
|
|
134
|
+
const exp = new Date(opts.expiresAt).getTime();
|
|
135
|
+
const days = Math.max(1, Math.round((exp - now) / (1440 * 6e4)));
|
|
136
|
+
return clip(`由 xopc 生成 · ${opts.audience === "public" ? "公开分享" : opts.audience === "colleague" ? "同事可见" : "好友可见"} · ${days} 天内有效`, 140);
|
|
137
|
+
}
|
|
138
|
+
function clip(text, max) {
|
|
139
|
+
if (text.length <= max) return text;
|
|
140
|
+
return `${text.slice(0, max - 1)}…`;
|
|
141
|
+
}
|
|
142
|
+
/** Subfolder under the workspace where single-HTML site shares are staged. */
|
|
143
|
+
const STAGING_DIR_NAME = ".xopc-share-staging";
|
|
144
|
+
/**
|
|
145
|
+
* Copy a single HTML file into `<workspaceRoot>/.xopc-share-staging/<uuid>/index.html`
|
|
146
|
+
* so it can be served as a site (SiteShareStore needs a directory root).
|
|
147
|
+
*
|
|
148
|
+
* The caller must register a cleanup that calls `cleanupStagedSite(stagingDir)`
|
|
149
|
+
* when the underlying site share is revoked / expires.
|
|
150
|
+
*/
|
|
151
|
+
async function stageSingleHtmlAsSite(workspaceRoot, absoluteHtmlPath) {
|
|
152
|
+
const stagingRoot = resolve(workspaceRoot, STAGING_DIR_NAME);
|
|
153
|
+
await mkdir(stagingRoot, { recursive: true });
|
|
154
|
+
const id = randomUUID();
|
|
155
|
+
const stagingDir = join(stagingRoot, id);
|
|
156
|
+
await mkdir(stagingDir, { recursive: true });
|
|
157
|
+
await copyFile(absoluteHtmlPath, join(stagingDir, "index.html"));
|
|
158
|
+
return {
|
|
159
|
+
stagingDir,
|
|
160
|
+
relativePath: `${STAGING_DIR_NAME}/${id}`
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/** Best-effort cleanup; never throws. */
|
|
164
|
+
async function cleanupStagedSite(stagingDir) {
|
|
165
|
+
if (!stagingDir.includes(".xopc-share-staging")) return;
|
|
166
|
+
try {
|
|
167
|
+
await rm(stagingDir, {
|
|
168
|
+
recursive: true,
|
|
169
|
+
force: true
|
|
170
|
+
});
|
|
171
|
+
} catch {}
|
|
172
|
+
}
|
|
173
|
+
const stagedSiteByRecordId = /* @__PURE__ */ new Map();
|
|
174
|
+
function rememberStagedSite(recordId, stagingDir) {
|
|
175
|
+
stagedSiteByRecordId.set(recordId, stagingDir);
|
|
176
|
+
}
|
|
177
|
+
function forgetStagedSite(recordId) {
|
|
178
|
+
const dir = stagedSiteByRecordId.get(recordId);
|
|
179
|
+
stagedSiteByRecordId.delete(recordId);
|
|
180
|
+
return dir;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Walk `<workspaceRoot>/.xopc-share-staging/` and drop directories that are
|
|
184
|
+
* no longer referenced by any live site-share record. Call at gateway boot.
|
|
185
|
+
*/
|
|
186
|
+
async function sweepOrphanedStagingDirs(workspaceRoot, liveStagingDirs) {
|
|
187
|
+
const stagingRoot = resolve(workspaceRoot, STAGING_DIR_NAME);
|
|
188
|
+
let entries;
|
|
189
|
+
try {
|
|
190
|
+
entries = readdirSync(stagingRoot);
|
|
191
|
+
} catch {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
for (const name of entries) {
|
|
195
|
+
const abs = join(stagingRoot, name);
|
|
196
|
+
if (liveStagingDirs.has(abs)) continue;
|
|
197
|
+
try {
|
|
198
|
+
await rm(abs, {
|
|
199
|
+
recursive: true,
|
|
200
|
+
force: true
|
|
201
|
+
});
|
|
202
|
+
} catch {}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Boot-time housekeeping:
|
|
207
|
+
* 1. Walk active static site shares whose source lives under STAGING_DIR_NAME
|
|
208
|
+
* (i.e. created via `stageSingleHtmlAsSite`).
|
|
209
|
+
* 2. Re-register them in the in-process registry so the SiteShareStore
|
|
210
|
+
* cleanup hook still wipes the right directory on revoke/expire after a
|
|
211
|
+
* restart.
|
|
212
|
+
* 3. Sweep each workspace's `.xopc-share-staging/` for entries that are not
|
|
213
|
+
* referenced by any live record — these are leftovers from a previous
|
|
214
|
+
* process death between create-staging and persist.
|
|
215
|
+
*
|
|
216
|
+
* Safe to call multiple times; idempotent. Best invoked from gateway.start().
|
|
217
|
+
*/
|
|
218
|
+
async function runStagingSweep() {
|
|
219
|
+
const { getSiteShareStore } = await import("./site-share-store.js");
|
|
220
|
+
const store = getSiteShareStore();
|
|
221
|
+
const records = store.getActiveShares();
|
|
222
|
+
const liveByWorkspace = /* @__PURE__ */ new Map();
|
|
223
|
+
for (const rec of records) {
|
|
224
|
+
if (rec.source.kind !== "static") continue;
|
|
225
|
+
const ws = rec.source.workspaceRoot;
|
|
226
|
+
const rootDir = rec.source.rootDir;
|
|
227
|
+
if (!ws || !rootDir) continue;
|
|
228
|
+
if (!rec.source.workspaceRelativePath.startsWith(`.xopc-share-staging/`)) continue;
|
|
229
|
+
rememberStagedSite(rec.id, rootDir);
|
|
230
|
+
const bucket = liveByWorkspace.get(ws) ?? /* @__PURE__ */ new Set();
|
|
231
|
+
bucket.add(rootDir);
|
|
232
|
+
liveByWorkspace.set(ws, bucket);
|
|
233
|
+
}
|
|
234
|
+
for (const rec of store.getAllShares()) {
|
|
235
|
+
if (rec.source.kind !== "static") continue;
|
|
236
|
+
if (!rec.source.workspaceRelativePath.startsWith(`.xopc-share-staging/`)) continue;
|
|
237
|
+
if (!liveByWorkspace.has(rec.source.workspaceRoot)) liveByWorkspace.set(rec.source.workspaceRoot, /* @__PURE__ */ new Set());
|
|
238
|
+
}
|
|
239
|
+
for (const [workspaceRoot, live] of liveByWorkspace) await sweepOrphanedStagingDirs(workspaceRoot, live);
|
|
240
|
+
}
|
|
241
|
+
function resetStagedSiteRegistryForTests() {
|
|
242
|
+
stagedSiteByRecordId.clear();
|
|
243
|
+
}
|
|
244
|
+
//#endregion
|
|
245
|
+
export { STAGING_DIR_NAME, audienceDefaults, cleanupStagedSite, decideShareKind, forgetStagedSite, makeDescription, makeTitle, probeShareTarget, rememberStagedSite, resetStagedSiteRegistryForTests, runStagingSweep, stageSingleHtmlAsSite, sweepOrphanedStagingDirs };
|
|
246
|
+
|
|
247
|
+
//# sourceMappingURL=share-auto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"share-auto.js","names":["resolvePath"],"sources":["../../../src/share/share-auto.ts"],"sourcesContent":["/**\n * Smart-share decision layer.\n *\n * The mobile app calls POST /api/shares/auto with a path + audience and\n * lets the server pick file-share vs site-share, pick sane TTLs, and bundle\n * the metadata the share-sheet needs (title, description, thumbnail URL,\n * reachability).\n */\nimport { copyFile, mkdir, rm, stat } from 'node:fs/promises';\nimport { basename, join, resolve as resolvePath } from 'node:path';\nimport { readdirSync } from 'node:fs';\nimport { randomUUID } from 'node:crypto';\n\nimport { resolveMimeType } from './share-store.js';\n\nexport type ShareAudience = 'friend' | 'colleague' | 'public';\n\nexport type ShareAutoMode = 'auto' | 'force-file' | 'force-site' | 'force-zip';\n\nexport type ShareAutoKind = 'file' | 'site' | 'zip';\n\nexport interface ShareAutoDecision {\n kind: ShareAutoKind;\n isDirectory: boolean;\n reason:\n | 'html-single-file'\n | 'html-with-assets'\n | 'small-image'\n | 'large-binary'\n | 'directory-zip'\n | 'directory-browse'\n | 'forced';\n hint: string;\n}\n\nexport interface ShareAutoProbe {\n absolutePath: string;\n kind: 'file' | 'directory';\n size: number;\n mimeType: string;\n hasIndexHtml: boolean;\n}\n\nexport interface ShareAutoDefaults {\n ttlMs: number;\n maxViews: number | null;\n}\n\n/** Convert an audience hint into TTL/maxViews defaults. */\nexport function audienceDefaults(audience: ShareAudience | undefined): ShareAutoDefaults {\n switch (audience) {\n case 'public':\n return { ttlMs: 24 * 60 * 60_000, maxViews: 100 };\n case 'colleague':\n return { ttlMs: 7 * 24 * 60 * 60_000, maxViews: null };\n case 'friend':\n default:\n return { ttlMs: 3 * 24 * 60 * 60_000, maxViews: null };\n }\n}\n\n/** Run filesystem probes needed by the routing logic. */\nexport async function probeShareTarget(\n workspaceRoot: string,\n relPath: string,\n): Promise<ShareAutoProbe> {\n const trimmed = relPath.trim().replace(/\\\\/g, '/').replace(/^\\/+/, '');\n if (!trimmed) throw new Error('Empty path');\n if (trimmed.includes('..') || trimmed.includes('\\0')) throw new Error('Invalid path');\n const abs = resolvePath(workspaceRoot, trimmed);\n const st = await stat(abs);\n if (st.isDirectory()) {\n let hasIndexHtml = false;\n try {\n const entries = readdirSync(abs);\n hasIndexHtml = entries.includes('index.html');\n } catch {\n /* unreadable dir is handled later */\n }\n return {\n absolutePath: abs,\n kind: 'directory',\n size: 0,\n mimeType: 'application/x-directory',\n hasIndexHtml,\n };\n }\n if (!st.isFile()) throw new Error('Path is not a regular file or directory');\n const fileName = basename(abs);\n return {\n absolutePath: abs,\n kind: 'file',\n size: st.size,\n mimeType: resolveMimeType(fileName),\n hasIndexHtml: false,\n };\n}\n\n/** Pure decision function — exported for tests. */\nexport function decideShareKind(probe: ShareAutoProbe, mode: ShareAutoMode | undefined): ShareAutoDecision {\n if (mode === 'force-file') {\n return forced(probe.kind === 'directory' ? 'zip' : 'file', probe);\n }\n if (mode === 'force-site') {\n if (probe.kind !== 'directory' && probe.mimeType !== 'text/html') {\n throw new Error('force-site requires a directory or a single HTML file');\n }\n return { kind: 'site', isDirectory: probe.kind === 'directory', reason: 'forced', hint: '已按指定方式作为站点分享' };\n }\n if (mode === 'force-zip') {\n if (probe.kind !== 'directory') throw new Error('force-zip requires a directory');\n return { kind: 'zip', isDirectory: true, reason: 'forced', hint: '已按指定方式打包为 ZIP 分享' };\n }\n\n // mode === undefined or 'auto'\n if (probe.kind === 'directory') {\n if (probe.hasIndexHtml) {\n return {\n kind: 'site',\n isDirectory: true,\n reason: 'html-with-assets',\n hint: '目录包含 index.html,已通过站点分享渲染',\n };\n }\n return {\n kind: 'file',\n isDirectory: true,\n reason: 'directory-browse',\n hint: '目录较多文件,通过分享页可浏览或下载 ZIP',\n };\n }\n\n if (probe.mimeType === 'text/html') {\n return {\n kind: 'site',\n isDirectory: false,\n reason: 'html-single-file',\n hint: 'HTML 文件,分享后朋友点开即渲染',\n };\n }\n\n if (probe.mimeType.startsWith('image/') && probe.size < 10 * 1024 * 1024) {\n return {\n kind: 'file',\n isDirectory: false,\n reason: 'small-image',\n hint: '图片文件,朋友可直接预览或保存',\n };\n }\n\n return {\n kind: 'file',\n isDirectory: false,\n reason: 'large-binary',\n hint: '通过分享链接由朋友点开下载',\n };\n}\n\nfunction forced(kind: ShareAutoKind, probe: ShareAutoProbe): ShareAutoDecision {\n return {\n kind,\n isDirectory: probe.kind === 'directory',\n reason: 'forced',\n hint: '按指定方式分享',\n };\n}\n\n/** Build the user-facing title (trimmed, no extension). */\nexport function makeTitle(fileName: string, override?: string): string {\n if (override && override.trim()) return clip(override.trim(), 60);\n const idx = fileName.lastIndexOf('.');\n const base = idx > 0 ? fileName.slice(0, idx) : fileName;\n return clip(base || fileName, 60);\n}\n\n/** Build the user-facing description (audience + TTL hint baked in). */\nexport function makeDescription(opts: {\n audience: ShareAudience | undefined;\n expiresAt: string;\n override?: string;\n}): string {\n if (opts.override && opts.override.trim()) return clip(opts.override.trim(), 140);\n const now = Date.now();\n const exp = new Date(opts.expiresAt).getTime();\n const days = Math.max(1, Math.round((exp - now) / (24 * 60 * 60_000)));\n const audienceHint =\n opts.audience === 'public' ? '公开分享' :\n opts.audience === 'colleague' ? '同事可见' : '好友可见';\n return clip(`由 xopc 生成 · ${audienceHint} · ${days} 天内有效`, 140);\n}\n\nfunction clip(text: string, max: number): string {\n if (text.length <= max) return text;\n return `${text.slice(0, max - 1)}…`;\n}\n\n/** Subfolder under the workspace where single-HTML site shares are staged. */\nexport const STAGING_DIR_NAME = '.xopc-share-staging';\n\nexport interface StagedSite {\n /** Absolute path of the freshly-created staging directory. */\n stagingDir: string;\n /** Workspace-relative path to feed into SiteShareStore.create({ path }). */\n relativePath: string;\n}\n\n/**\n * Copy a single HTML file into `<workspaceRoot>/.xopc-share-staging/<uuid>/index.html`\n * so it can be served as a site (SiteShareStore needs a directory root).\n *\n * The caller must register a cleanup that calls `cleanupStagedSite(stagingDir)`\n * when the underlying site share is revoked / expires.\n */\nexport async function stageSingleHtmlAsSite(\n workspaceRoot: string,\n absoluteHtmlPath: string,\n): Promise<StagedSite> {\n const stagingRoot = resolvePath(workspaceRoot, STAGING_DIR_NAME);\n await mkdir(stagingRoot, { recursive: true });\n const id = randomUUID();\n const stagingDir = join(stagingRoot, id);\n await mkdir(stagingDir, { recursive: true });\n const target = join(stagingDir, 'index.html');\n await copyFile(absoluteHtmlPath, target);\n // Workspace-relative POSIX form expected by SiteShareStore.\n const rel = `${STAGING_DIR_NAME}/${id}`;\n return { stagingDir, relativePath: rel };\n}\n\n/** Best-effort cleanup; never throws. */\nexport async function cleanupStagedSite(stagingDir: string): Promise<void> {\n // Defensive: only remove if it's inside our staging folder name.\n if (!stagingDir.includes(STAGING_DIR_NAME)) return;\n try {\n await rm(stagingDir, { recursive: true, force: true });\n } catch {\n /* ignore — disk may already be gone */\n }\n}\n\n// ── Site staging registry (in-process) ────────────────────────────────────────\n//\n// Maps a site-share record id → absolute staging directory created for it.\n// Used by the SiteShareStore cleanup hook to delete the directory when the\n// share is revoked / expires. Restart-safe in the trivial sense: if the\n// process dies before cleanup fires, the directory just lingers on disk;\n// it is bounded (one folder per stale share) and gets swept by\n// `sweepOrphanedStagingDirs` at startup.\n\nconst stagedSiteByRecordId = new Map<string, string>();\n\nexport function rememberStagedSite(recordId: string, stagingDir: string): void {\n stagedSiteByRecordId.set(recordId, stagingDir);\n}\n\nexport function forgetStagedSite(recordId: string): string | undefined {\n const dir = stagedSiteByRecordId.get(recordId);\n stagedSiteByRecordId.delete(recordId);\n return dir;\n}\n\n/**\n * Walk `<workspaceRoot>/.xopc-share-staging/` and drop directories that are\n * no longer referenced by any live site-share record. Call at gateway boot.\n */\nexport async function sweepOrphanedStagingDirs(\n workspaceRoot: string,\n liveStagingDirs: Set<string>,\n): Promise<void> {\n const stagingRoot = resolvePath(workspaceRoot, STAGING_DIR_NAME);\n let entries: string[];\n try {\n entries = readdirSync(stagingRoot);\n } catch {\n return; // dir doesn't exist; nothing to sweep\n }\n for (const name of entries) {\n const abs = join(stagingRoot, name);\n if (liveStagingDirs.has(abs)) continue;\n try {\n await rm(abs, { recursive: true, force: true });\n } catch {\n /* ignore */\n }\n }\n}\n\n/**\n * Boot-time housekeeping:\n * 1. Walk active static site shares whose source lives under STAGING_DIR_NAME\n * (i.e. created via `stageSingleHtmlAsSite`).\n * 2. Re-register them in the in-process registry so the SiteShareStore\n * cleanup hook still wipes the right directory on revoke/expire after a\n * restart.\n * 3. Sweep each workspace's `.xopc-share-staging/` for entries that are not\n * referenced by any live record — these are leftovers from a previous\n * process death between create-staging and persist.\n *\n * Safe to call multiple times; idempotent. Best invoked from gateway.start().\n */\nexport async function runStagingSweep(): Promise<void> {\n // Lazy import to keep this module dependency-light for tests that only\n // exercise the pure decision helpers above.\n const { getSiteShareStore } = await import('./site-share-store.js');\n const store = getSiteShareStore();\n const records = store.getActiveShares();\n\n const liveByWorkspace = new Map<string, Set<string>>();\n for (const rec of records) {\n if (rec.source.kind !== 'static') continue;\n const ws = rec.source.workspaceRoot;\n const rootDir = rec.source.rootDir;\n if (!ws || !rootDir) continue;\n if (!rec.source.workspaceRelativePath.startsWith(`${STAGING_DIR_NAME}/`)) continue;\n rememberStagedSite(rec.id, rootDir);\n const bucket = liveByWorkspace.get(ws) ?? new Set<string>();\n bucket.add(rootDir);\n liveByWorkspace.set(ws, bucket);\n }\n\n // Workspaces with NO live staged shares still need a sweep: a record may\n // have expired (so getActiveShares filters it out) without its staging dir\n // being deleted. We collect those workspaces too.\n for (const rec of store.getAllShares()) {\n if (rec.source.kind !== 'static') continue;\n if (!rec.source.workspaceRelativePath.startsWith(`${STAGING_DIR_NAME}/`)) continue;\n if (!liveByWorkspace.has(rec.source.workspaceRoot)) {\n liveByWorkspace.set(rec.source.workspaceRoot, new Set());\n }\n }\n\n for (const [workspaceRoot, live] of liveByWorkspace) {\n await sweepOrphanedStagingDirs(workspaceRoot, live);\n }\n}\n\nexport function resetStagedSiteRegistryForTests(): void {\n stagedSiteByRecordId.clear();\n}\n"],"mappings":"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"}
|
|
@@ -1,14 +1,62 @@
|
|
|
1
1
|
import { SHARE_CONFIG_DEFAULTS } from "./share-types.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
//#region src/share/share-config.ts
|
|
4
|
+
const ShareDirectoryPatchSchema = z.object({
|
|
5
|
+
enabled: z.boolean().optional(),
|
|
6
|
+
maxFolderSize: z.number().int().min(1048576).max(10737418240).optional(),
|
|
7
|
+
maxFileCount: z.number().int().min(1).max(1e5).optional(),
|
|
8
|
+
maxDepth: z.number().int().min(1).max(64).optional(),
|
|
9
|
+
listingCacheMs: z.number().int().min(0).max(6e5).optional(),
|
|
10
|
+
zipConcurrency: z.number().int().min(1).max(8).optional()
|
|
11
|
+
});
|
|
12
|
+
const ShareThumbnailPatchSchema = z.object({
|
|
13
|
+
enabled: z.boolean().optional(),
|
|
14
|
+
concurrency: z.number().int().min(1).max(8).optional(),
|
|
15
|
+
maxBytes: z.number().int().min(8192).max(4194304).optional(),
|
|
16
|
+
viewportWidth: z.number().int().min(320).max(2560).optional(),
|
|
17
|
+
viewportHeight: z.number().int().min(320).max(2560).optional(),
|
|
18
|
+
generationTimeoutMs: z.number().int().min(1e3).max(6e4).optional(),
|
|
19
|
+
failureCooldownMs: z.number().int().min(0).max(36e5).optional(),
|
|
20
|
+
internalGatewayUrl: z.string().min(1).optional()
|
|
21
|
+
});
|
|
4
22
|
const ShareConfigPatchSchema = z.object({
|
|
5
23
|
enabled: z.boolean().optional(),
|
|
6
24
|
defaultTtlMs: z.number().int().min(6e4).max(6048e5).optional(),
|
|
7
25
|
maxTtlMs: z.number().int().min(6e4).max(2592e6).optional(),
|
|
8
26
|
maxActiveShares: z.number().int().min(1).max(1e4).optional(),
|
|
9
27
|
maxFileSize: z.number().int().min(1048576).max(10737418240).optional(),
|
|
10
|
-
inlinePreviewMimes: z.array(z.string().min(1)).optional()
|
|
28
|
+
inlinePreviewMimes: z.array(z.string().min(1)).optional(),
|
|
29
|
+
directory: ShareDirectoryPatchSchema.optional(),
|
|
30
|
+
thumbnail: ShareThumbnailPatchSchema.optional()
|
|
11
31
|
});
|
|
32
|
+
function resolveDirectoryConfig(raw) {
|
|
33
|
+
const base = SHARE_CONFIG_DEFAULTS.directory;
|
|
34
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return { ...base };
|
|
35
|
+
const patch = raw;
|
|
36
|
+
return {
|
|
37
|
+
enabled: patch.enabled ?? base.enabled,
|
|
38
|
+
maxFolderSize: patch.maxFolderSize ?? base.maxFolderSize,
|
|
39
|
+
maxFileCount: patch.maxFileCount ?? base.maxFileCount,
|
|
40
|
+
maxDepth: patch.maxDepth ?? base.maxDepth,
|
|
41
|
+
listingCacheMs: patch.listingCacheMs ?? base.listingCacheMs,
|
|
42
|
+
zipConcurrency: patch.zipConcurrency ?? base.zipConcurrency
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function resolveThumbnailConfig(raw) {
|
|
46
|
+
const base = SHARE_CONFIG_DEFAULTS.thumbnail;
|
|
47
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return { ...base };
|
|
48
|
+
const patch = raw;
|
|
49
|
+
return {
|
|
50
|
+
enabled: patch.enabled ?? base.enabled,
|
|
51
|
+
concurrency: patch.concurrency ?? base.concurrency,
|
|
52
|
+
maxBytes: patch.maxBytes ?? base.maxBytes,
|
|
53
|
+
viewportWidth: patch.viewportWidth ?? base.viewportWidth,
|
|
54
|
+
viewportHeight: patch.viewportHeight ?? base.viewportHeight,
|
|
55
|
+
generationTimeoutMs: patch.generationTimeoutMs ?? base.generationTimeoutMs,
|
|
56
|
+
failureCooldownMs: patch.failureCooldownMs ?? base.failureCooldownMs,
|
|
57
|
+
internalGatewayUrl: patch.internalGatewayUrl ?? base.internalGatewayUrl
|
|
58
|
+
};
|
|
59
|
+
}
|
|
12
60
|
function resolveShareConfig(raw) {
|
|
13
61
|
const patch = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
14
62
|
return {
|
|
@@ -17,7 +65,9 @@ function resolveShareConfig(raw) {
|
|
|
17
65
|
maxTtlMs: patch.maxTtlMs ?? SHARE_CONFIG_DEFAULTS.maxTtlMs,
|
|
18
66
|
maxActiveShares: patch.maxActiveShares ?? SHARE_CONFIG_DEFAULTS.maxActiveShares,
|
|
19
67
|
maxFileSize: patch.maxFileSize ?? SHARE_CONFIG_DEFAULTS.maxFileSize,
|
|
20
|
-
inlinePreviewMimes: Array.isArray(patch.inlinePreviewMimes) ? patch.inlinePreviewMimes.filter((x) => typeof x === "string" && x.trim().length > 0) : [...SHARE_CONFIG_DEFAULTS.inlinePreviewMimes]
|
|
68
|
+
inlinePreviewMimes: Array.isArray(patch.inlinePreviewMimes) ? patch.inlinePreviewMimes.filter((x) => typeof x === "string" && x.trim().length > 0) : [...SHARE_CONFIG_DEFAULTS.inlinePreviewMimes],
|
|
69
|
+
directory: resolveDirectoryConfig(patch.directory),
|
|
70
|
+
thumbnail: resolveThumbnailConfig(patch.thumbnail)
|
|
21
71
|
};
|
|
22
72
|
}
|
|
23
73
|
function mergeShareConfigPatch(config, patch) {
|
|
@@ -38,10 +88,19 @@ function mergeShareConfigPatch(config, patch) {
|
|
|
38
88
|
maxSseConnections: 100,
|
|
39
89
|
corsOrigins: []
|
|
40
90
|
};
|
|
91
|
+
const current = resolveShareConfig(config.gateway.share);
|
|
41
92
|
const next = {
|
|
42
|
-
...
|
|
93
|
+
...current,
|
|
43
94
|
...parsed.data,
|
|
44
|
-
|
|
95
|
+
inlinePreviewMimes: parsed.data.inlinePreviewMimes ? parsed.data.inlinePreviewMimes.map((x) => x.trim()).filter(Boolean) : current.inlinePreviewMimes,
|
|
96
|
+
directory: parsed.data.directory ? {
|
|
97
|
+
...current.directory,
|
|
98
|
+
...parsed.data.directory
|
|
99
|
+
} : current.directory,
|
|
100
|
+
thumbnail: parsed.data.thumbnail ? {
|
|
101
|
+
...current.thumbnail,
|
|
102
|
+
...parsed.data.thumbnail
|
|
103
|
+
} : current.thumbnail
|
|
45
104
|
};
|
|
46
105
|
if (next.defaultTtlMs > next.maxTtlMs) return {
|
|
47
106
|
ok: false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"share-config.js","names":[],"sources":["../../../src/share/share-config.ts"],"sourcesContent":["import { z } from 'zod';\n\nimport type { Config } from '../config/schema.js';\nimport {
|
|
1
|
+
{"version":3,"file":"share-config.js","names":[],"sources":["../../../src/share/share-config.ts"],"sourcesContent":["import { z } from 'zod';\n\nimport type { Config } from '../config/schema.js';\nimport {\n SHARE_CONFIG_DEFAULTS,\n type ShareConfig,\n type ShareDirectoryConfig,\n type ShareThumbnailConfig,\n} from './share-types.js';\n\nconst ShareDirectoryPatchSchema = z.object({\n enabled: z.boolean().optional(),\n maxFolderSize: z.number().int().min(1_048_576).max(10_737_418_240).optional(),\n maxFileCount: z.number().int().min(1).max(100_000).optional(),\n maxDepth: z.number().int().min(1).max(64).optional(),\n listingCacheMs: z.number().int().min(0).max(600_000).optional(),\n zipConcurrency: z.number().int().min(1).max(8).optional(),\n});\n\nconst ShareThumbnailPatchSchema = z.object({\n enabled: z.boolean().optional(),\n concurrency: z.number().int().min(1).max(8).optional(),\n maxBytes: z.number().int().min(8_192).max(4_194_304).optional(),\n viewportWidth: z.number().int().min(320).max(2_560).optional(),\n viewportHeight: z.number().int().min(320).max(2_560).optional(),\n generationTimeoutMs: z.number().int().min(1_000).max(60_000).optional(),\n failureCooldownMs: z.number().int().min(0).max(3_600_000).optional(),\n internalGatewayUrl: z.string().min(1).optional(),\n});\n\nconst ShareConfigPatchSchema = z.object({\n enabled: z.boolean().optional(),\n defaultTtlMs: z.number().int().min(60_000).max(604_800_000).optional(),\n maxTtlMs: z.number().int().min(60_000).max(2_592_000_000).optional(),\n maxActiveShares: z.number().int().min(1).max(10_000).optional(),\n maxFileSize: z.number().int().min(1_048_576).max(10_737_418_240).optional(),\n inlinePreviewMimes: z.array(z.string().min(1)).optional(),\n directory: ShareDirectoryPatchSchema.optional(),\n thumbnail: ShareThumbnailPatchSchema.optional(),\n});\n\nfunction resolveDirectoryConfig(raw: unknown): ShareDirectoryConfig {\n const base = SHARE_CONFIG_DEFAULTS.directory;\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return { ...base };\n const patch = raw as Partial<ShareDirectoryConfig>;\n return {\n enabled: patch.enabled ?? base.enabled,\n maxFolderSize: patch.maxFolderSize ?? base.maxFolderSize,\n maxFileCount: patch.maxFileCount ?? base.maxFileCount,\n maxDepth: patch.maxDepth ?? base.maxDepth,\n listingCacheMs: patch.listingCacheMs ?? base.listingCacheMs,\n zipConcurrency: patch.zipConcurrency ?? base.zipConcurrency,\n };\n}\n\nfunction resolveThumbnailConfig(raw: unknown): ShareThumbnailConfig {\n const base = SHARE_CONFIG_DEFAULTS.thumbnail;\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return { ...base };\n const patch = raw as Partial<ShareThumbnailConfig>;\n return {\n enabled: patch.enabled ?? base.enabled,\n concurrency: patch.concurrency ?? base.concurrency,\n maxBytes: patch.maxBytes ?? base.maxBytes,\n viewportWidth: patch.viewportWidth ?? base.viewportWidth,\n viewportHeight: patch.viewportHeight ?? base.viewportHeight,\n generationTimeoutMs: patch.generationTimeoutMs ?? base.generationTimeoutMs,\n failureCooldownMs: patch.failureCooldownMs ?? base.failureCooldownMs,\n internalGatewayUrl: patch.internalGatewayUrl ?? base.internalGatewayUrl,\n };\n}\n\nexport function resolveShareConfig(raw: unknown): ShareConfig {\n const patch =\n raw && typeof raw === 'object' && !Array.isArray(raw)\n ? (raw as Partial<ShareConfig> & { directory?: unknown; thumbnail?: unknown })\n : {};\n return {\n enabled: patch.enabled ?? SHARE_CONFIG_DEFAULTS.enabled,\n defaultTtlMs: patch.defaultTtlMs ?? SHARE_CONFIG_DEFAULTS.defaultTtlMs,\n maxTtlMs: patch.maxTtlMs ?? SHARE_CONFIG_DEFAULTS.maxTtlMs,\n maxActiveShares: patch.maxActiveShares ?? SHARE_CONFIG_DEFAULTS.maxActiveShares,\n maxFileSize: patch.maxFileSize ?? SHARE_CONFIG_DEFAULTS.maxFileSize,\n inlinePreviewMimes: Array.isArray(patch.inlinePreviewMimes)\n ? patch.inlinePreviewMimes.filter((x): x is string => typeof x === 'string' && x.trim().length > 0)\n : [...SHARE_CONFIG_DEFAULTS.inlinePreviewMimes],\n directory: resolveDirectoryConfig(patch.directory),\n thumbnail: resolveThumbnailConfig(patch.thumbnail),\n };\n}\n\nexport function mergeShareConfigPatch(\n config: Config,\n patch: Record<string, unknown>,\n): { ok: true } | { ok: false; message: string } {\n const parsed = ShareConfigPatchSchema.safeParse(patch);\n if (!parsed.success) {\n return { ok: false, message: parsed.error.issues.map((i) => i.message).join('; ') };\n }\n\n if (!config.gateway) {\n config.gateway = {\n bind: 'loopback',\n port: 18790,\n auth: { mode: 'token' },\n heartbeat: { enabled: true, intervalMs: 1_800_000, includeSystemPromptSection: false },\n maxSseConnections: 100,\n corsOrigins: [],\n };\n }\n\n const current = resolveShareConfig(config.gateway.share);\n const next: ShareConfig = {\n ...current,\n ...parsed.data,\n inlinePreviewMimes: parsed.data.inlinePreviewMimes\n ? parsed.data.inlinePreviewMimes.map((x) => x.trim()).filter(Boolean)\n : current.inlinePreviewMimes,\n directory: parsed.data.directory\n ? { ...current.directory, ...parsed.data.directory }\n : current.directory,\n thumbnail: parsed.data.thumbnail\n ? { ...current.thumbnail, ...parsed.data.thumbnail }\n : current.thumbnail,\n };\n\n if (next.defaultTtlMs > next.maxTtlMs) {\n return {\n ok: false,\n message: 'gateway.share.defaultTtlMs must not exceed gateway.share.maxTtlMs',\n };\n }\n\n config.gateway.share = next;\n return { ok: true };\n}\n"],"mappings":";;;AAUA,MAAM,4BAA4B,EAAE,OAAO;CACzC,SAAS,EAAE,SAAS,CAAC,UAAU;CAC/B,eAAe,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,QAAU,CAAC,IAAI,YAAe,CAAC,UAAU;CAC7E,cAAc,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,IAAQ,CAAC,UAAU;CAC7D,UAAU,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,GAAG,CAAC,UAAU;CACpD,gBAAgB,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,IAAQ,CAAC,UAAU;CAC/D,gBAAgB,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,UAAU;CAC1D,CAAC;AAEF,MAAM,4BAA4B,EAAE,OAAO;CACzC,SAAS,EAAE,SAAS,CAAC,UAAU;CAC/B,aAAa,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,UAAU;CACtD,UAAU,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAM,CAAC,IAAI,QAAU,CAAC,UAAU;CAC/D,eAAe,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,IAAI,KAAM,CAAC,UAAU;CAC9D,gBAAgB,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,IAAI,KAAM,CAAC,UAAU;CAC/D,qBAAqB,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAM,CAAC,IAAI,IAAO,CAAC,UAAU;CACvE,mBAAmB,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,KAAU,CAAC,UAAU;CACpE,oBAAoB,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,UAAU;CACjD,CAAC;AAEF,MAAM,yBAAyB,EAAE,OAAO;CACtC,SAAS,EAAE,SAAS,CAAC,UAAU;CAC/B,cAAc,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAO,CAAC,IAAI,OAAY,CAAC,UAAU;CACtE,UAAU,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAO,CAAC,IAAI,OAAc,CAAC,UAAU;CACpE,iBAAiB,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,IAAO,CAAC,UAAU;CAC/D,aAAa,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,QAAU,CAAC,IAAI,YAAe,CAAC,UAAU;CAC3E,oBAAoB,EAAE,MAAM,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,UAAU;CACzD,WAAW,0BAA0B,UAAU;CAC/C,WAAW,0BAA0B,UAAU;CAChD,CAAC;AAEF,SAAS,uBAAuB,KAAoC;CAClE,MAAM,OAAO,sBAAsB;AACnC,KAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAAE,QAAO,EAAE,GAAG,MAAM;CAC7E,MAAM,QAAQ;AACd,QAAO;EACL,SAAS,MAAM,WAAW,KAAK;EAC/B,eAAe,MAAM,iBAAiB,KAAK;EAC3C,cAAc,MAAM,gBAAgB,KAAK;EACzC,UAAU,MAAM,YAAY,KAAK;EACjC,gBAAgB,MAAM,kBAAkB,KAAK;EAC7C,gBAAgB,MAAM,kBAAkB,KAAK;EAC9C;;AAGH,SAAS,uBAAuB,KAAoC;CAClE,MAAM,OAAO,sBAAsB;AACnC,KAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAAE,QAAO,EAAE,GAAG,MAAM;CAC7E,MAAM,QAAQ;AACd,QAAO;EACL,SAAS,MAAM,WAAW,KAAK;EAC/B,aAAa,MAAM,eAAe,KAAK;EACvC,UAAU,MAAM,YAAY,KAAK;EACjC,eAAe,MAAM,iBAAiB,KAAK;EAC3C,gBAAgB,MAAM,kBAAkB,KAAK;EAC7C,qBAAqB,MAAM,uBAAuB,KAAK;EACvD,mBAAmB,MAAM,qBAAqB,KAAK;EACnD,oBAAoB,MAAM,sBAAsB,KAAK;EACtD;;AAGH,SAAgB,mBAAmB,KAA2B;CAC5D,MAAM,QACJ,OAAO,OAAO,QAAQ,YAAY,CAAC,MAAM,QAAQ,IAAI,GAChD,MACD,EAAE;AACR,QAAO;EACL,SAAS,MAAM,WAAW,sBAAsB;EAChD,cAAc,MAAM,gBAAgB,sBAAsB;EAC1D,UAAU,MAAM,YAAY,sBAAsB;EAClD,iBAAiB,MAAM,mBAAmB,sBAAsB;EAChE,aAAa,MAAM,eAAe,sBAAsB;EACxD,oBAAoB,MAAM,QAAQ,MAAM,mBAAmB,GACvD,MAAM,mBAAmB,QAAQ,MAAmB,OAAO,MAAM,YAAY,EAAE,MAAM,CAAC,SAAS,EAAE,GACjG,CAAC,GAAG,sBAAsB,mBAAmB;EACjD,WAAW,uBAAuB,MAAM,UAAU;EAClD,WAAW,uBAAuB,MAAM,UAAU;EACnD;;AAGH,SAAgB,sBACd,QACA,OAC+C;CAC/C,MAAM,SAAS,uBAAuB,UAAU,MAAM;AACtD,KAAI,CAAC,OAAO,QACV,QAAO;EAAE,IAAI;EAAO,SAAS,OAAO,MAAM,OAAO,KAAK,MAAM,EAAE,QAAQ,CAAC,KAAK,KAAK;EAAE;AAGrF,KAAI,CAAC,OAAO,QACV,QAAO,UAAU;EACf,MAAM;EACN,MAAM;EACN,MAAM,EAAE,MAAM,SAAS;EACvB,WAAW;GAAE,SAAS;GAAM,YAAY;GAAW,4BAA4B;GAAO;EACtF,mBAAmB;EACnB,aAAa,EAAE;EAChB;CAGH,MAAM,UAAU,mBAAmB,OAAO,QAAQ,MAAM;CACxD,MAAM,OAAoB;EACxB,GAAG;EACH,GAAG,OAAO;EACV,oBAAoB,OAAO,KAAK,qBAC5B,OAAO,KAAK,mBAAmB,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC,OAAO,QAAQ,GACnE,QAAQ;EACZ,WAAW,OAAO,KAAK,YACnB;GAAE,GAAG,QAAQ;GAAW,GAAG,OAAO,KAAK;GAAW,GAClD,QAAQ;EACZ,WAAW,OAAO,KAAK,YACnB;GAAE,GAAG,QAAQ;GAAW,GAAG,OAAO,KAAK;GAAW,GAClD,QAAQ;EACb;AAED,KAAI,KAAK,eAAe,KAAK,SAC3B,QAAO;EACL,IAAI;EACJ,SAAS;EACV;AAGH,QAAO,QAAQ,QAAQ;AACvB,QAAO,EAAE,IAAI,MAAM"}
|
|
@@ -1,6 +1,32 @@
|
|
|
1
|
+
import type { DirectoryListing } from './share-store.js';
|
|
1
2
|
import type { ShareRecord } from './share-types.js';
|
|
2
|
-
|
|
3
|
-
|
|
3
|
+
export interface ShareLandingOgOptions {
|
|
4
|
+
/** Absolute https/http URL of this landing page (for og:url). Omit if non-public. */
|
|
5
|
+
absoluteShareUrl?: string | null;
|
|
6
|
+
/** Absolute URL of the thumbnail image (for og:image). Omit if non-public. */
|
|
7
|
+
absoluteThumbnailUrl?: string | null;
|
|
8
|
+
/** Title override (default: fileName). */
|
|
9
|
+
title?: string;
|
|
10
|
+
/** Description override (default: type + size). */
|
|
11
|
+
description?: string;
|
|
12
|
+
}
|
|
13
|
+
/** Render the file download confirmation landing page (does not consume downloadCount). */
|
|
14
|
+
export declare function renderShareLandingPage(record: ShareRecord, downloadPath: string, options?: {
|
|
15
|
+
/** Path to open the file inline (?inline=1) for browser-native rendering. */
|
|
16
|
+
inlineUrl?: string | null;
|
|
17
|
+
/** SPA preview URL (for markdown / docs / code that benefit from rich rendering). */
|
|
18
|
+
previewUrl?: string | null;
|
|
19
|
+
/** Social card / unfurl metadata. */
|
|
20
|
+
og?: ShareLandingOgOptions;
|
|
21
|
+
}): string;
|
|
22
|
+
/** Render the folder landing page (browse mode shows a tree, zip-only shows a single button). */
|
|
23
|
+
export declare function renderFolderLandingPage(record: ShareRecord, listing: DirectoryListing | null, urls: {
|
|
24
|
+
tree: (path: string) => string;
|
|
25
|
+
file: (path: string) => string;
|
|
26
|
+
zip: (path: string) => string;
|
|
27
|
+
}, options?: {
|
|
28
|
+
og?: ShareLandingOgOptions;
|
|
29
|
+
}): string;
|
|
4
30
|
export type ShareExpiredReason = 'expired' | 'revoked' | 'max_views' | 'not_found' | 'file_deleted';
|
|
5
31
|
/** Render the "share no longer valid" page — does NOT reveal file name or path. */
|
|
6
32
|
export declare function renderShareExpiredPage(reason: ShareExpiredReason): string;
|