@xopcai/xopc 0.0.84 → 0.0.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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-mS3_HpRI.js +222 -0
- package/dist/gateway/static/root/assets/apps-page-DrfytjOb.js +1 -0
- package/dist/gateway/static/root/assets/channels-settings-BG6b9KrW.js +1 -0
- package/dist/gateway/static/root/assets/channels-status-swr-Bs5kMCMI.js +8 -0
- package/dist/gateway/static/root/assets/createLucideIcon-DPHK1VkS.js +1 -0
- package/dist/gateway/static/root/assets/cron-api-BuVcZ5zR.js +1 -0
- package/dist/gateway/static/root/assets/cron-page-BMrloeFH.js +1 -0
- package/dist/gateway/static/root/assets/dist-BTWC-BTN.js +45 -0
- package/dist/gateway/static/root/assets/{dist-CqNMNhJM.js → dist-CKU1OOTf.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-gf2L0kY_.js → extension-debug-page-BdW_46sN.js} +1 -1
- package/dist/gateway/static/root/assets/extension-page-DW47KI82.js +1 -0
- package/dist/gateway/static/root/assets/extension-settings-page-B-W4x2xP.js +1 -0
- package/dist/gateway/static/root/assets/fetch-B2MYHbWg.js +1 -0
- package/dist/gateway/static/root/assets/{field-primitives-DTtlp-l8.js → field-primitives-DPG-oJmx.js} +1 -1
- package/dist/gateway/static/root/assets/heartbeat-config-api-C8dNts9i.js +1 -0
- package/dist/gateway/static/root/assets/index-BmVYculr.js +4700 -0
- package/dist/gateway/static/root/assets/index-ew_2L2We.css +1 -0
- package/dist/gateway/static/root/assets/logs-page-sTsVWz0X.js +1 -0
- package/dist/gateway/static/root/assets/sessions-page-FaG_Vlkb.js +1 -0
- package/dist/gateway/static/root/assets/settings-form-section-DuvRQW--.js +1 -0
- package/dist/gateway/static/root/assets/settings-page-Bet1OerL.js +3 -0
- package/dist/gateway/static/root/assets/share-preview-page-BtG2kLDh.js +2 -0
- package/dist/gateway/static/root/assets/skills-page-DhUO235y.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-BY7bU1DT.js +1 -0
- package/dist/gateway/static/root/assets/voice-api-key-field-CGEydndO.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 +13 -0
- package/dist/src/agent/workflow/builtins/audit-repo.js +156 -0
- package/dist/src/agent/workflow/builtins/audit-repo.js.map +1 -0
- package/dist/src/agent/workflow/builtins/debug-incident.d.ts +13 -0
- package/dist/src/agent/workflow/builtins/debug-incident.js +155 -0
- package/dist/src/agent/workflow/builtins/debug-incident.js.map +1 -0
- package/dist/src/agent/workflow/builtins/index.d.ts +17 -0
- package/dist/src/agent/workflow/builtins/index.js +38 -0
- package/dist/src/agent/workflow/builtins/index.js.map +1 -0
- package/dist/src/agent/workflow/builtins/multi-perspective-review.d.ts +14 -0
- package/dist/src/agent/workflow/builtins/multi-perspective-review.js +149 -0
- package/dist/src/agent/workflow/builtins/multi-perspective-review.js.map +1 -0
- package/dist/src/agent/workflow/builtins/pr-review.d.ts +12 -0
- package/dist/src/agent/workflow/builtins/pr-review.js +156 -0
- package/dist/src/agent/workflow/builtins/pr-review.js.map +1 -0
- package/dist/src/agent/workflow/builtins/research.d.ts +13 -0
- package/dist/src/agent/workflow/builtins/research.js +160 -0
- package/dist/src/agent/workflow/builtins/research.js.map +1 -0
- package/dist/src/agent/workflow/catalog.d.ts +56 -0
- package/dist/src/agent/workflow/catalog.js +159 -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 +146 -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 +145 -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 +172 -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/commands/config.js +2 -2
- 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.js +10 -4
- package/dist/src/cli/commands/gateway/lifecycle.js.map +1 -1
- package/dist/src/cli/commands/gateway/shared.js +1 -1
- package/dist/src/cli/commands/image.js +1 -1
- package/dist/src/cli/commands/init.js +4 -4
- package/dist/src/cli/commands/onboard.js +2 -2
- package/dist/src/cli/commands/tunnel.js +2 -2
- 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-tR-nNP04.js +0 -222
- package/dist/gateway/static/root/assets/apps-page-BDw6SP-d.js +0 -1
- package/dist/gateway/static/root/assets/button-KafIU8dx.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-DEFd-jj1.js +0 -1
- package/dist/gateway/static/root/assets/channels-status-swr-DI5FHdGe.js +0 -8
- package/dist/gateway/static/root/assets/cron-api-BSqY8LwW.js +0 -1
- package/dist/gateway/static/root/assets/cron-page-D7lVDjcR.js +0 -1
- package/dist/gateway/static/root/assets/dist-C57OMHW8.js +0 -48
- package/dist/gateway/static/root/assets/extension-page-CQo2Xsmg.js +0 -1
- package/dist/gateway/static/root/assets/extension-settings-page-CZf0WoZg.js +0 -1
- package/dist/gateway/static/root/assets/fetch-2iRFmd3n.js +0 -3
- package/dist/gateway/static/root/assets/heartbeat-config-api-B0drdQEJ.js +0 -1
- package/dist/gateway/static/root/assets/index-0Gt3TG4j.js +0 -4693
- package/dist/gateway/static/root/assets/index-BuFldCsB.css +0 -1
- package/dist/gateway/static/root/assets/logs-page-DMuORLfC.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-_UO8g6NN.js +0 -1
- package/dist/gateway/static/root/assets/settings-form-section-DkmHkknc.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-Cz8FoW_A.js +0 -3
- package/dist/gateway/static/root/assets/skills-page-HrUOxF7H.js +0 -2
- package/dist/gateway/static/root/assets/theme-store-D01dJt95.js +0 -1
- package/dist/gateway/static/root/assets/utils-BFwcR6pL.js +0 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-JF8-aqc5.js +0 -1
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
import { resolveStateDir } from "../config/paths-state.js";
|
|
2
|
+
import { createLogger } from "../utils/logger/index.js";
|
|
3
|
+
import { init_logger } from "../utils/logger.js";
|
|
4
|
+
import { init_paths } from "../config/paths.js";
|
|
5
|
+
import { SITE_SHARE_CONFIG_DEFAULTS } from "./site-share-types.js";
|
|
6
|
+
import { isPathUnderWorkspace } from "../gateway/workspace-editor-path.js";
|
|
7
|
+
import { join, relative, resolve } from "node:path";
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { randomBytes, randomUUID } from "node:crypto";
|
|
10
|
+
import { lstat, readdir, realpath, stat } from "node:fs/promises";
|
|
11
|
+
//#region src/share/site-share-store.ts
|
|
12
|
+
init_paths();
|
|
13
|
+
init_logger();
|
|
14
|
+
const log = createLogger("SiteShareStore");
|
|
15
|
+
const SITE_SHARES_FILE = "site-shares.json";
|
|
16
|
+
const CLEANUP_INTERVAL_MS = 10 * 6e4;
|
|
17
|
+
const EXPIRED_RETENTION_MS = 1440 * 6e4;
|
|
18
|
+
const MAX_STORED = 200;
|
|
19
|
+
const REQUEST_COUNT_DEBOUNCE_MS = 5e3;
|
|
20
|
+
const SUBDOMAIN_PATTERN = /^[a-z0-9]([a-z0-9-]{0,30}[a-z0-9])?$/;
|
|
21
|
+
function resolveStorePath() {
|
|
22
|
+
return join(resolveStateDir(), SITE_SHARES_FILE);
|
|
23
|
+
}
|
|
24
|
+
var SiteShareStore = class {
|
|
25
|
+
shares = /* @__PURE__ */ new Map();
|
|
26
|
+
byToken = /* @__PURE__ */ new Map();
|
|
27
|
+
bySubdomain = /* @__PURE__ */ new Map();
|
|
28
|
+
config;
|
|
29
|
+
dirty = false;
|
|
30
|
+
debounceTimer = null;
|
|
31
|
+
cleanupTimer = null;
|
|
32
|
+
/** Optional cleanup hook invoked when a record is dropped (e.g. delete staging dir). */
|
|
33
|
+
onCleanup = null;
|
|
34
|
+
constructor(config) {
|
|
35
|
+
this.config = {
|
|
36
|
+
...SITE_SHARE_CONFIG_DEFAULTS,
|
|
37
|
+
...config
|
|
38
|
+
};
|
|
39
|
+
this.load();
|
|
40
|
+
this.startCleanupTimer();
|
|
41
|
+
}
|
|
42
|
+
updateConfig(config) {
|
|
43
|
+
this.config = {
|
|
44
|
+
...this.config,
|
|
45
|
+
...config
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
setCleanupHook(hook) {
|
|
49
|
+
this.onCleanup = hook;
|
|
50
|
+
}
|
|
51
|
+
getConfig() {
|
|
52
|
+
return JSON.parse(JSON.stringify(this.config));
|
|
53
|
+
}
|
|
54
|
+
async create(params) {
|
|
55
|
+
if (!this.config.enabled) throw new Error("Site sharing is disabled");
|
|
56
|
+
if (this.getActiveShares().length >= this.config.maxActiveSites) throw new Error(`Maximum active site shares reached (${this.config.maxActiveSites})`);
|
|
57
|
+
const ttlMs = params.ttlMs ?? this.config.defaultTtlMs;
|
|
58
|
+
if (ttlMs < 6e4 || ttlMs > this.config.maxTtlMs) throw new Error(`TTL must be between 60s and ${this.config.maxTtlMs / 1e3}s`);
|
|
59
|
+
const source = await this.buildSource(params);
|
|
60
|
+
const subdomain = this.resolveSubdomain(params.subdomain);
|
|
61
|
+
const id = randomUUID();
|
|
62
|
+
const token = randomBytes(24).toString("base64url");
|
|
63
|
+
const now = /* @__PURE__ */ new Date();
|
|
64
|
+
const record = {
|
|
65
|
+
id,
|
|
66
|
+
token,
|
|
67
|
+
subdomain,
|
|
68
|
+
source,
|
|
69
|
+
createdAt: now.toISOString(),
|
|
70
|
+
expiresAt: new Date(now.getTime() + ttlMs).toISOString(),
|
|
71
|
+
revoked: false,
|
|
72
|
+
description: params.description,
|
|
73
|
+
createdByTokenHash: params.gatewayTokenHash,
|
|
74
|
+
requestCount: 0,
|
|
75
|
+
uniqueClientCount: 0,
|
|
76
|
+
maxRequests: params.maxRequests ?? null
|
|
77
|
+
};
|
|
78
|
+
this.shares.set(id, record);
|
|
79
|
+
this.byToken.set(token, id);
|
|
80
|
+
if (subdomain) this.bySubdomain.set(subdomain, id);
|
|
81
|
+
this.persistSync();
|
|
82
|
+
log.info({
|
|
83
|
+
id,
|
|
84
|
+
tokenPrefix: token.slice(0, 8),
|
|
85
|
+
subdomain,
|
|
86
|
+
kind: source.kind
|
|
87
|
+
}, `Site share created (${source.kind})`);
|
|
88
|
+
return record;
|
|
89
|
+
}
|
|
90
|
+
async buildSource(params) {
|
|
91
|
+
if (params.kind === "static") {
|
|
92
|
+
if (!this.config.static.enabled) throw new Error("Static site sharing is disabled");
|
|
93
|
+
if (!params.workspaceRoot) throw new Error("workspaceRoot required for static site shares");
|
|
94
|
+
if (!params.path) throw new Error("path required for static site shares");
|
|
95
|
+
const relPath = params.path.trim().replace(/\\/g, "/").replace(/^\/+/, "");
|
|
96
|
+
if (!relPath) throw new Error("Empty path");
|
|
97
|
+
if (relPath.includes("..") || relPath.includes("\0")) throw new Error("Invalid path");
|
|
98
|
+
const absRoot = resolve(params.workspaceRoot, relPath);
|
|
99
|
+
const relToRoot = relative(resolve(params.workspaceRoot), absRoot);
|
|
100
|
+
if (relToRoot.startsWith("..") || relToRoot.split(/[/\\]/).includes("..")) throw new Error("Path is outside workspace");
|
|
101
|
+
const real = await realpath(absRoot);
|
|
102
|
+
if (!isPathUnderWorkspace(params.workspaceRoot, real)) throw new Error("Path target is outside workspace");
|
|
103
|
+
if (!(await stat(absRoot)).isDirectory()) throw new Error("Static source must be a directory");
|
|
104
|
+
const summary = await scanStatic(absRoot, this.config.static.maxFileCount, this.config.static.maxRootDirSize);
|
|
105
|
+
log.debug({
|
|
106
|
+
...summary,
|
|
107
|
+
rootDir: absRoot
|
|
108
|
+
}, "Static site scanned");
|
|
109
|
+
return {
|
|
110
|
+
kind: "static",
|
|
111
|
+
rootDir: absRoot,
|
|
112
|
+
workspaceRoot: params.workspaceRoot,
|
|
113
|
+
workspaceRelativePath: relPath,
|
|
114
|
+
spaFallback: params.spaFallback ?? true,
|
|
115
|
+
rewriteMode: params.rewriteMode ?? (this.config.static.rewriteEnabledByDefault ? "html-css" : "none")
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
if (params.kind === "proxy") {
|
|
119
|
+
if (!this.config.proxy.enabled) throw new Error("Proxy site sharing is disabled");
|
|
120
|
+
if (!params.upstreamUrl) throw new Error("upstreamUrl required for proxy shares");
|
|
121
|
+
return {
|
|
122
|
+
kind: "proxy",
|
|
123
|
+
upstreamUrl: validateUpstreamUrl(params.upstreamUrl, this.config.proxy),
|
|
124
|
+
rewriteSetCookiePath: this.config.proxy.rewriteSetCookiePath,
|
|
125
|
+
forwardWebSocket: params.forwardWebSocket ?? this.config.proxy.forwardWebSocket,
|
|
126
|
+
forwardedHeaders: "minimal"
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
throw new Error(`Unsupported site share kind: ${params.kind}`);
|
|
130
|
+
}
|
|
131
|
+
resolveSubdomain(preferred) {
|
|
132
|
+
if (!preferred) return null;
|
|
133
|
+
const lower = preferred.trim().toLowerCase();
|
|
134
|
+
if (!SUBDOMAIN_PATTERN.test(lower)) throw new Error("Subdomain must match /^[a-z0-9]([a-z0-9-]{0,30}[a-z0-9])?$/");
|
|
135
|
+
if (this.bySubdomain.has(lower)) throw new Error(`Subdomain '${lower}' already in use`);
|
|
136
|
+
return lower;
|
|
137
|
+
}
|
|
138
|
+
getById(id) {
|
|
139
|
+
return this.shares.get(id) ?? null;
|
|
140
|
+
}
|
|
141
|
+
getByToken(token) {
|
|
142
|
+
const id = this.byToken.get(token);
|
|
143
|
+
return id ? this.shares.get(id) ?? null : null;
|
|
144
|
+
}
|
|
145
|
+
getBySubdomain(subdomain) {
|
|
146
|
+
const id = this.bySubdomain.get(subdomain.toLowerCase());
|
|
147
|
+
return id ? this.shares.get(id) ?? null : null;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Look up a record by token first, then by subdomain. Subdomain label may
|
|
151
|
+
* itself be the share token (when user didn't pick a custom subdomain).
|
|
152
|
+
*/
|
|
153
|
+
resolveByHostLabel(label) {
|
|
154
|
+
return this.getByToken(label) ?? this.getBySubdomain(label);
|
|
155
|
+
}
|
|
156
|
+
getActiveShares() {
|
|
157
|
+
const now = Date.now();
|
|
158
|
+
return [...this.shares.values()].filter((r) => !r.revoked && now < new Date(r.expiresAt).getTime());
|
|
159
|
+
}
|
|
160
|
+
getAllShares() {
|
|
161
|
+
return [...this.shares.values()].sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
162
|
+
}
|
|
163
|
+
validateAccess(record) {
|
|
164
|
+
if (record.revoked) return {
|
|
165
|
+
valid: false,
|
|
166
|
+
reason: "revoked"
|
|
167
|
+
};
|
|
168
|
+
if (Date.now() >= new Date(record.expiresAt).getTime()) return {
|
|
169
|
+
valid: false,
|
|
170
|
+
reason: "expired"
|
|
171
|
+
};
|
|
172
|
+
if (record.maxRequests !== null && record.requestCount >= record.maxRequests) return {
|
|
173
|
+
valid: false,
|
|
174
|
+
reason: "max_requests"
|
|
175
|
+
};
|
|
176
|
+
return { valid: true };
|
|
177
|
+
}
|
|
178
|
+
/** Increment counters when a request lands. Debounced persist. */
|
|
179
|
+
recordRequest(id, clientIp) {
|
|
180
|
+
const record = this.shares.get(id);
|
|
181
|
+
if (!record) return;
|
|
182
|
+
record.requestCount++;
|
|
183
|
+
const recent = record.recentClientIps ??= [];
|
|
184
|
+
if (!recent.includes(clientIp)) {
|
|
185
|
+
recent.unshift(clientIp);
|
|
186
|
+
if (recent.length > 200) recent.length = 200;
|
|
187
|
+
record.uniqueClientCount = recent.length;
|
|
188
|
+
}
|
|
189
|
+
this.scheduleDebouncedPersist();
|
|
190
|
+
}
|
|
191
|
+
setThumbnailStatus(id, status) {
|
|
192
|
+
const record = this.shares.get(id);
|
|
193
|
+
if (!record) return;
|
|
194
|
+
record.thumbnailStatus = status;
|
|
195
|
+
if (status === "ready") record.thumbnailGeneratedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
196
|
+
if (status === "failed") record.thumbnailFailedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
197
|
+
this.persistSync();
|
|
198
|
+
}
|
|
199
|
+
revoke(id) {
|
|
200
|
+
const record = this.shares.get(id);
|
|
201
|
+
if (!record) return false;
|
|
202
|
+
record.revoked = true;
|
|
203
|
+
this.persistSync();
|
|
204
|
+
if (this.onCleanup) try {
|
|
205
|
+
this.onCleanup(record);
|
|
206
|
+
} catch (err) {
|
|
207
|
+
log.warn({
|
|
208
|
+
err,
|
|
209
|
+
id
|
|
210
|
+
}, "Site share cleanup hook threw on revoke");
|
|
211
|
+
}
|
|
212
|
+
log.info({
|
|
213
|
+
id,
|
|
214
|
+
tokenPrefix: record.token.slice(0, 8)
|
|
215
|
+
}, "Site share revoked");
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
revokeMany(ids) {
|
|
219
|
+
let count = 0;
|
|
220
|
+
for (const id of ids) {
|
|
221
|
+
const record = this.shares.get(id);
|
|
222
|
+
if (record && !record.revoked) {
|
|
223
|
+
record.revoked = true;
|
|
224
|
+
count++;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (count > 0) this.persistSync();
|
|
228
|
+
return count;
|
|
229
|
+
}
|
|
230
|
+
revokeExpired() {
|
|
231
|
+
const now = Date.now();
|
|
232
|
+
let count = 0;
|
|
233
|
+
for (const record of this.shares.values()) if (!record.revoked && now >= new Date(record.expiresAt).getTime()) {
|
|
234
|
+
record.revoked = true;
|
|
235
|
+
count++;
|
|
236
|
+
}
|
|
237
|
+
if (count > 0) this.persistSync();
|
|
238
|
+
return count;
|
|
239
|
+
}
|
|
240
|
+
update(id, patch) {
|
|
241
|
+
const record = this.shares.get(id);
|
|
242
|
+
if (!record) return null;
|
|
243
|
+
if (patch.extendTtlMs !== void 0) record.expiresAt = new Date(Date.now() + patch.extendTtlMs).toISOString();
|
|
244
|
+
if (patch.maxRequests !== void 0) record.maxRequests = patch.maxRequests;
|
|
245
|
+
this.persistSync();
|
|
246
|
+
return record;
|
|
247
|
+
}
|
|
248
|
+
load() {
|
|
249
|
+
const path = resolveStorePath();
|
|
250
|
+
if (!existsSync(path)) return;
|
|
251
|
+
try {
|
|
252
|
+
const raw = readFileSync(path, "utf8");
|
|
253
|
+
const data = JSON.parse(raw);
|
|
254
|
+
if (data.version !== 1 || !Array.isArray(data.shares)) return;
|
|
255
|
+
const now = Date.now();
|
|
256
|
+
let cleaned = 0;
|
|
257
|
+
for (const record of data.shares) {
|
|
258
|
+
if (now - new Date(record.expiresAt).getTime() > EXPIRED_RETENTION_MS) {
|
|
259
|
+
cleaned++;
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
this.shares.set(record.id, record);
|
|
263
|
+
this.byToken.set(record.token, record.id);
|
|
264
|
+
if (record.subdomain) this.bySubdomain.set(record.subdomain, record.id);
|
|
265
|
+
}
|
|
266
|
+
if (cleaned > 0) {
|
|
267
|
+
log.info({ cleaned }, `Cleaned ${cleaned} expired site-share records on load`);
|
|
268
|
+
this.persistSync();
|
|
269
|
+
}
|
|
270
|
+
} catch (err) {
|
|
271
|
+
log.warn({ err }, "Failed to load site-shares.json");
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
persistSync() {
|
|
275
|
+
const path = resolveStorePath();
|
|
276
|
+
mkdirSync(resolveStateDir(), { recursive: true });
|
|
277
|
+
let records = [...this.shares.values()];
|
|
278
|
+
if (records.length > MAX_STORED) {
|
|
279
|
+
records.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
280
|
+
records = records.slice(0, MAX_STORED);
|
|
281
|
+
this.shares.clear();
|
|
282
|
+
this.byToken.clear();
|
|
283
|
+
this.bySubdomain.clear();
|
|
284
|
+
for (const r of records) {
|
|
285
|
+
this.shares.set(r.id, r);
|
|
286
|
+
this.byToken.set(r.token, r.id);
|
|
287
|
+
if (r.subdomain) this.bySubdomain.set(r.subdomain, r.id);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
writeFileSync(path, `${JSON.stringify({
|
|
291
|
+
version: 1,
|
|
292
|
+
shares: records
|
|
293
|
+
}, null, 2)}\n`, "utf8");
|
|
294
|
+
}
|
|
295
|
+
scheduleDebouncedPersist() {
|
|
296
|
+
this.dirty = true;
|
|
297
|
+
if (this.debounceTimer) return;
|
|
298
|
+
this.debounceTimer = setTimeout(() => {
|
|
299
|
+
this.debounceTimer = null;
|
|
300
|
+
if (this.dirty) {
|
|
301
|
+
this.dirty = false;
|
|
302
|
+
this.persistSync();
|
|
303
|
+
}
|
|
304
|
+
}, REQUEST_COUNT_DEBOUNCE_MS);
|
|
305
|
+
this.debounceTimer.unref?.();
|
|
306
|
+
}
|
|
307
|
+
startCleanupTimer() {
|
|
308
|
+
this.cleanupTimer = setInterval(() => {
|
|
309
|
+
const now = Date.now();
|
|
310
|
+
let removed = 0;
|
|
311
|
+
for (const [id, record] of this.shares) if (now - new Date(record.expiresAt).getTime() > EXPIRED_RETENTION_MS) {
|
|
312
|
+
this.shares.delete(id);
|
|
313
|
+
this.byToken.delete(record.token);
|
|
314
|
+
if (record.subdomain) this.bySubdomain.delete(record.subdomain);
|
|
315
|
+
if (this.onCleanup) try {
|
|
316
|
+
this.onCleanup(record);
|
|
317
|
+
} catch (err) {
|
|
318
|
+
log.warn({
|
|
319
|
+
err,
|
|
320
|
+
id
|
|
321
|
+
}, "Site share cleanup hook threw");
|
|
322
|
+
}
|
|
323
|
+
removed++;
|
|
324
|
+
}
|
|
325
|
+
if (removed > 0) {
|
|
326
|
+
this.persistSync();
|
|
327
|
+
log.debug({ removed }, `Cleaned ${removed} expired site shares`);
|
|
328
|
+
}
|
|
329
|
+
}, CLEANUP_INTERVAL_MS);
|
|
330
|
+
this.cleanupTimer.unref?.();
|
|
331
|
+
}
|
|
332
|
+
shutdown() {
|
|
333
|
+
if (this.cleanupTimer) {
|
|
334
|
+
clearInterval(this.cleanupTimer);
|
|
335
|
+
this.cleanupTimer = null;
|
|
336
|
+
}
|
|
337
|
+
if (this.debounceTimer) {
|
|
338
|
+
clearTimeout(this.debounceTimer);
|
|
339
|
+
this.debounceTimer = null;
|
|
340
|
+
}
|
|
341
|
+
if (this.dirty) {
|
|
342
|
+
this.dirty = false;
|
|
343
|
+
this.persistSync();
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
async function scanStatic(root, maxFileCount, maxRootDirSize) {
|
|
348
|
+
let fileCount = 0;
|
|
349
|
+
let totalSize = 0;
|
|
350
|
+
async function walk(dir, depth) {
|
|
351
|
+
if (depth > 16) return;
|
|
352
|
+
const dirents = await readdir(dir, { withFileTypes: true });
|
|
353
|
+
for (const dirent of dirents) {
|
|
354
|
+
if (fileCount >= maxFileCount) throw new Error(`Static site exceeds maxFileCount (${maxFileCount})`);
|
|
355
|
+
const abs = resolve(dir, dirent.name);
|
|
356
|
+
const lst = await lstat(abs);
|
|
357
|
+
if (lst.isSymbolicLink()) continue;
|
|
358
|
+
if (lst.isFile()) {
|
|
359
|
+
fileCount++;
|
|
360
|
+
totalSize += lst.size;
|
|
361
|
+
if (totalSize > maxRootDirSize) {
|
|
362
|
+
const mb = (maxRootDirSize / 1048576).toFixed(0);
|
|
363
|
+
throw new Error(`Static site exceeds maxRootDirSize (${mb} MB)`);
|
|
364
|
+
}
|
|
365
|
+
} else if (lst.isDirectory()) await walk(abs, depth + 1);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
await walk(root, 0);
|
|
369
|
+
return {
|
|
370
|
+
fileCount,
|
|
371
|
+
totalSize
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
function validateUpstreamUrl(raw, cfg) {
|
|
375
|
+
let parsed;
|
|
376
|
+
try {
|
|
377
|
+
parsed = new URL(raw);
|
|
378
|
+
} catch {
|
|
379
|
+
throw new Error("upstreamUrl must be a valid URL");
|
|
380
|
+
}
|
|
381
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new Error("upstreamUrl must use http or https");
|
|
382
|
+
const host = parsed.hostname.toLowerCase();
|
|
383
|
+
if (!cfg.allowedUpstreamHosts.map((h) => h.toLowerCase()).includes(host)) throw new Error(`upstream host '${host}' is not in allowedUpstreamHosts`);
|
|
384
|
+
const port = parsed.port ? Number(parsed.port) : parsed.protocol === "https:" ? 443 : 80;
|
|
385
|
+
if (!cfg.allowedUpstreamPorts.includes(port)) throw new Error(`upstream port ${port} is not in allowedUpstreamPorts`);
|
|
386
|
+
return `${parsed.protocol}//${parsed.host}`;
|
|
387
|
+
}
|
|
388
|
+
let singleton = null;
|
|
389
|
+
function getSiteShareStore(config) {
|
|
390
|
+
if (!singleton) singleton = new SiteShareStore(config);
|
|
391
|
+
return singleton;
|
|
392
|
+
}
|
|
393
|
+
function resetSiteShareStoreForTests() {
|
|
394
|
+
singleton?.shutdown();
|
|
395
|
+
singleton = null;
|
|
396
|
+
}
|
|
397
|
+
//#endregion
|
|
398
|
+
export { SiteShareStore, getSiteShareStore, resetSiteShareStoreForTests };
|
|
399
|
+
|
|
400
|
+
//# sourceMappingURL=site-share-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"site-share-store.js","names":["resolvePath","relPathPosix"],"sources":["../../../src/share/site-share-store.ts"],"sourcesContent":["import { randomBytes, randomUUID } from 'node:crypto';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { stat, readdir, lstat, realpath } from 'node:fs/promises';\nimport { join, relative as relPathPosix, resolve as resolvePath } from 'node:path';\n\nimport { resolveStateDir } from '../config/paths.js';\nimport { isPathUnderWorkspace } from '../gateway/workspace-editor-path.js';\nimport { createLogger } from '../utils/logger.js';\nimport {\n SITE_SHARE_CONFIG_DEFAULTS,\n type CreateSiteShareParams,\n type SiteShareConfig,\n type SiteShareRecord,\n type SiteShareStoreData,\n type SiteSource,\n} from './site-share-types.js';\n\nconst log = createLogger('SiteShareStore');\n\nconst SITE_SHARES_FILE = 'site-shares.json';\nconst CLEANUP_INTERVAL_MS = 10 * 60_000;\nconst EXPIRED_RETENTION_MS = 24 * 60 * 60_000;\nconst MAX_STORED = 200;\nconst REQUEST_COUNT_DEBOUNCE_MS = 5_000;\nconst SUBDOMAIN_PATTERN = /^[a-z0-9]([a-z0-9-]{0,30}[a-z0-9])?$/;\n\nfunction resolveStorePath(): string {\n return join(resolveStateDir(), SITE_SHARES_FILE);\n}\n\nexport class SiteShareStore {\n private shares = new Map<string, SiteShareRecord>();\n private byToken = new Map<string, string>();\n private bySubdomain = new Map<string, string>();\n private config: SiteShareConfig;\n private dirty = false;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private cleanupTimer: ReturnType<typeof setInterval> | null = null;\n /** Optional cleanup hook invoked when a record is dropped (e.g. delete staging dir). */\n private onCleanup: ((record: SiteShareRecord) => void) | null = null;\n\n constructor(config?: Partial<SiteShareConfig>) {\n this.config = { ...SITE_SHARE_CONFIG_DEFAULTS, ...config };\n this.load();\n this.startCleanupTimer();\n }\n\n updateConfig(config: Partial<SiteShareConfig>): void {\n this.config = { ...this.config, ...config };\n }\n\n setCleanupHook(hook: (record: SiteShareRecord) => void): void {\n this.onCleanup = hook;\n }\n\n getConfig(): SiteShareConfig {\n return JSON.parse(JSON.stringify(this.config)) as SiteShareConfig;\n }\n\n // ── CRUD ────────────────────────────────────────────────────────────────────\n\n async create(\n params: CreateSiteShareParams & {\n workspaceRoot: string | null;\n gatewayTokenHash: string;\n },\n ): Promise<SiteShareRecord> {\n if (!this.config.enabled) throw new Error('Site sharing is disabled');\n\n const active = this.getActiveShares();\n if (active.length >= this.config.maxActiveSites) {\n throw new Error(`Maximum active site shares reached (${this.config.maxActiveSites})`);\n }\n\n const ttlMs = params.ttlMs ?? this.config.defaultTtlMs;\n if (ttlMs < 60_000 || ttlMs > this.config.maxTtlMs) {\n throw new Error(`TTL must be between 60s and ${this.config.maxTtlMs / 1000}s`);\n }\n\n const source = await this.buildSource(params);\n\n const subdomain = this.resolveSubdomain(params.subdomain);\n const id = randomUUID();\n const token = randomBytes(24).toString('base64url');\n const now = new Date();\n const record: SiteShareRecord = {\n id,\n token,\n subdomain,\n source,\n createdAt: now.toISOString(),\n expiresAt: new Date(now.getTime() + ttlMs).toISOString(),\n revoked: false,\n description: params.description,\n createdByTokenHash: params.gatewayTokenHash,\n requestCount: 0,\n uniqueClientCount: 0,\n maxRequests: params.maxRequests ?? null,\n };\n\n this.shares.set(id, record);\n this.byToken.set(token, id);\n if (subdomain) this.bySubdomain.set(subdomain, id);\n this.persistSync();\n\n log.info(\n { id, tokenPrefix: token.slice(0, 8), subdomain, kind: source.kind },\n `Site share created (${source.kind})`,\n );\n\n return record;\n }\n\n private async buildSource(params: CreateSiteShareParams & { workspaceRoot: string | null }): Promise<SiteSource> {\n if (params.kind === 'static') {\n if (!this.config.static.enabled) throw new Error('Static site sharing is disabled');\n if (!params.workspaceRoot) throw new Error('workspaceRoot required for static site shares');\n if (!params.path) throw new Error('path required for static site shares');\n const relPath = params.path.trim().replace(/\\\\/g, '/').replace(/^\\/+/, '');\n if (!relPath) throw new Error('Empty path');\n if (relPath.includes('..') || relPath.includes('\\0')) throw new Error('Invalid path');\n const absRoot = resolvePath(params.workspaceRoot, relPath);\n const relToRoot = relPathPosix(resolvePath(params.workspaceRoot), absRoot);\n if (relToRoot.startsWith('..') || relToRoot.split(/[/\\\\]/).includes('..')) {\n throw new Error('Path is outside workspace');\n }\n const real = await realpath(absRoot);\n if (!isPathUnderWorkspace(params.workspaceRoot, real)) {\n throw new Error('Path target is outside workspace');\n }\n const stats = await stat(absRoot);\n if (!stats.isDirectory()) throw new Error('Static source must be a directory');\n const summary = await scanStatic(absRoot, this.config.static.maxFileCount, this.config.static.maxRootDirSize);\n log.debug({ ...summary, rootDir: absRoot }, 'Static site scanned');\n return {\n kind: 'static',\n rootDir: absRoot,\n workspaceRoot: params.workspaceRoot,\n workspaceRelativePath: relPath,\n spaFallback: params.spaFallback ?? true,\n rewriteMode: params.rewriteMode ?? (this.config.static.rewriteEnabledByDefault ? 'html-css' : 'none'),\n };\n }\n\n if (params.kind === 'proxy') {\n if (!this.config.proxy.enabled) throw new Error('Proxy site sharing is disabled');\n if (!params.upstreamUrl) throw new Error('upstreamUrl required for proxy shares');\n const validated = validateUpstreamUrl(params.upstreamUrl, this.config.proxy);\n return {\n kind: 'proxy',\n upstreamUrl: validated,\n rewriteSetCookiePath: this.config.proxy.rewriteSetCookiePath,\n forwardWebSocket: params.forwardWebSocket ?? this.config.proxy.forwardWebSocket,\n forwardedHeaders: 'minimal',\n };\n }\n\n throw new Error(`Unsupported site share kind: ${params.kind}`);\n }\n\n private resolveSubdomain(preferred?: string): string | null {\n if (!preferred) return null;\n const lower = preferred.trim().toLowerCase();\n if (!SUBDOMAIN_PATTERN.test(lower)) {\n throw new Error('Subdomain must match /^[a-z0-9]([a-z0-9-]{0,30}[a-z0-9])?$/');\n }\n if (this.bySubdomain.has(lower)) {\n throw new Error(`Subdomain '${lower}' already in use`);\n }\n return lower;\n }\n\n getById(id: string): SiteShareRecord | null {\n return this.shares.get(id) ?? null;\n }\n\n getByToken(token: string): SiteShareRecord | null {\n const id = this.byToken.get(token);\n return id ? this.shares.get(id) ?? null : null;\n }\n\n getBySubdomain(subdomain: string): SiteShareRecord | null {\n const id = this.bySubdomain.get(subdomain.toLowerCase());\n return id ? this.shares.get(id) ?? null : null;\n }\n\n /**\n * Look up a record by token first, then by subdomain. Subdomain label may\n * itself be the share token (when user didn't pick a custom subdomain).\n */\n resolveByHostLabel(label: string): SiteShareRecord | null {\n return this.getByToken(label) ?? this.getBySubdomain(label);\n }\n\n getActiveShares(): SiteShareRecord[] {\n const now = Date.now();\n return [...this.shares.values()].filter(\n (r) => !r.revoked && now < new Date(r.expiresAt).getTime(),\n );\n }\n\n getAllShares(): SiteShareRecord[] {\n return [...this.shares.values()].sort(\n (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(),\n );\n }\n\n validateAccess(record: SiteShareRecord): { valid: boolean; reason?: string } {\n if (record.revoked) return { valid: false, reason: 'revoked' };\n if (Date.now() >= new Date(record.expiresAt).getTime()) return { valid: false, reason: 'expired' };\n if (record.maxRequests !== null && record.requestCount >= record.maxRequests) {\n return { valid: false, reason: 'max_requests' };\n }\n return { valid: true };\n }\n\n /** Increment counters when a request lands. Debounced persist. */\n recordRequest(id: string, clientIp: string): void {\n const record = this.shares.get(id);\n if (!record) return;\n record.requestCount++;\n const recent = (record.recentClientIps ??= []);\n if (!recent.includes(clientIp)) {\n recent.unshift(clientIp);\n if (recent.length > 200) recent.length = 200;\n record.uniqueClientCount = recent.length;\n }\n this.scheduleDebouncedPersist();\n }\n\n setThumbnailStatus(id: string, status: 'pending' | 'ready' | 'failed'): void {\n const record = this.shares.get(id);\n if (!record) return;\n record.thumbnailStatus = status;\n if (status === 'ready') record.thumbnailGeneratedAt = new Date().toISOString();\n if (status === 'failed') record.thumbnailFailedAt = new Date().toISOString();\n this.persistSync();\n }\n\n revoke(id: string): boolean {\n const record = this.shares.get(id);\n if (!record) return false;\n record.revoked = true;\n this.persistSync();\n if (this.onCleanup) {\n try {\n this.onCleanup(record);\n } catch (err) {\n log.warn({ err, id }, 'Site share cleanup hook threw on revoke');\n }\n }\n log.info({ id, tokenPrefix: record.token.slice(0, 8) }, 'Site share revoked');\n return true;\n }\n\n revokeMany(ids: string[]): number {\n let count = 0;\n for (const id of ids) {\n const record = this.shares.get(id);\n if (record && !record.revoked) {\n record.revoked = true;\n count++;\n }\n }\n if (count > 0) this.persistSync();\n return count;\n }\n\n revokeExpired(): number {\n const now = Date.now();\n let count = 0;\n for (const record of this.shares.values()) {\n if (!record.revoked && now >= new Date(record.expiresAt).getTime()) {\n record.revoked = true;\n count++;\n }\n }\n if (count > 0) this.persistSync();\n return count;\n }\n\n update(id: string, patch: { extendTtlMs?: number; maxRequests?: number | null }): SiteShareRecord | null {\n const record = this.shares.get(id);\n if (!record) return null;\n if (patch.extendTtlMs !== undefined) {\n record.expiresAt = new Date(Date.now() + patch.extendTtlMs).toISOString();\n }\n if (patch.maxRequests !== undefined) {\n record.maxRequests = patch.maxRequests;\n }\n this.persistSync();\n return record;\n }\n\n // ── Persistence ─────────────────────────────────────────────────────────────\n\n private load(): void {\n const path = resolveStorePath();\n if (!existsSync(path)) return;\n try {\n const raw = readFileSync(path, 'utf8');\n const data = JSON.parse(raw) as SiteShareStoreData;\n if (data.version !== 1 || !Array.isArray(data.shares)) return;\n const now = Date.now();\n let cleaned = 0;\n for (const record of data.shares) {\n const expiredMs = now - new Date(record.expiresAt).getTime();\n if (expiredMs > EXPIRED_RETENTION_MS) {\n cleaned++;\n continue;\n }\n this.shares.set(record.id, record);\n this.byToken.set(record.token, record.id);\n if (record.subdomain) this.bySubdomain.set(record.subdomain, record.id);\n }\n if (cleaned > 0) {\n log.info({ cleaned }, `Cleaned ${cleaned} expired site-share records on load`);\n this.persistSync();\n }\n } catch (err) {\n log.warn({ err }, 'Failed to load site-shares.json');\n }\n }\n\n private persistSync(): void {\n const path = resolveStorePath();\n mkdirSync(resolveStateDir(), { recursive: true });\n let records = [...this.shares.values()];\n if (records.length > MAX_STORED) {\n records.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());\n records = records.slice(0, MAX_STORED);\n this.shares.clear();\n this.byToken.clear();\n this.bySubdomain.clear();\n for (const r of records) {\n this.shares.set(r.id, r);\n this.byToken.set(r.token, r.id);\n if (r.subdomain) this.bySubdomain.set(r.subdomain, r.id);\n }\n }\n const data: SiteShareStoreData = { version: 1, shares: records };\n writeFileSync(path, `${JSON.stringify(data, null, 2)}\\n`, 'utf8');\n }\n\n private scheduleDebouncedPersist(): void {\n this.dirty = true;\n if (this.debounceTimer) return;\n this.debounceTimer = setTimeout(() => {\n this.debounceTimer = null;\n if (this.dirty) {\n this.dirty = false;\n this.persistSync();\n }\n }, REQUEST_COUNT_DEBOUNCE_MS);\n this.debounceTimer.unref?.();\n }\n\n private startCleanupTimer(): void {\n this.cleanupTimer = setInterval(() => {\n const now = Date.now();\n let removed = 0;\n for (const [id, record] of this.shares) {\n const expiredMs = now - new Date(record.expiresAt).getTime();\n if (expiredMs > EXPIRED_RETENTION_MS) {\n this.shares.delete(id);\n this.byToken.delete(record.token);\n if (record.subdomain) this.bySubdomain.delete(record.subdomain);\n if (this.onCleanup) {\n try {\n this.onCleanup(record);\n } catch (err) {\n log.warn({ err, id }, 'Site share cleanup hook threw');\n }\n }\n removed++;\n }\n }\n if (removed > 0) {\n this.persistSync();\n log.debug({ removed }, `Cleaned ${removed} expired site shares`);\n }\n }, CLEANUP_INTERVAL_MS);\n this.cleanupTimer.unref?.();\n }\n\n shutdown(): void {\n if (this.cleanupTimer) {\n clearInterval(this.cleanupTimer);\n this.cleanupTimer = null;\n }\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n if (this.dirty) {\n this.dirty = false;\n this.persistSync();\n }\n }\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nasync function scanStatic(\n root: string,\n maxFileCount: number,\n maxRootDirSize: number,\n): Promise<{ fileCount: number; totalSize: number }> {\n let fileCount = 0;\n let totalSize = 0;\n\n async function walk(dir: string, depth: number): Promise<void> {\n if (depth > 16) return;\n const dirents = await readdir(dir, { withFileTypes: true });\n for (const dirent of dirents) {\n if (fileCount >= maxFileCount) throw new Error(`Static site exceeds maxFileCount (${maxFileCount})`);\n const abs = resolvePath(dir, dirent.name);\n const lst = await lstat(abs);\n if (lst.isSymbolicLink()) continue; // never follow symlinks in a public static root\n if (lst.isFile()) {\n fileCount++;\n totalSize += lst.size;\n if (totalSize > maxRootDirSize) {\n const mb = (maxRootDirSize / 1_048_576).toFixed(0);\n throw new Error(`Static site exceeds maxRootDirSize (${mb} MB)`);\n }\n } else if (lst.isDirectory()) {\n await walk(abs, depth + 1);\n }\n }\n }\n\n await walk(root, 0);\n return { fileCount, totalSize };\n}\n\nfunction validateUpstreamUrl(raw: string, cfg: SiteShareConfig['proxy']): string {\n let parsed: URL;\n try {\n parsed = new URL(raw);\n } catch {\n throw new Error('upstreamUrl must be a valid URL');\n }\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n throw new Error('upstreamUrl must use http or https');\n }\n const host = parsed.hostname.toLowerCase();\n if (!cfg.allowedUpstreamHosts.map((h) => h.toLowerCase()).includes(host)) {\n throw new Error(`upstream host '${host}' is not in allowedUpstreamHosts`);\n }\n const port = parsed.port ? Number(parsed.port) : parsed.protocol === 'https:' ? 443 : 80;\n if (!cfg.allowedUpstreamPorts.includes(port)) {\n throw new Error(`upstream port ${port} is not in allowedUpstreamPorts`);\n }\n // Strip path / search / hash — proxy uses base only\n return `${parsed.protocol}//${parsed.host}`;\n}\n\n// ── Singleton ─────────────────────────────────────────────────────────────────\n\nlet singleton: SiteShareStore | null = null;\n\nexport function getSiteShareStore(config?: Partial<SiteShareConfig>): SiteShareStore {\n if (!singleton) singleton = new SiteShareStore(config);\n return singleton;\n}\n\nexport function resetSiteShareStoreForTests(): void {\n singleton?.shutdown();\n singleton = null;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
export type SiteSourceKind = 'static' | 'proxy';
|
|
2
|
+
export interface SiteStaticSource {
|
|
3
|
+
kind: 'static';
|
|
4
|
+
/** Absolute filesystem root of the static site directory. */
|
|
5
|
+
rootDir: string;
|
|
6
|
+
/** Workspace root captured at creation for re-validation. */
|
|
7
|
+
workspaceRoot: string;
|
|
8
|
+
/** Workspace-relative POSIX path for display. */
|
|
9
|
+
workspaceRelativePath: string;
|
|
10
|
+
/** Send index.html (status 200) for unmatched paths under root (SPA). */
|
|
11
|
+
spaFallback: boolean;
|
|
12
|
+
/** Whether HTML/CSS absolute-path rewriting is applied. */
|
|
13
|
+
rewriteMode: 'none' | 'html-only' | 'html-css';
|
|
14
|
+
/** Regex string matched against URL path; matches get an immutable cache. */
|
|
15
|
+
immutableAssetsRegex?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface SiteProxySource {
|
|
18
|
+
kind: 'proxy';
|
|
19
|
+
/** Upstream base URL (e.g. http://127.0.0.1:3000). */
|
|
20
|
+
upstreamUrl: string;
|
|
21
|
+
/** Forward Set-Cookie path attribute (subpath mode only). */
|
|
22
|
+
rewriteSetCookiePath: boolean;
|
|
23
|
+
/** Whether to handle WebSocket upgrades. */
|
|
24
|
+
forwardWebSocket: boolean;
|
|
25
|
+
/** Per-request forwarded headers strategy. */
|
|
26
|
+
forwardedHeaders: 'minimal' | 'full';
|
|
27
|
+
}
|
|
28
|
+
export type SiteSource = SiteStaticSource | SiteProxySource;
|
|
29
|
+
export interface SiteShareRecord {
|
|
30
|
+
id: string;
|
|
31
|
+
/** Token used both as the URL subdomain label (subdomain mode) and as `/site/:token/` subpath fallback. */
|
|
32
|
+
token: string;
|
|
33
|
+
/** Optional creator-chosen subdomain (must be unique, only [a-z0-9-]). */
|
|
34
|
+
subdomain: string | null;
|
|
35
|
+
source: SiteSource;
|
|
36
|
+
createdAt: string;
|
|
37
|
+
expiresAt: string;
|
|
38
|
+
revoked: boolean;
|
|
39
|
+
description?: string;
|
|
40
|
+
createdByTokenHash: string;
|
|
41
|
+
requestCount: number;
|
|
42
|
+
uniqueClientCount: number;
|
|
43
|
+
/** Hard cap on total requests served (null = unlimited). */
|
|
44
|
+
maxRequests: number | null;
|
|
45
|
+
/** Cached client IPs that have counted toward uniqueClientCount (last 200). */
|
|
46
|
+
recentClientIps?: string[];
|
|
47
|
+
/** Thumbnail generation status (parallels ShareRecord). */
|
|
48
|
+
thumbnailStatus?: 'pending' | 'ready' | 'failed';
|
|
49
|
+
thumbnailGeneratedAt?: string;
|
|
50
|
+
thumbnailFailedAt?: string;
|
|
51
|
+
}
|
|
52
|
+
export interface SiteShareStoreData {
|
|
53
|
+
version: 1;
|
|
54
|
+
shares: SiteShareRecord[];
|
|
55
|
+
}
|
|
56
|
+
export interface CreateSiteShareParams {
|
|
57
|
+
/** 'static' uses workspace path; 'proxy' uses upstreamUrl. */
|
|
58
|
+
kind: SiteSourceKind;
|
|
59
|
+
/** Static: workspace-relative path of build output. */
|
|
60
|
+
path?: string;
|
|
61
|
+
/** Proxy: full upstream URL (must be loopback or whitelisted). */
|
|
62
|
+
upstreamUrl?: string;
|
|
63
|
+
/** Default 6h. */
|
|
64
|
+
ttlMs?: number;
|
|
65
|
+
/** Optional friendly description. */
|
|
66
|
+
description?: string;
|
|
67
|
+
/** Optional preferred subdomain (will fall back to token if taken). */
|
|
68
|
+
subdomain?: string;
|
|
69
|
+
/** Optional max request count. */
|
|
70
|
+
maxRequests?: number | null;
|
|
71
|
+
/** Static: SPA fallback (default true for static). */
|
|
72
|
+
spaFallback?: boolean;
|
|
73
|
+
/** Static: rewrite mode (default 'none'). */
|
|
74
|
+
rewriteMode?: 'none' | 'html-only' | 'html-css';
|
|
75
|
+
/** Proxy: forward WS (default true if enabled in config). */
|
|
76
|
+
forwardWebSocket?: boolean;
|
|
77
|
+
sessionKey?: string;
|
|
78
|
+
agentId?: string;
|
|
79
|
+
}
|
|
80
|
+
export interface SiteShareConfig {
|
|
81
|
+
enabled: boolean;
|
|
82
|
+
publicHostSuffix: string;
|
|
83
|
+
defaultTtlMs: number;
|
|
84
|
+
maxTtlMs: number;
|
|
85
|
+
maxActiveSites: number;
|
|
86
|
+
static: {
|
|
87
|
+
enabled: boolean;
|
|
88
|
+
maxRootDirSize: number;
|
|
89
|
+
maxFileCount: number;
|
|
90
|
+
rewriteEnabledByDefault: boolean;
|
|
91
|
+
};
|
|
92
|
+
proxy: {
|
|
93
|
+
enabled: boolean;
|
|
94
|
+
allowedUpstreamHosts: string[];
|
|
95
|
+
allowedUpstreamPorts: number[];
|
|
96
|
+
forwardWebSocket: boolean;
|
|
97
|
+
bodySizeLimit: number;
|
|
98
|
+
requestTimeoutMs: number;
|
|
99
|
+
wsIdleTimeoutMs: number;
|
|
100
|
+
rewriteSetCookiePath: boolean;
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
export declare const SITE_SHARE_CONFIG_DEFAULTS: SiteShareConfig;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//#region src/share/site-share-types.ts
|
|
2
|
+
const SITE_SHARE_CONFIG_DEFAULTS = {
|
|
3
|
+
enabled: true,
|
|
4
|
+
publicHostSuffix: "share.xopc.ai",
|
|
5
|
+
defaultTtlMs: 216e5,
|
|
6
|
+
maxTtlMs: 6048e5,
|
|
7
|
+
maxActiveSites: 5,
|
|
8
|
+
static: {
|
|
9
|
+
enabled: true,
|
|
10
|
+
maxRootDirSize: 524288e3,
|
|
11
|
+
maxFileCount: 1e4,
|
|
12
|
+
rewriteEnabledByDefault: false
|
|
13
|
+
},
|
|
14
|
+
proxy: {
|
|
15
|
+
enabled: true,
|
|
16
|
+
allowedUpstreamHosts: [
|
|
17
|
+
"127.0.0.1",
|
|
18
|
+
"localhost",
|
|
19
|
+
"::1"
|
|
20
|
+
],
|
|
21
|
+
allowedUpstreamPorts: [
|
|
22
|
+
3e3,
|
|
23
|
+
3001,
|
|
24
|
+
4321,
|
|
25
|
+
5173,
|
|
26
|
+
8e3,
|
|
27
|
+
8080,
|
|
28
|
+
8888,
|
|
29
|
+
9e3
|
|
30
|
+
],
|
|
31
|
+
forwardWebSocket: true,
|
|
32
|
+
bodySizeLimit: 52428800,
|
|
33
|
+
requestTimeoutMs: 3e4,
|
|
34
|
+
wsIdleTimeoutMs: 3e5,
|
|
35
|
+
rewriteSetCookiePath: true
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
//#endregion
|
|
39
|
+
export { SITE_SHARE_CONFIG_DEFAULTS };
|
|
40
|
+
|
|
41
|
+
//# sourceMappingURL=site-share-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"site-share-types.js","names":[],"sources":["../../../src/share/site-share-types.ts"],"sourcesContent":["export type SiteSourceKind = 'static' | 'proxy';\n\nexport interface SiteStaticSource {\n kind: 'static';\n /** Absolute filesystem root of the static site directory. */\n rootDir: string;\n /** Workspace root captured at creation for re-validation. */\n workspaceRoot: string;\n /** Workspace-relative POSIX path for display. */\n workspaceRelativePath: string;\n /** Send index.html (status 200) for unmatched paths under root (SPA). */\n spaFallback: boolean;\n /** Whether HTML/CSS absolute-path rewriting is applied. */\n rewriteMode: 'none' | 'html-only' | 'html-css';\n /** Regex string matched against URL path; matches get an immutable cache. */\n immutableAssetsRegex?: string;\n}\n\nexport interface SiteProxySource {\n kind: 'proxy';\n /** Upstream base URL (e.g. http://127.0.0.1:3000). */\n upstreamUrl: string;\n /** Forward Set-Cookie path attribute (subpath mode only). */\n rewriteSetCookiePath: boolean;\n /** Whether to handle WebSocket upgrades. */\n forwardWebSocket: boolean;\n /** Per-request forwarded headers strategy. */\n forwardedHeaders: 'minimal' | 'full';\n}\n\nexport type SiteSource = SiteStaticSource | SiteProxySource;\n\nexport interface SiteShareRecord {\n id: string;\n /** Token used both as the URL subdomain label (subdomain mode) and as `/site/:token/` subpath fallback. */\n token: string;\n /** Optional creator-chosen subdomain (must be unique, only [a-z0-9-]). */\n subdomain: string | null;\n source: SiteSource;\n createdAt: string;\n expiresAt: string;\n revoked: boolean;\n description?: string;\n createdByTokenHash: string;\n requestCount: number;\n uniqueClientCount: number;\n /** Hard cap on total requests served (null = unlimited). */\n maxRequests: number | null;\n /** Cached client IPs that have counted toward uniqueClientCount (last 200). */\n recentClientIps?: string[];\n /** Thumbnail generation status (parallels ShareRecord). */\n thumbnailStatus?: 'pending' | 'ready' | 'failed';\n thumbnailGeneratedAt?: string;\n thumbnailFailedAt?: string;\n}\n\nexport interface SiteShareStoreData {\n version: 1;\n shares: SiteShareRecord[];\n}\n\nexport interface CreateSiteShareParams {\n /** 'static' uses workspace path; 'proxy' uses upstreamUrl. */\n kind: SiteSourceKind;\n /** Static: workspace-relative path of build output. */\n path?: string;\n /** Proxy: full upstream URL (must be loopback or whitelisted). */\n upstreamUrl?: string;\n /** Default 6h. */\n ttlMs?: number;\n /** Optional friendly description. */\n description?: string;\n /** Optional preferred subdomain (will fall back to token if taken). */\n subdomain?: string;\n /** Optional max request count. */\n maxRequests?: number | null;\n /** Static: SPA fallback (default true for static). */\n spaFallback?: boolean;\n /** Static: rewrite mode (default 'none'). */\n rewriteMode?: 'none' | 'html-only' | 'html-css';\n /** Proxy: forward WS (default true if enabled in config). */\n forwardWebSocket?: boolean;\n sessionKey?: string;\n agentId?: string;\n}\n\nexport interface SiteShareConfig {\n enabled: boolean;\n publicHostSuffix: string;\n defaultTtlMs: number;\n maxTtlMs: number;\n maxActiveSites: number;\n static: {\n enabled: boolean;\n maxRootDirSize: number;\n maxFileCount: number;\n rewriteEnabledByDefault: boolean;\n };\n proxy: {\n enabled: boolean;\n allowedUpstreamHosts: string[];\n allowedUpstreamPorts: number[];\n forwardWebSocket: boolean;\n bodySizeLimit: number;\n requestTimeoutMs: number;\n wsIdleTimeoutMs: number;\n rewriteSetCookiePath: boolean;\n };\n}\n\nexport const SITE_SHARE_CONFIG_DEFAULTS: SiteShareConfig = {\n enabled: true,\n publicHostSuffix: 'share.xopc.ai',\n defaultTtlMs: 21_600_000,\n maxTtlMs: 604_800_000,\n maxActiveSites: 5,\n static: {\n enabled: true,\n maxRootDirSize: 524_288_000,\n maxFileCount: 10_000,\n rewriteEnabledByDefault: false,\n },\n proxy: {\n enabled: true,\n allowedUpstreamHosts: ['127.0.0.1', 'localhost', '::1'],\n allowedUpstreamPorts: [3000, 3001, 4321, 5173, 8000, 8080, 8888, 9000],\n forwardWebSocket: true,\n bodySizeLimit: 52_428_800,\n requestTimeoutMs: 30_000,\n wsIdleTimeoutMs: 300_000,\n rewriteSetCookiePath: true,\n },\n};\n"],"mappings":";AA8GA,MAAa,6BAA8C;CACzD,SAAS;CACT,kBAAkB;CAClB,cAAc;CACd,UAAU;CACV,gBAAgB;CAChB,QAAQ;EACN,SAAS;EACT,gBAAgB;EAChB,cAAc;EACd,yBAAyB;EAC1B;CACD,OAAO;EACL,SAAS;EACT,sBAAsB;GAAC;GAAa;GAAa;GAAM;EACvD,sBAAsB;GAAC;GAAM;GAAM;GAAM;GAAM;GAAM;GAAM;GAAM;GAAK;EACtE,kBAAkB;EAClB,eAAe;EACf,kBAAkB;EAClB,iBAAiB;EACjB,sBAAsB;EACvB;CACF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SiteShareRecord } from './site-share-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Serve a request for a static site share.
|
|
4
|
+
*
|
|
5
|
+
* `urlPath` is the in-site path (already stripped of any subdomain/subpath base).
|
|
6
|
+
* `basePrefix` is the path prefix that the share is mounted under, used by the
|
|
7
|
+
* HTML rewriter to make root-relative URLs (`/assets/x`) point back into the
|
|
8
|
+
* share. Pass '' when serving on a subdomain (root mode).
|
|
9
|
+
*/
|
|
10
|
+
export declare function serveStaticSiteRequest(record: SiteShareRecord, urlPath: string, basePrefix: string): Promise<Response>;
|