hivehq 2.0.0
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/CHANGELOG.md +592 -0
- package/LICENSE +208 -0
- package/LICENSE.BSL +101 -0
- package/NOTICE +15 -0
- package/README.en.md +451 -0
- package/README.md +329 -0
- package/SECURITY.md +59 -0
- package/TRADEMARK.md +41 -0
- package/assets/hive-hero.png +0 -0
- package/assets/hive-team-view.png +0 -0
- package/assets/logo.png +0 -0
- package/assets/qq-group.jpg +0 -0
- package/dist/bin/team +7 -0
- package/dist/bin/team.cmd +3 -0
- package/dist/src/cli/hive-remote.d.ts +46 -0
- package/dist/src/cli/hive-remote.js +257 -0
- package/dist/src/cli/hive-update.d.ts +75 -0
- package/dist/src/cli/hive-update.js +215 -0
- package/dist/src/cli/hive.d.ts +78 -0
- package/dist/src/cli/hive.js +336 -0
- package/dist/src/cli/team.d.ts +38 -0
- package/dist/src/cli/team.js +762 -0
- package/dist/src/server/agent-command-resolver.d.ts +17 -0
- package/dist/src/server/agent-command-resolver.js +106 -0
- package/dist/src/server/agent-exit-classification.d.ts +6 -0
- package/dist/src/server/agent-exit-classification.js +6 -0
- package/dist/src/server/agent-launch-cache.d.ts +20 -0
- package/dist/src/server/agent-launch-cache.js +75 -0
- package/dist/src/server/agent-launch-resolver.d.ts +4 -0
- package/dist/src/server/agent-launch-resolver.js +38 -0
- package/dist/src/server/agent-manager-support.d.ts +36 -0
- package/dist/src/server/agent-manager-support.js +318 -0
- package/dist/src/server/agent-manager.d.ts +54 -0
- package/dist/src/server/agent-manager.js +104 -0
- package/dist/src/server/agent-run-bootstrap.d.ts +83 -0
- package/dist/src/server/agent-run-bootstrap.js +101 -0
- package/dist/src/server/agent-run-exit-handler.d.ts +8 -0
- package/dist/src/server/agent-run-exit-handler.js +32 -0
- package/dist/src/server/agent-run-start-context.d.ts +24 -0
- package/dist/src/server/agent-run-start-context.js +1 -0
- package/dist/src/server/agent-run-starter.d.ts +30 -0
- package/dist/src/server/agent-run-starter.js +155 -0
- package/dist/src/server/agent-run-store.d.ts +51 -0
- package/dist/src/server/agent-run-store.js +137 -0
- package/dist/src/server/agent-run-sync.d.ts +10 -0
- package/dist/src/server/agent-run-sync.js +32 -0
- package/dist/src/server/agent-runtime-active-run.d.ts +3 -0
- package/dist/src/server/agent-runtime-active-run.js +10 -0
- package/dist/src/server/agent-runtime-close.d.ts +5 -0
- package/dist/src/server/agent-runtime-close.js +36 -0
- package/dist/src/server/agent-runtime-contract.d.ts +48 -0
- package/dist/src/server/agent-runtime-contract.js +1 -0
- package/dist/src/server/agent-runtime-flow-adapter.d.ts +10 -0
- package/dist/src/server/agent-runtime-flow-adapter.js +14 -0
- package/dist/src/server/agent-runtime-list-runs.d.ts +3 -0
- package/dist/src/server/agent-runtime-list-runs.js +18 -0
- package/dist/src/server/agent-runtime-ports.d.ts +22 -0
- package/dist/src/server/agent-runtime-ports.js +1 -0
- package/dist/src/server/agent-runtime-stop-run.d.ts +4 -0
- package/dist/src/server/agent-runtime-stop-run.js +19 -0
- package/dist/src/server/agent-runtime-types.d.ts +5 -0
- package/dist/src/server/agent-runtime-types.js +1 -0
- package/dist/src/server/agent-runtime.d.ts +10 -0
- package/dist/src/server/agent-runtime.js +143 -0
- package/dist/src/server/agent-session-store.d.ts +7 -0
- package/dist/src/server/agent-session-store.js +45 -0
- package/dist/src/server/agent-startup-instructions.d.ts +20 -0
- package/dist/src/server/agent-startup-instructions.js +36 -0
- package/dist/src/server/agent-stdin-dispatcher.d.ts +52 -0
- package/dist/src/server/agent-stdin-dispatcher.js +224 -0
- package/dist/src/server/agent-tokens.d.ts +15 -0
- package/dist/src/server/agent-tokens.js +25 -0
- package/dist/src/server/app-state-store.d.ts +10 -0
- package/dist/src/server/app-state-store.js +12 -0
- package/dist/src/server/app.d.ts +20 -0
- package/dist/src/server/app.js +211 -0
- package/dist/src/server/claude-command-defaults.d.ts +1 -0
- package/dist/src/server/claude-command-defaults.js +5 -0
- package/dist/src/server/claude-session-coordinator.d.ts +10 -0
- package/dist/src/server/claude-session-coordinator.js +68 -0
- package/dist/src/server/claude-session-support.d.ts +1 -0
- package/dist/src/server/claude-session-support.js +1 -0
- package/dist/src/server/command-preset-defaults.d.ts +11 -0
- package/dist/src/server/command-preset-defaults.js +63 -0
- package/dist/src/server/command-preset-store.d.ts +49 -0
- package/dist/src/server/command-preset-store.js +83 -0
- package/dist/src/server/cron-util.d.ts +7 -0
- package/dist/src/server/cron-util.js +19 -0
- package/dist/src/server/dispatch-ledger-serializer.d.ts +15 -0
- package/dist/src/server/dispatch-ledger-serializer.js +14 -0
- package/dist/src/server/dispatch-ledger-store.d.ts +108 -0
- package/dist/src/server/dispatch-ledger-store.js +241 -0
- package/dist/src/server/env-sync-message.d.ts +9 -0
- package/dist/src/server/env-sync-message.js +29 -0
- package/dist/src/server/feature-flags.d.ts +42 -0
- package/dist/src/server/feature-flags.js +24 -0
- package/dist/src/server/fs-browse.d.ts +40 -0
- package/dist/src/server/fs-browse.js +306 -0
- package/dist/src/server/fs-pick-folder.d.ts +25 -0
- package/dist/src/server/fs-pick-folder.js +115 -0
- package/dist/src/server/fs-sandbox.d.ts +15 -0
- package/dist/src/server/fs-sandbox.js +56 -0
- package/dist/src/server/hive-team-guidance.d.ts +39 -0
- package/dist/src/server/hive-team-guidance.js +295 -0
- package/dist/src/server/http-errors.d.ts +22 -0
- package/dist/src/server/http-errors.js +44 -0
- package/dist/src/server/live-run-registry.d.ts +22 -0
- package/dist/src/server/live-run-registry.js +52 -0
- package/dist/src/server/local-request-guard.d.ts +3 -0
- package/dist/src/server/local-request-guard.js +41 -0
- package/dist/src/server/machine-name.d.ts +2 -0
- package/dist/src/server/machine-name.js +13 -0
- package/dist/src/server/marketplace-store.d.ts +38 -0
- package/dist/src/server/marketplace-store.js +85 -0
- package/dist/src/server/message-log-store.d.ts +51 -0
- package/dist/src/server/message-log-store.js +90 -0
- package/dist/src/server/open-target-commands.d.ts +54 -0
- package/dist/src/server/open-target-commands.js +204 -0
- package/dist/src/server/orchestrator-autostart.d.ts +42 -0
- package/dist/src/server/orchestrator-autostart.js +114 -0
- package/dist/src/server/orchestrator-launch.d.ts +24 -0
- package/dist/src/server/orchestrator-launch.js +71 -0
- package/dist/src/server/package-version.d.ts +17 -0
- package/dist/src/server/package-version.js +32 -0
- package/dist/src/server/path-canonicalization.d.ts +3 -0
- package/dist/src/server/path-canonicalization.js +29 -0
- package/dist/src/server/platform-path.d.ts +4 -0
- package/dist/src/server/platform-path.js +26 -0
- package/dist/src/server/post-start-input-writer.d.ts +6 -0
- package/dist/src/server/post-start-input-writer.js +242 -0
- package/dist/src/server/preset-launch-support.d.ts +6 -0
- package/dist/src/server/preset-launch-support.js +98 -0
- package/dist/src/server/pty-output-bus.d.ts +8 -0
- package/dist/src/server/pty-output-bus.js +32 -0
- package/dist/src/server/recovery-summary.d.ts +15 -0
- package/dist/src/server/recovery-summary.js +92 -0
- package/dist/src/server/remote-audit-store.d.ts +51 -0
- package/dist/src/server/remote-audit-store.js +108 -0
- package/dist/src/server/remote-config-keys.d.ts +17 -0
- package/dist/src/server/remote-config-keys.js +27 -0
- package/dist/src/server/remote-control-constants.d.ts +30 -0
- package/dist/src/server/remote-control-constants.js +29 -0
- package/dist/src/server/remote-device-session.d.ts +40 -0
- package/dist/src/server/remote-device-session.js +22 -0
- package/dist/src/server/remote-device-store.d.ts +36 -0
- package/dist/src/server/remote-device-store.js +67 -0
- package/dist/src/server/remote-frame-bridge.d.ts +102 -0
- package/dist/src/server/remote-frame-bridge.js +791 -0
- package/dist/src/server/remote-gateway-client.d.ts +14 -0
- package/dist/src/server/remote-gateway-client.js +36 -0
- package/dist/src/server/remote-loopback-auth.d.ts +6 -0
- package/dist/src/server/remote-loopback-auth.js +112 -0
- package/dist/src/server/remote-pairing-tunnel.d.ts +59 -0
- package/dist/src/server/remote-pairing-tunnel.js +146 -0
- package/dist/src/server/remote-pairing.d.ts +58 -0
- package/dist/src/server/remote-pairing.js +237 -0
- package/dist/src/server/remote-tunnel.d.ts +113 -0
- package/dist/src/server/remote-tunnel.js +514 -0
- package/dist/src/server/report-outbox-store.d.ts +36 -0
- package/dist/src/server/report-outbox-store.js +33 -0
- package/dist/src/server/restart-policy-support.d.ts +30 -0
- package/dist/src/server/restart-policy-support.js +21 -0
- package/dist/src/server/restart-policy.d.ts +18 -0
- package/dist/src/server/restart-policy.js +77 -0
- package/dist/src/server/role-template-store.d.ts +45 -0
- package/dist/src/server/role-template-store.js +76 -0
- package/dist/src/server/role-templates.d.ts +7 -0
- package/dist/src/server/role-templates.js +54 -0
- package/dist/src/server/route-helpers.d.ts +9 -0
- package/dist/src/server/route-helpers.js +61 -0
- package/dist/src/server/route-types.d.ts +121 -0
- package/dist/src/server/route-types.js +1 -0
- package/dist/src/server/routes-dispatches.d.ts +2 -0
- package/dist/src/server/routes-dispatches.js +54 -0
- package/dist/src/server/routes-fs.d.ts +2 -0
- package/dist/src/server/routes-fs.js +24 -0
- package/dist/src/server/routes-marketplace.d.ts +2 -0
- package/dist/src/server/routes-marketplace.js +54 -0
- package/dist/src/server/routes-open-workspace.d.ts +2 -0
- package/dist/src/server/routes-open-workspace.js +47 -0
- package/dist/src/server/routes-remote.d.ts +2 -0
- package/dist/src/server/routes-remote.js +166 -0
- package/dist/src/server/routes-runtime.d.ts +2 -0
- package/dist/src/server/routes-runtime.js +79 -0
- package/dist/src/server/routes-settings.d.ts +2 -0
- package/dist/src/server/routes-settings.js +213 -0
- package/dist/src/server/routes-tasks.d.ts +2 -0
- package/dist/src/server/routes-tasks.js +47 -0
- package/dist/src/server/routes-team-memory.d.ts +2 -0
- package/dist/src/server/routes-team-memory.js +154 -0
- package/dist/src/server/routes-team-recall.d.ts +2 -0
- package/dist/src/server/routes-team-recall.js +119 -0
- package/dist/src/server/routes-team.d.ts +2 -0
- package/dist/src/server/routes-team.js +351 -0
- package/dist/src/server/routes-ui.d.ts +2 -0
- package/dist/src/server/routes-ui.js +17 -0
- package/dist/src/server/routes-version.d.ts +2 -0
- package/dist/src/server/routes-version.js +6 -0
- package/dist/src/server/routes-workflow-schedules.d.ts +2 -0
- package/dist/src/server/routes-workflow-schedules.js +58 -0
- package/dist/src/server/routes-workflows.d.ts +2 -0
- package/dist/src/server/routes-workflows.js +83 -0
- package/dist/src/server/routes-workspace-memory-dreams.d.ts +2 -0
- package/dist/src/server/routes-workspace-memory-dreams.js +105 -0
- package/dist/src/server/routes-workspace-memory.d.ts +2 -0
- package/dist/src/server/routes-workspace-memory.js +215 -0
- package/dist/src/server/routes-workspaces.d.ts +2 -0
- package/dist/src/server/routes-workspaces.js +177 -0
- package/dist/src/server/routes.d.ts +6 -0
- package/dist/src/server/routes.js +55 -0
- package/dist/src/server/runtime-database.d.ts +3 -0
- package/dist/src/server/runtime-database.js +41 -0
- package/dist/src/server/runtime-message-builders.d.ts +7 -0
- package/dist/src/server/runtime-message-builders.js +60 -0
- package/dist/src/server/runtime-restart-policy.d.ts +18 -0
- package/dist/src/server/runtime-restart-policy.js +12 -0
- package/dist/src/server/runtime-store-contract.d.ts +162 -0
- package/dist/src/server/runtime-store-contract.js +1 -0
- package/dist/src/server/runtime-store-dream.d.ts +23 -0
- package/dist/src/server/runtime-store-dream.js +16 -0
- package/dist/src/server/runtime-store-helpers.d.ts +115 -0
- package/dist/src/server/runtime-store-helpers.js +411 -0
- package/dist/src/server/runtime-store-memory.d.ts +33 -0
- package/dist/src/server/runtime-store-memory.js +37 -0
- package/dist/src/server/runtime-store-remote.d.ts +5 -0
- package/dist/src/server/runtime-store-remote.js +45 -0
- package/dist/src/server/runtime-store-workflows.d.ts +6 -0
- package/dist/src/server/runtime-store-workflows.js +108 -0
- package/dist/src/server/runtime-store.d.ts +4 -0
- package/dist/src/server/runtime-store.js +165 -0
- package/dist/src/server/session-capture-claude.d.ts +34 -0
- package/dist/src/server/session-capture-claude.js +103 -0
- package/dist/src/server/session-capture-codex.d.ts +6 -0
- package/dist/src/server/session-capture-codex.js +108 -0
- package/dist/src/server/session-capture-gemini.d.ts +5 -0
- package/dist/src/server/session-capture-gemini.js +81 -0
- package/dist/src/server/session-capture-opencode.d.ts +22 -0
- package/dist/src/server/session-capture-opencode.js +75 -0
- package/dist/src/server/session-capture.d.ts +77 -0
- package/dist/src/server/session-capture.js +123 -0
- package/dist/src/server/settings-store.d.ts +22 -0
- package/dist/src/server/settings-store.js +22 -0
- package/dist/src/server/sqlite-schema-v10.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v10.js +13 -0
- package/dist/src/server/sqlite-schema-v11.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v11.js +10 -0
- package/dist/src/server/sqlite-schema-v12.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v12.js +21 -0
- package/dist/src/server/sqlite-schema-v13.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v13.js +4 -0
- package/dist/src/server/sqlite-schema-v14.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v14.js +84 -0
- package/dist/src/server/sqlite-schema-v15.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v15.js +73 -0
- package/dist/src/server/sqlite-schema-v16.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v16.js +6 -0
- package/dist/src/server/sqlite-schema-v17.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v17.js +15 -0
- package/dist/src/server/sqlite-schema-v18.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v18.js +6 -0
- package/dist/src/server/sqlite-schema-v19.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v19.js +17 -0
- package/dist/src/server/sqlite-schema-v20.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v20.js +20 -0
- package/dist/src/server/sqlite-schema-v21.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v21.js +20 -0
- package/dist/src/server/sqlite-schema-v22.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v22.js +27 -0
- package/dist/src/server/sqlite-schema-v23.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v23.js +43 -0
- package/dist/src/server/sqlite-schema-v24.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v24.js +34 -0
- package/dist/src/server/sqlite-schema-v25.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v25.js +127 -0
- package/dist/src/server/sqlite-schema-v26.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v26.js +56 -0
- package/dist/src/server/sqlite-schema-v27.d.ts +6 -0
- package/dist/src/server/sqlite-schema-v27.js +92 -0
- package/dist/src/server/sqlite-schema-v28.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v28.js +19 -0
- package/dist/src/server/sqlite-schema-v5.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v5.js +61 -0
- package/dist/src/server/sqlite-schema-v7.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v7.js +70 -0
- package/dist/src/server/sqlite-schema-v8.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v8.js +6 -0
- package/dist/src/server/sqlite-schema-v9.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v9.js +6 -0
- package/dist/src/server/sqlite-schema.d.ts +3 -0
- package/dist/src/server/sqlite-schema.js +353 -0
- package/dist/src/server/startup-command-parser.d.ts +20 -0
- package/dist/src/server/startup-command-parser.js +72 -0
- package/dist/src/server/system-message.d.ts +8 -0
- package/dist/src/server/system-message.js +8 -0
- package/dist/src/server/task-deps.d.ts +32 -0
- package/dist/src/server/task-deps.js +40 -0
- package/dist/src/server/tasks-file-watcher.d.ts +47 -0
- package/dist/src/server/tasks-file-watcher.js +200 -0
- package/dist/src/server/tasks-file.d.ts +23 -0
- package/dist/src/server/tasks-file.js +101 -0
- package/dist/src/server/tasks-websocket-server.d.ts +8 -0
- package/dist/src/server/tasks-websocket-server.js +95 -0
- package/dist/src/server/team-authz.d.ts +13 -0
- package/dist/src/server/team-authz.js +53 -0
- package/dist/src/server/team-autostaff.d.ts +16 -0
- package/dist/src/server/team-autostaff.js +16 -0
- package/dist/src/server/team-list-enrichment.d.ts +22 -0
- package/dist/src/server/team-list-enrichment.js +40 -0
- package/dist/src/server/team-list-serializer.d.ts +2 -0
- package/dist/src/server/team-list-serializer.js +11 -0
- package/dist/src/server/team-memory-digest.d.ts +52 -0
- package/dist/src/server/team-memory-digest.js +200 -0
- package/dist/src/server/team-memory-dream-applier.d.ts +5 -0
- package/dist/src/server/team-memory-dream-applier.js +234 -0
- package/dist/src/server/team-memory-dream-http-serializers.d.ts +13 -0
- package/dist/src/server/team-memory-dream-http-serializers.js +12 -0
- package/dist/src/server/team-memory-dream-ops.d.ts +40 -0
- package/dist/src/server/team-memory-dream-ops.js +153 -0
- package/dist/src/server/team-memory-dream-reverter.d.ts +22 -0
- package/dist/src/server/team-memory-dream-reverter.js +221 -0
- package/dist/src/server/team-memory-dream-run-store.d.ts +23 -0
- package/dist/src/server/team-memory-dream-run-store.js +211 -0
- package/dist/src/server/team-memory-dream-runner.d.ts +37 -0
- package/dist/src/server/team-memory-dream-runner.js +178 -0
- package/dist/src/server/team-memory-dream-scheduler.d.ts +32 -0
- package/dist/src/server/team-memory-dream-scheduler.js +115 -0
- package/dist/src/server/team-memory-dream-store.d.ts +19 -0
- package/dist/src/server/team-memory-dream-store.js +16 -0
- package/dist/src/server/team-memory-dream-types.d.ts +104 -0
- package/dist/src/server/team-memory-dream-types.js +23 -0
- package/dist/src/server/team-memory-export.d.ts +22 -0
- package/dist/src/server/team-memory-export.js +220 -0
- package/dist/src/server/team-memory-feature.d.ts +12 -0
- package/dist/src/server/team-memory-feature.js +12 -0
- package/dist/src/server/team-memory-http-serializers.d.ts +102 -0
- package/dist/src/server/team-memory-http-serializers.js +46 -0
- package/dist/src/server/team-memory-injection.d.ts +31 -0
- package/dist/src/server/team-memory-injection.js +49 -0
- package/dist/src/server/team-memory-store.d.ts +116 -0
- package/dist/src/server/team-memory-store.js +513 -0
- package/dist/src/server/team-operations.d.ts +102 -0
- package/dist/src/server/team-operations.js +387 -0
- package/dist/src/server/team-recall-store.d.ts +38 -0
- package/dist/src/server/team-recall-store.js +205 -0
- package/dist/src/server/terminal-flow-control.d.ts +19 -0
- package/dist/src/server/terminal-flow-control.js +116 -0
- package/dist/src/server/terminal-input-profile.d.ts +10 -0
- package/dist/src/server/terminal-input-profile.js +9 -0
- package/dist/src/server/terminal-protocol.d.ts +29 -0
- package/dist/src/server/terminal-protocol.js +47 -0
- package/dist/src/server/terminal-state-mirror.d.ts +21 -0
- package/dist/src/server/terminal-state-mirror.js +67 -0
- package/dist/src/server/terminal-stream-hub.d.ts +9 -0
- package/dist/src/server/terminal-stream-hub.js +192 -0
- package/dist/src/server/terminal-ws-server.d.ts +6 -0
- package/dist/src/server/terminal-ws-server.js +115 -0
- package/dist/src/server/ui-auth-helpers.d.ts +4 -0
- package/dist/src/server/ui-auth-helpers.js +28 -0
- package/dist/src/server/ui-auth.d.ts +8 -0
- package/dist/src/server/ui-auth.js +32 -0
- package/dist/src/server/version-service.d.ts +16 -0
- package/dist/src/server/version-service.js +72 -0
- package/dist/src/server/webhook-notifier.d.ts +34 -0
- package/dist/src/server/webhook-notifier.js +47 -0
- package/dist/src/server/websocket-upgrade-safety.d.ts +10 -0
- package/dist/src/server/websocket-upgrade-safety.js +35 -0
- package/dist/src/server/windows-command-line.d.ts +3 -0
- package/dist/src/server/windows-command-line.js +9 -0
- package/dist/src/server/windows-filename.d.ts +2 -0
- package/dist/src/server/windows-filename.js +33 -0
- package/dist/src/server/worker-output-tracker.d.ts +14 -0
- package/dist/src/server/worker-output-tracker.js +50 -0
- package/dist/src/server/workflow-cli-policy.d.ts +60 -0
- package/dist/src/server/workflow-cli-policy.js +110 -0
- package/dist/src/server/workflow-dispatch-awaiter.d.ts +12 -0
- package/dist/src/server/workflow-dispatch-awaiter.js +80 -0
- package/dist/src/server/workflow-feature.d.ts +15 -0
- package/dist/src/server/workflow-feature.js +15 -0
- package/dist/src/server/workflow-http-serializers.d.ts +64 -0
- package/dist/src/server/workflow-http-serializers.js +58 -0
- package/dist/src/server/workflow-output-schema.d.ts +18 -0
- package/dist/src/server/workflow-output-schema.js +41 -0
- package/dist/src/server/workflow-run-log-store.d.ts +19 -0
- package/dist/src/server/workflow-run-log-store.js +45 -0
- package/dist/src/server/workflow-run-store.d.ts +50 -0
- package/dist/src/server/workflow-run-store.js +103 -0
- package/dist/src/server/workflow-runner.d.ts +147 -0
- package/dist/src/server/workflow-runner.js +411 -0
- package/dist/src/server/workflow-schedule-create.d.ts +14 -0
- package/dist/src/server/workflow-schedule-create.js +41 -0
- package/dist/src/server/workflow-schedule-store.d.ts +43 -0
- package/dist/src/server/workflow-schedule-store.js +112 -0
- package/dist/src/server/workflow-scheduler.d.ts +36 -0
- package/dist/src/server/workflow-scheduler.js +97 -0
- package/dist/src/server/workflow-script-loader.d.ts +34 -0
- package/dist/src/server/workflow-script-loader.js +106 -0
- package/dist/src/server/workspace-path-validation.d.ts +1 -0
- package/dist/src/server/workspace-path-validation.js +42 -0
- package/dist/src/server/workspace-shell-runtime.d.ts +35 -0
- package/dist/src/server/workspace-shell-runtime.js +228 -0
- package/dist/src/server/workspace-store-contract.d.ts +32 -0
- package/dist/src/server/workspace-store-contract.js +1 -0
- package/dist/src/server/workspace-store-hydration.d.ts +5 -0
- package/dist/src/server/workspace-store-hydration.js +76 -0
- package/dist/src/server/workspace-store-mutations.d.ts +14 -0
- package/dist/src/server/workspace-store-mutations.js +48 -0
- package/dist/src/server/workspace-store-support.d.ts +33 -0
- package/dist/src/server/workspace-store-support.js +36 -0
- package/dist/src/server/workspace-store.d.ts +5 -0
- package/dist/src/server/workspace-store.js +153 -0
- package/dist/src/shared/fs-browse.d.ts +1 -0
- package/dist/src/shared/fs-browse.js +1 -0
- package/dist/src/shared/open-targets.d.ts +20 -0
- package/dist/src/shared/open-targets.js +36 -0
- package/dist/src/shared/path-input.d.ts +12 -0
- package/dist/src/shared/path-input.js +22 -0
- package/dist/src/shared/remote-bridge-routing.d.ts +19 -0
- package/dist/src/shared/remote-bridge-routing.js +141 -0
- package/dist/src/shared/remote-crypto.d.ts +138 -0
- package/dist/src/shared/remote-crypto.js +427 -0
- package/dist/src/shared/remote-pairing-code.d.ts +7 -0
- package/dist/src/shared/remote-pairing-code.js +47 -0
- package/dist/src/shared/remote-protocol.d.ts +160 -0
- package/dist/src/shared/remote-protocol.js +526 -0
- package/dist/src/shared/team-memory.d.ts +11 -0
- package/dist/src/shared/team-memory.js +10 -0
- package/dist/src/shared/team-recall.d.ts +1 -0
- package/dist/src/shared/team-recall.js +1 -0
- package/dist/src/shared/types.d.ts +66 -0
- package/dist/src/shared/types.js +1 -0
- package/dist/vendor/marketplace/en/LICENSE +21 -0
- package/dist/vendor/marketplace/en/SOURCES.md +12 -0
- package/dist/vendor/marketplace/en/academic/academic-anthropologist.md +125 -0
- package/dist/vendor/marketplace/en/academic/academic-geographer.md +127 -0
- package/dist/vendor/marketplace/en/academic/academic-historian.md +123 -0
- package/dist/vendor/marketplace/en/academic/academic-narratologist.md +118 -0
- package/dist/vendor/marketplace/en/academic/academic-psychologist.md +118 -0
- package/dist/vendor/marketplace/en/design/design-brand-guardian.md +322 -0
- package/dist/vendor/marketplace/en/design/design-image-prompt-engineer.md +236 -0
- package/dist/vendor/marketplace/en/design/design-inclusive-visuals-specialist.md +71 -0
- package/dist/vendor/marketplace/en/design/design-ui-designer.md +383 -0
- package/dist/vendor/marketplace/en/design/design-ux-architect.md +469 -0
- package/dist/vendor/marketplace/en/design/design-ux-researcher.md +329 -0
- package/dist/vendor/marketplace/en/design/design-visual-storyteller.md +149 -0
- package/dist/vendor/marketplace/en/design/design-whimsy-injector.md +438 -0
- package/dist/vendor/marketplace/en/engineering/engineering-ai-data-remediation-engineer.md +211 -0
- package/dist/vendor/marketplace/en/engineering/engineering-ai-engineer.md +146 -0
- package/dist/vendor/marketplace/en/engineering/engineering-autonomous-optimization-architect.md +107 -0
- package/dist/vendor/marketplace/en/engineering/engineering-backend-architect.md +235 -0
- package/dist/vendor/marketplace/en/engineering/engineering-cms-developer.md +536 -0
- package/dist/vendor/marketplace/en/engineering/engineering-code-reviewer.md +76 -0
- package/dist/vendor/marketplace/en/engineering/engineering-codebase-onboarding-engineer.md +173 -0
- package/dist/vendor/marketplace/en/engineering/engineering-data-engineer.md +306 -0
- package/dist/vendor/marketplace/en/engineering/engineering-database-optimizer.md +176 -0
- package/dist/vendor/marketplace/en/engineering/engineering-devops-automator.md +376 -0
- package/dist/vendor/marketplace/en/engineering/engineering-email-intelligence-engineer.md +353 -0
- package/dist/vendor/marketplace/en/engineering/engineering-embedded-firmware-engineer.md +173 -0
- package/dist/vendor/marketplace/en/engineering/engineering-feishu-integration-developer.md +598 -0
- package/dist/vendor/marketplace/en/engineering/engineering-filament-optimization-specialist.md +283 -0
- package/dist/vendor/marketplace/en/engineering/engineering-frontend-developer.md +225 -0
- package/dist/vendor/marketplace/en/engineering/engineering-git-workflow-master.md +84 -0
- package/dist/vendor/marketplace/en/engineering/engineering-incident-response-commander.md +444 -0
- package/dist/vendor/marketplace/en/engineering/engineering-minimal-change-engineer.md +207 -0
- package/dist/vendor/marketplace/en/engineering/engineering-mobile-app-builder.md +493 -0
- package/dist/vendor/marketplace/en/engineering/engineering-rapid-prototyper.md +462 -0
- package/dist/vendor/marketplace/en/engineering/engineering-security-engineer.md +304 -0
- package/dist/vendor/marketplace/en/engineering/engineering-senior-developer.md +176 -0
- package/dist/vendor/marketplace/en/engineering/engineering-software-architect.md +81 -0
- package/dist/vendor/marketplace/en/engineering/engineering-solidity-smart-contract-engineer.md +522 -0
- package/dist/vendor/marketplace/en/engineering/engineering-sre.md +90 -0
- package/dist/vendor/marketplace/en/engineering/engineering-technical-writer.md +393 -0
- package/dist/vendor/marketplace/en/engineering/engineering-threat-detection-engineer.md +534 -0
- package/dist/vendor/marketplace/en/engineering/engineering-voice-ai-integration-engineer.md +561 -0
- package/dist/vendor/marketplace/en/engineering/engineering-wechat-mini-program-developer.md +350 -0
- package/dist/vendor/marketplace/en/finance/finance-bookkeeper-controller.md +260 -0
- package/dist/vendor/marketplace/en/finance/finance-financial-analyst.md +234 -0
- package/dist/vendor/marketplace/en/finance/finance-fpa-analyst.md +263 -0
- package/dist/vendor/marketplace/en/finance/finance-investment-researcher.md +272 -0
- package/dist/vendor/marketplace/en/finance/finance-tax-strategist.md +239 -0
- package/dist/vendor/marketplace/en/game-development/blender/blender-addon-engineer.md +234 -0
- package/dist/vendor/marketplace/en/game-development/game-audio-engineer.md +264 -0
- package/dist/vendor/marketplace/en/game-development/game-designer.md +167 -0
- package/dist/vendor/marketplace/en/game-development/godot/godot-gameplay-scripter.md +334 -0
- package/dist/vendor/marketplace/en/game-development/godot/godot-multiplayer-engineer.md +297 -0
- package/dist/vendor/marketplace/en/game-development/godot/godot-shader-developer.md +266 -0
- package/dist/vendor/marketplace/en/game-development/level-designer.md +208 -0
- package/dist/vendor/marketplace/en/game-development/narrative-designer.md +243 -0
- package/dist/vendor/marketplace/en/game-development/roblox-studio/roblox-avatar-creator.md +297 -0
- package/dist/vendor/marketplace/en/game-development/roblox-studio/roblox-experience-designer.md +305 -0
- package/dist/vendor/marketplace/en/game-development/roblox-studio/roblox-systems-scripter.md +325 -0
- package/dist/vendor/marketplace/en/game-development/technical-artist.md +229 -0
- package/dist/vendor/marketplace/en/game-development/unity/unity-architect.md +271 -0
- package/dist/vendor/marketplace/en/game-development/unity/unity-editor-tool-developer.md +310 -0
- package/dist/vendor/marketplace/en/game-development/unity/unity-multiplayer-engineer.md +321 -0
- package/dist/vendor/marketplace/en/game-development/unity/unity-shader-graph-artist.md +269 -0
- package/dist/vendor/marketplace/en/game-development/unreal-engine/unreal-multiplayer-architect.md +313 -0
- package/dist/vendor/marketplace/en/game-development/unreal-engine/unreal-systems-engineer.md +310 -0
- package/dist/vendor/marketplace/en/game-development/unreal-engine/unreal-technical-artist.md +256 -0
- package/dist/vendor/marketplace/en/game-development/unreal-engine/unreal-world-builder.md +273 -0
- package/dist/vendor/marketplace/en/integrations/mcp-memory/backend-architect-with-memory.md +247 -0
- package/dist/vendor/marketplace/en/manifest.json +1869 -0
- package/dist/vendor/marketplace/en/marketing/marketing-agentic-search-optimizer.md +311 -0
- package/dist/vendor/marketplace/en/marketing/marketing-ai-citation-strategist.md +170 -0
- package/dist/vendor/marketplace/en/marketing/marketing-app-store-optimizer.md +321 -0
- package/dist/vendor/marketplace/en/marketing/marketing-baidu-seo-specialist.md +226 -0
- package/dist/vendor/marketplace/en/marketing/marketing-bilibili-content-strategist.md +199 -0
- package/dist/vendor/marketplace/en/marketing/marketing-book-co-author.md +110 -0
- package/dist/vendor/marketplace/en/marketing/marketing-carousel-growth-engine.md +199 -0
- package/dist/vendor/marketplace/en/marketing/marketing-china-ecommerce-operator.md +283 -0
- package/dist/vendor/marketplace/en/marketing/marketing-china-market-localization-strategist.md +283 -0
- package/dist/vendor/marketplace/en/marketing/marketing-content-creator.md +54 -0
- package/dist/vendor/marketplace/en/marketing/marketing-cross-border-ecommerce.md +259 -0
- package/dist/vendor/marketplace/en/marketing/marketing-douyin-strategist.md +149 -0
- package/dist/vendor/marketplace/en/marketing/marketing-growth-hacker.md +54 -0
- package/dist/vendor/marketplace/en/marketing/marketing-instagram-curator.md +113 -0
- package/dist/vendor/marketplace/en/marketing/marketing-kuaishou-strategist.md +223 -0
- package/dist/vendor/marketplace/en/marketing/marketing-linkedin-content-creator.md +214 -0
- package/dist/vendor/marketplace/en/marketing/marketing-livestream-commerce-coach.md +305 -0
- package/dist/vendor/marketplace/en/marketing/marketing-podcast-strategist.md +277 -0
- package/dist/vendor/marketplace/en/marketing/marketing-private-domain-operator.md +308 -0
- package/dist/vendor/marketplace/en/marketing/marketing-reddit-community-builder.md +123 -0
- package/dist/vendor/marketplace/en/marketing/marketing-seo-specialist.md +321 -0
- package/dist/vendor/marketplace/en/marketing/marketing-short-video-editing-coach.md +412 -0
- package/dist/vendor/marketplace/en/marketing/marketing-social-media-strategist.md +125 -0
- package/dist/vendor/marketplace/en/marketing/marketing-tiktok-strategist.md +125 -0
- package/dist/vendor/marketplace/en/marketing/marketing-twitter-engager.md +126 -0
- package/dist/vendor/marketplace/en/marketing/marketing-video-optimization-specialist.md +119 -0
- package/dist/vendor/marketplace/en/marketing/marketing-wechat-official-account.md +145 -0
- package/dist/vendor/marketplace/en/marketing/marketing-weibo-strategist.md +240 -0
- package/dist/vendor/marketplace/en/marketing/marketing-xiaohongshu-specialist.md +138 -0
- package/dist/vendor/marketplace/en/marketing/marketing-zhihu-strategist.md +162 -0
- package/dist/vendor/marketplace/en/paid-media/paid-media-auditor.md +71 -0
- package/dist/vendor/marketplace/en/paid-media/paid-media-creative-strategist.md +71 -0
- package/dist/vendor/marketplace/en/paid-media/paid-media-paid-social-strategist.md +71 -0
- package/dist/vendor/marketplace/en/paid-media/paid-media-ppc-strategist.md +71 -0
- package/dist/vendor/marketplace/en/paid-media/paid-media-programmatic-buyer.md +71 -0
- package/dist/vendor/marketplace/en/paid-media/paid-media-search-query-analyst.md +71 -0
- package/dist/vendor/marketplace/en/paid-media/paid-media-tracking-specialist.md +71 -0
- package/dist/vendor/marketplace/en/product/product-behavioral-nudge-engine.md +80 -0
- package/dist/vendor/marketplace/en/product/product-feedback-synthesizer.md +119 -0
- package/dist/vendor/marketplace/en/product/product-manager.md +469 -0
- package/dist/vendor/marketplace/en/product/product-sprint-prioritizer.md +154 -0
- package/dist/vendor/marketplace/en/product/product-trend-researcher.md +159 -0
- package/dist/vendor/marketplace/en/project-management/project-management-experiment-tracker.md +198 -0
- package/dist/vendor/marketplace/en/project-management/project-management-jira-workflow-steward.md +230 -0
- package/dist/vendor/marketplace/en/project-management/project-management-project-shepherd.md +194 -0
- package/dist/vendor/marketplace/en/project-management/project-management-studio-operations.md +200 -0
- package/dist/vendor/marketplace/en/project-management/project-management-studio-producer.md +203 -0
- package/dist/vendor/marketplace/en/project-management/project-manager-senior.md +135 -0
- package/dist/vendor/marketplace/en/sales/sales-account-strategist.md +227 -0
- package/dist/vendor/marketplace/en/sales/sales-coach.md +271 -0
- package/dist/vendor/marketplace/en/sales/sales-deal-strategist.md +180 -0
- package/dist/vendor/marketplace/en/sales/sales-discovery-coach.md +225 -0
- package/dist/vendor/marketplace/en/sales/sales-engineer.md +182 -0
- package/dist/vendor/marketplace/en/sales/sales-outbound-strategist.md +201 -0
- package/dist/vendor/marketplace/en/sales/sales-pipeline-analyst.md +267 -0
- package/dist/vendor/marketplace/en/sales/sales-proposal-strategist.md +217 -0
- package/dist/vendor/marketplace/en/spatial-computing/macos-spatial-metal-engineer.md +337 -0
- package/dist/vendor/marketplace/en/spatial-computing/terminal-integration-specialist.md +70 -0
- package/dist/vendor/marketplace/en/spatial-computing/visionos-spatial-engineer.md +54 -0
- package/dist/vendor/marketplace/en/spatial-computing/xr-cockpit-interaction-specialist.md +32 -0
- package/dist/vendor/marketplace/en/spatial-computing/xr-immersive-developer.md +32 -0
- package/dist/vendor/marketplace/en/spatial-computing/xr-interface-architect.md +32 -0
- package/dist/vendor/marketplace/en/specialized/accounts-payable-agent.md +185 -0
- package/dist/vendor/marketplace/en/specialized/agentic-identity-trust.md +387 -0
- package/dist/vendor/marketplace/en/specialized/agents-orchestrator.md +367 -0
- package/dist/vendor/marketplace/en/specialized/automation-governance-architect.md +216 -0
- package/dist/vendor/marketplace/en/specialized/blockchain-security-auditor.md +463 -0
- package/dist/vendor/marketplace/en/specialized/compliance-auditor.md +158 -0
- package/dist/vendor/marketplace/en/specialized/corporate-training-designer.md +192 -0
- package/dist/vendor/marketplace/en/specialized/customer-service.md +398 -0
- package/dist/vendor/marketplace/en/specialized/data-consolidation-agent.md +60 -0
- package/dist/vendor/marketplace/en/specialized/government-digital-presales-consultant.md +363 -0
- package/dist/vendor/marketplace/en/specialized/healthcare-customer-service.md +389 -0
- package/dist/vendor/marketplace/en/specialized/healthcare-marketing-compliance.md +395 -0
- package/dist/vendor/marketplace/en/specialized/hospitality-guest-services.md +603 -0
- package/dist/vendor/marketplace/en/specialized/hr-onboarding.md +451 -0
- package/dist/vendor/marketplace/en/specialized/identity-graph-operator.md +260 -0
- package/dist/vendor/marketplace/en/specialized/language-translator.md +264 -0
- package/dist/vendor/marketplace/en/specialized/legal-billing-time-tracking.md +569 -0
- package/dist/vendor/marketplace/en/specialized/legal-client-intake.md +492 -0
- package/dist/vendor/marketplace/en/specialized/legal-document-review.md +454 -0
- package/dist/vendor/marketplace/en/specialized/loan-officer-assistant.md +555 -0
- package/dist/vendor/marketplace/en/specialized/lsp-index-engineer.md +314 -0
- package/dist/vendor/marketplace/en/specialized/real-estate-buyer-seller.md +596 -0
- package/dist/vendor/marketplace/en/specialized/recruitment-specialist.md +509 -0
- package/dist/vendor/marketplace/en/specialized/report-distribution-agent.md +65 -0
- package/dist/vendor/marketplace/en/specialized/retail-customer-returns.md +566 -0
- package/dist/vendor/marketplace/en/specialized/sales-data-extraction-agent.md +67 -0
- package/dist/vendor/marketplace/en/specialized/sales-outreach.md +425 -0
- package/dist/vendor/marketplace/en/specialized/specialized-chief-of-staff.md +279 -0
- package/dist/vendor/marketplace/en/specialized/specialized-civil-engineer.md +356 -0
- package/dist/vendor/marketplace/en/specialized/specialized-cultural-intelligence-strategist.md +88 -0
- package/dist/vendor/marketplace/en/specialized/specialized-developer-advocate.md +317 -0
- package/dist/vendor/marketplace/en/specialized/specialized-document-generator.md +55 -0
- package/dist/vendor/marketplace/en/specialized/specialized-french-consulting-market.md +192 -0
- package/dist/vendor/marketplace/en/specialized/specialized-korean-business-navigator.md +216 -0
- package/dist/vendor/marketplace/en/specialized/specialized-mcp-builder.md +248 -0
- package/dist/vendor/marketplace/en/specialized/specialized-model-qa.md +488 -0
- package/dist/vendor/marketplace/en/specialized/specialized-salesforce-architect.md +180 -0
- package/dist/vendor/marketplace/en/specialized/specialized-workflow-architect.md +597 -0
- package/dist/vendor/marketplace/en/specialized/study-abroad-advisor.md +282 -0
- package/dist/vendor/marketplace/en/specialized/supply-chain-strategist.md +582 -0
- package/dist/vendor/marketplace/en/support/support-analytics-reporter.md +365 -0
- package/dist/vendor/marketplace/en/support/support-executive-summary-generator.md +212 -0
- package/dist/vendor/marketplace/en/support/support-finance-tracker.md +442 -0
- package/dist/vendor/marketplace/en/support/support-infrastructure-maintainer.md +618 -0
- package/dist/vendor/marketplace/en/support/support-legal-compliance-checker.md +588 -0
- package/dist/vendor/marketplace/en/support/support-support-responder.md +585 -0
- package/dist/vendor/marketplace/en/testing/testing-accessibility-auditor.md +316 -0
- package/dist/vendor/marketplace/en/testing/testing-api-tester.md +306 -0
- package/dist/vendor/marketplace/en/testing/testing-evidence-collector.md +210 -0
- package/dist/vendor/marketplace/en/testing/testing-performance-benchmarker.md +268 -0
- package/dist/vendor/marketplace/en/testing/testing-reality-checker.md +236 -0
- package/dist/vendor/marketplace/en/testing/testing-test-results-analyzer.md +305 -0
- package/dist/vendor/marketplace/en/testing/testing-tool-evaluator.md +394 -0
- package/dist/vendor/marketplace/en/testing/testing-workflow-optimizer.md +450 -0
- package/dist/vendor/marketplace/zh/LICENSE +22 -0
- package/dist/vendor/marketplace/zh/SOURCES.md +12 -0
- package/dist/vendor/marketplace/zh/academic/academic-anthropologist.md +124 -0
- package/dist/vendor/marketplace/zh/academic/academic-geographer.md +126 -0
- package/dist/vendor/marketplace/zh/academic/academic-historian.md +122 -0
- package/dist/vendor/marketplace/zh/academic/academic-narratologist.md +117 -0
- package/dist/vendor/marketplace/zh/academic/academic-psychologist.md +117 -0
- package/dist/vendor/marketplace/zh/academic/academic-study-planner.md +214 -0
- package/dist/vendor/marketplace/zh/design/design-brand-guardian.md +321 -0
- package/dist/vendor/marketplace/zh/design/design-image-prompt-engineer.md +255 -0
- package/dist/vendor/marketplace/zh/design/design-inclusive-visuals-specialist.md +177 -0
- package/dist/vendor/marketplace/zh/design/design-ui-designer.md +382 -0
- package/dist/vendor/marketplace/zh/design/design-ux-architect.md +482 -0
- package/dist/vendor/marketplace/zh/design/design-ux-researcher.md +328 -0
- package/dist/vendor/marketplace/zh/design/design-visual-storyteller.md +159 -0
- package/dist/vendor/marketplace/zh/design/design-whimsy-injector.md +453 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-ai-data-remediation-engineer.md +209 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-ai-engineer.md +161 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-autonomous-optimization-architect.md +115 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-backend-architect.md +234 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-cms-developer.md +534 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-code-reviewer.md +172 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-codebase-onboarding-engineer.md +172 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-data-engineer.md +324 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-database-optimizer.md +175 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-devops-automator.md +375 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-dingtalk-integration-developer.md +574 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-email-intelligence-engineer.md +349 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-embedded-firmware-engineer.md +168 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-embedded-linux-driver-engineer.md +255 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-feishu-integration-developer.md +597 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-filament-optimization-specialist.md +283 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-fpga-digital-design-engineer.md +227 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-frontend-developer.md +224 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-git-workflow-master.md +220 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-incident-response-commander.md +465 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-iot-solution-architect.md +220 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-mechanical-design-engineer.md +311 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-minimal-change-engineer.md +206 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-mobile-app-builder.md +434 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-pc-host-engineer.md +231 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-rapid-prototyper.md +461 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-security-engineer.md +303 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-senior-developer.md +177 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-software-architect.md +200 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-solidity-smart-contract-engineer.md +541 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-sre.md +233 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-technical-writer.md +409 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-threat-detection-engineer.md +553 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-voice-ai-integration-engineer.md +560 -0
- package/dist/vendor/marketplace/zh/engineering/engineering-wechat-mini-program-developer.md +288 -0
- package/dist/vendor/marketplace/zh/finance/finance-bookkeeper-controller.md +271 -0
- package/dist/vendor/marketplace/zh/finance/finance-financial-analyst.md +244 -0
- package/dist/vendor/marketplace/zh/finance/finance-financial-forecaster.md +213 -0
- package/dist/vendor/marketplace/zh/finance/finance-fpa-analyst.md +272 -0
- package/dist/vendor/marketplace/zh/finance/finance-fraud-detector.md +241 -0
- package/dist/vendor/marketplace/zh/finance/finance-investment-researcher.md +283 -0
- package/dist/vendor/marketplace/zh/finance/finance-invoice-manager.md +242 -0
- package/dist/vendor/marketplace/zh/finance/finance-tax-strategist.md +250 -0
- package/dist/vendor/marketplace/zh/game-development/blender/blender-addon-engineer.md +233 -0
- package/dist/vendor/marketplace/zh/game-development/game-audio-engineer.md +265 -0
- package/dist/vendor/marketplace/zh/game-development/game-designer.md +168 -0
- package/dist/vendor/marketplace/zh/game-development/godot/godot-gameplay-scripter.md +335 -0
- package/dist/vendor/marketplace/zh/game-development/godot/godot-multiplayer-engineer.md +296 -0
- package/dist/vendor/marketplace/zh/game-development/godot/godot-shader-developer.md +267 -0
- package/dist/vendor/marketplace/zh/game-development/level-designer.md +209 -0
- package/dist/vendor/marketplace/zh/game-development/narrative-designer.md +244 -0
- package/dist/vendor/marketplace/zh/game-development/roblox-studio/roblox-avatar-creator.md +298 -0
- package/dist/vendor/marketplace/zh/game-development/roblox-studio/roblox-experience-designer.md +306 -0
- package/dist/vendor/marketplace/zh/game-development/roblox-studio/roblox-systems-scripter.md +325 -0
- package/dist/vendor/marketplace/zh/game-development/technical-artist.md +230 -0
- package/dist/vendor/marketplace/zh/game-development/unity/unity-architect.md +272 -0
- package/dist/vendor/marketplace/zh/game-development/unity/unity-editor-tool-developer.md +300 -0
- package/dist/vendor/marketplace/zh/game-development/unity/unity-multiplayer-engineer.md +238 -0
- package/dist/vendor/marketplace/zh/game-development/unity/unity-shader-graph-artist.md +270 -0
- package/dist/vendor/marketplace/zh/game-development/unreal-engine/unreal-multiplayer-architect.md +314 -0
- package/dist/vendor/marketplace/zh/game-development/unreal-engine/unreal-systems-engineer.md +311 -0
- package/dist/vendor/marketplace/zh/game-development/unreal-engine/unreal-technical-artist.md +256 -0
- package/dist/vendor/marketplace/zh/game-development/unreal-engine/unreal-world-builder.md +274 -0
- package/dist/vendor/marketplace/zh/hr/hr-performance-reviewer.md +248 -0
- package/dist/vendor/marketplace/zh/hr/hr-recruiter.md +232 -0
- package/dist/vendor/marketplace/zh/legal/legal-contract-reviewer.md +240 -0
- package/dist/vendor/marketplace/zh/legal/legal-policy-writer.md +308 -0
- package/dist/vendor/marketplace/zh/manifest.json +2181 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-agentic-search-optimizer.md +312 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-ai-citation-strategist.md +169 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-app-store-optimizer.md +319 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-baidu-seo-specialist.md +220 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-bilibili-strategist.md +194 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-book-co-author.md +109 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-carousel-growth-engine.md +215 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-china-ecommerce-operator.md +277 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-china-market-localization-strategist.md +282 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-content-creator.md +145 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-cross-border-ecommerce.md +260 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-daily-news-briefing.md +303 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-douyin-strategist.md +150 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-ecommerce-operator.md +216 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-growth-hacker.md +121 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-instagram-curator.md +179 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-knowledge-commerce-strategist.md +384 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-kuaishou-strategist.md +182 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-linkedin-content-creator.md +232 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-livestream-commerce-coach.md +303 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-podcast-strategist.md +278 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-private-domain-operator.md +309 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-reddit-community-builder.md +127 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-seo-specialist.md +298 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-short-video-editing-coach.md +413 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-social-media-strategist.md +118 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-tiktok-strategist.md +124 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-twitter-engager.md +132 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-video-optimization-specialist.md +128 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-wechat-official-account.md +158 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-wechat-operator.md +156 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-weibo-strategist.md +241 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-weixin-channels-strategist.md +297 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-xiaohongshu-operator.md +139 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-xiaohongshu-specialist.md +151 -0
- package/dist/vendor/marketplace/zh/marketing/marketing-zhihu-strategist.md +175 -0
- package/dist/vendor/marketplace/zh/paid-media/paid-media-auditor.md +170 -0
- package/dist/vendor/marketplace/zh/paid-media/paid-media-creative-strategist.md +173 -0
- package/dist/vendor/marketplace/zh/paid-media/paid-media-paid-social-strategist.md +180 -0
- package/dist/vendor/marketplace/zh/paid-media/paid-media-ppc-strategist.md +180 -0
- package/dist/vendor/marketplace/zh/paid-media/paid-media-programmatic-buyer.md +177 -0
- package/dist/vendor/marketplace/zh/paid-media/paid-media-search-query-analyst.md +182 -0
- package/dist/vendor/marketplace/zh/paid-media/paid-media-tracking-specialist.md +199 -0
- package/dist/vendor/marketplace/zh/product/product-behavioral-nudge-engine.md +246 -0
- package/dist/vendor/marketplace/zh/product/product-feedback-synthesizer.md +175 -0
- package/dist/vendor/marketplace/zh/product/product-manager.md +474 -0
- package/dist/vendor/marketplace/zh/product/product-sprint-prioritizer.md +133 -0
- package/dist/vendor/marketplace/zh/product/product-trend-researcher.md +143 -0
- package/dist/vendor/marketplace/zh/project-management/project-management-experiment-tracker.md +206 -0
- package/dist/vendor/marketplace/zh/project-management/project-management-jira-workflow-steward.md +249 -0
- package/dist/vendor/marketplace/zh/project-management/project-management-project-shepherd.md +202 -0
- package/dist/vendor/marketplace/zh/project-management/project-management-studio-operations.md +208 -0
- package/dist/vendor/marketplace/zh/project-management/project-management-studio-producer.md +211 -0
- package/dist/vendor/marketplace/zh/project-management/project-manager-senior.md +135 -0
- package/dist/vendor/marketplace/zh/sales/sales-account-strategist.md +243 -0
- package/dist/vendor/marketplace/zh/sales/sales-coach.md +291 -0
- package/dist/vendor/marketplace/zh/sales/sales-deal-strategist.md +204 -0
- package/dist/vendor/marketplace/zh/sales/sales-discovery-coach.md +230 -0
- package/dist/vendor/marketplace/zh/sales/sales-engineer.md +200 -0
- package/dist/vendor/marketplace/zh/sales/sales-outbound-strategist.md +208 -0
- package/dist/vendor/marketplace/zh/sales/sales-pipeline-analyst.md +284 -0
- package/dist/vendor/marketplace/zh/sales/sales-proposal-strategist.md +233 -0
- package/dist/vendor/marketplace/zh/spatial-computing/macos-spatial-metal-engineer.md +337 -0
- package/dist/vendor/marketplace/zh/spatial-computing/terminal-integration-specialist.md +236 -0
- package/dist/vendor/marketplace/zh/spatial-computing/visionos-spatial-engineer.md +282 -0
- package/dist/vendor/marketplace/zh/spatial-computing/xr-cockpit-interaction-specialist.md +220 -0
- package/dist/vendor/marketplace/zh/spatial-computing/xr-immersive-developer.md +229 -0
- package/dist/vendor/marketplace/zh/spatial-computing/xr-interface-architect.md +253 -0
- package/dist/vendor/marketplace/zh/specialized/accounts-payable-agent.md +212 -0
- package/dist/vendor/marketplace/zh/specialized/agentic-identity-trust.md +388 -0
- package/dist/vendor/marketplace/zh/specialized/agents-orchestrator.md +366 -0
- package/dist/vendor/marketplace/zh/specialized/automation-governance-architect.md +215 -0
- package/dist/vendor/marketplace/zh/specialized/blockchain-security-auditor.md +484 -0
- package/dist/vendor/marketplace/zh/specialized/compliance-auditor.md +172 -0
- package/dist/vendor/marketplace/zh/specialized/corporate-training-designer.md +191 -0
- package/dist/vendor/marketplace/zh/specialized/data-consolidation-agent.md +327 -0
- package/dist/vendor/marketplace/zh/specialized/gaokao-college-advisor.md +333 -0
- package/dist/vendor/marketplace/zh/specialized/government-digital-presales-consultant.md +362 -0
- package/dist/vendor/marketplace/zh/specialized/healthcare-customer-service.md +388 -0
- package/dist/vendor/marketplace/zh/specialized/healthcare-marketing-compliance.md +394 -0
- package/dist/vendor/marketplace/zh/specialized/hospitality-guest-services.md +597 -0
- package/dist/vendor/marketplace/zh/specialized/hr-onboarding.md +450 -0
- package/dist/vendor/marketplace/zh/specialized/identity-graph-operator.md +270 -0
- package/dist/vendor/marketplace/zh/specialized/language-translator.md +275 -0
- package/dist/vendor/marketplace/zh/specialized/legal-billing-time-tracking.md +566 -0
- package/dist/vendor/marketplace/zh/specialized/legal-client-intake.md +487 -0
- package/dist/vendor/marketplace/zh/specialized/legal-document-review.md +452 -0
- package/dist/vendor/marketplace/zh/specialized/livestock-archive-auditor.md +135 -0
- package/dist/vendor/marketplace/zh/specialized/loan-officer-assistant.md +549 -0
- package/dist/vendor/marketplace/zh/specialized/lsp-index-engineer.md +334 -0
- package/dist/vendor/marketplace/zh/specialized/prompt-engineer.md +176 -0
- package/dist/vendor/marketplace/zh/specialized/real-estate-buyer-seller.md +594 -0
- package/dist/vendor/marketplace/zh/specialized/recruitment-specialist.md +508 -0
- package/dist/vendor/marketplace/zh/specialized/report-distribution-agent.md +354 -0
- package/dist/vendor/marketplace/zh/specialized/retail-customer-returns.md +564 -0
- package/dist/vendor/marketplace/zh/specialized/sales-data-extraction-agent.md +159 -0
- package/dist/vendor/marketplace/zh/specialized/specialized-ai-policy-writer.md +217 -0
- package/dist/vendor/marketplace/zh/specialized/specialized-chief-of-staff.md +278 -0
- package/dist/vendor/marketplace/zh/specialized/specialized-civil-engineer.md +355 -0
- package/dist/vendor/marketplace/zh/specialized/specialized-cultural-intelligence-strategist.md +168 -0
- package/dist/vendor/marketplace/zh/specialized/specialized-developer-advocate.md +334 -0
- package/dist/vendor/marketplace/zh/specialized/specialized-document-generator.md +346 -0
- package/dist/vendor/marketplace/zh/specialized/specialized-french-consulting-market.md +191 -0
- package/dist/vendor/marketplace/zh/specialized/specialized-korean-business-navigator.md +215 -0
- package/dist/vendor/marketplace/zh/specialized/specialized-mcp-builder.md +351 -0
- package/dist/vendor/marketplace/zh/specialized/specialized-meeting-assistant.md +236 -0
- package/dist/vendor/marketplace/zh/specialized/specialized-model-qa.md +507 -0
- package/dist/vendor/marketplace/zh/specialized/specialized-pricing-optimizer.md +254 -0
- package/dist/vendor/marketplace/zh/specialized/specialized-risk-assessor.md +240 -0
- package/dist/vendor/marketplace/zh/specialized/specialized-salesforce-architect.md +179 -0
- package/dist/vendor/marketplace/zh/specialized/specialized-workflow-architect.md +596 -0
- package/dist/vendor/marketplace/zh/specialized/study-abroad-advisor.md +281 -0
- package/dist/vendor/marketplace/zh/specialized/technical-translator-agent.md +167 -0
- package/dist/vendor/marketplace/zh/specialized/zk-steward.md +228 -0
- package/dist/vendor/marketplace/zh/supply-chain/supply-chain-inventory-forecaster.md +212 -0
- package/dist/vendor/marketplace/zh/supply-chain/supply-chain-route-optimizer.md +224 -0
- package/dist/vendor/marketplace/zh/supply-chain/supply-chain-strategist.md +581 -0
- package/dist/vendor/marketplace/zh/supply-chain/supply-chain-vendor-evaluator.md +232 -0
- package/dist/vendor/marketplace/zh/support/support-analytics-reporter.md +364 -0
- package/dist/vendor/marketplace/zh/support/support-executive-summary-generator.md +217 -0
- package/dist/vendor/marketplace/zh/support/support-finance-tracker.md +447 -0
- package/dist/vendor/marketplace/zh/support/support-infrastructure-maintainer.md +623 -0
- package/dist/vendor/marketplace/zh/support/support-legal-compliance-checker.md +587 -0
- package/dist/vendor/marketplace/zh/support/support-recruitment-specialist.md +508 -0
- package/dist/vendor/marketplace/zh/support/support-support-responder.md +584 -0
- package/dist/vendor/marketplace/zh/testing/testing-accessibility-auditor.md +329 -0
- package/dist/vendor/marketplace/zh/testing/testing-api-tester.md +305 -0
- package/dist/vendor/marketplace/zh/testing/testing-embedded-qa-engineer.md +258 -0
- package/dist/vendor/marketplace/zh/testing/testing-evidence-collector.md +153 -0
- package/dist/vendor/marketplace/zh/testing/testing-performance-benchmarker.md +196 -0
- package/dist/vendor/marketplace/zh/testing/testing-reality-checker.md +235 -0
- package/dist/vendor/marketplace/zh/testing/testing-test-results-analyzer.md +313 -0
- package/dist/vendor/marketplace/zh/testing/testing-tool-evaluator.md +402 -0
- package/dist/vendor/marketplace/zh/testing/testing-workflow-optimizer.md +458 -0
- package/package.json +131 -0
- package/scripts/postinstall-native-artifacts.mjs +113 -0
- package/web/dist/assets/AddWorkerDialog-BU2Fn-Mx.js +2 -0
- package/web/dist/assets/AddWorkspaceFlow-BV04MkRt.js +1 -0
- package/web/dist/assets/FirstRunWizard-DS4IYOrd.js +1 -0
- package/web/dist/assets/MarketplaceDrawer-CIQ2B6Fx.js +76 -0
- package/web/dist/assets/TaskGraphDrawer-BPMcRocd.js +1 -0
- package/web/dist/assets/WhatsNewDialog-DEgVY_LD.js +1 -0
- package/web/dist/assets/WorkerModal-DiCcTPpc.js +1 -0
- package/web/dist/assets/WorkflowsDrawer-M2CgAJWK.js +1 -0
- package/web/dist/assets/WorkspaceMemoryDrawer-ClJIqWck.js +1 -0
- package/web/dist/assets/WorkspaceTaskDrawer-gjvXuZ2K.js +1 -0
- package/web/dist/assets/addon-clipboard-wHJhZAA4.js +1 -0
- package/web/dist/assets/addon-fit-DX4qG4td.js +1 -0
- package/web/dist/assets/addon-unicode11-Bt8F3D7-.js +7 -0
- package/web/dist/assets/addon-web-links-DIbG5aQx.js +1 -0
- package/web/dist/assets/addon-webgl-DCtw1yLn.js +64 -0
- package/web/dist/assets/finder-C4Jmsb0B.png +0 -0
- package/web/dist/assets/ghostty-D-Js4rdm.png +0 -0
- package/web/dist/assets/index-bOMtwrkA.js +73 -0
- package/web/dist/assets/index-i9YgsMdD.css +1 -0
- package/web/dist/assets/path-join-7MR1s7b1.js +1 -0
- package/web/dist/assets/search-CmzeVhy0.js +1 -0
- package/web/dist/assets/xterm-B-qIQCd3.js +16 -0
- package/web/dist/assets/zed-C5BQT8X3.png +0 -0
- package/web/dist/bilibili.ico +0 -0
- package/web/dist/cli-icons/claude.png +0 -0
- package/web/dist/cli-icons/codex.png +0 -0
- package/web/dist/cli-icons/gemini.png +0 -0
- package/web/dist/cli-icons/hermes.png +0 -0
- package/web/dist/cli-icons/opencode.svg +18 -0
- package/web/dist/icons/apple-touch-icon-180.png +0 -0
- package/web/dist/icons/icon-192.png +0 -0
- package/web/dist/icons/icon-32.png +0 -0
- package/web/dist/icons/icon-512-maskable.png +0 -0
- package/web/dist/icons/icon-512.png +0 -0
- package/web/dist/index.html +39 -0
- package/web/dist/logo.png +0 -0
- package/web/dist/manifest.webmanifest +60 -0
- package/web/dist/screenshots/wide-overview.png +0 -0
- package/web/dist/sounds/LICENSE-KENNEY.txt +22 -0
- package/web/dist/sounds/hive-beacon.ogg +0 -0
- package/web/dist/sounds/hive-cascade.ogg +0 -0
- package/web/dist/sounds/hive-chime.ogg +0 -0
- package/web/dist/sounds/hive-ping.ogg +0 -0
- package/web/dist/sounds/hive-resolve.ogg +0 -0
- package/web/dist/sounds/hive-soft.ogg +0 -0
- package/web/dist/sw.js +99 -0
|
@@ -0,0 +1,791 @@
|
|
|
1
|
+
// The E2E/mux frame bridge — the trusted core of the remote tunnel.
|
|
2
|
+
//
|
|
3
|
+
// The tunnel controller (remote-tunnel.ts) only pumps opaque bytes between the gateway socket and
|
|
4
|
+
// this bridge. THIS file is where opaque phone frames become real loopback requests:
|
|
5
|
+
//
|
|
6
|
+
// inbound frame -> split header||ciphertext -> resolve device (trial-open against candidates)
|
|
7
|
+
// -> openNext (M1: AEAD integrity + seq replay guard) -> ONLY NOW trust the
|
|
8
|
+
// decoded header.kind + plaintext -> per-stream machine -> on Open, run the
|
|
9
|
+
// whitelist gate (classifyOpen) BEFORE any loopback socket -> bridge to
|
|
10
|
+
// 127.0.0.1:<port> (stamped with the per-boot secret) -> seal the response back
|
|
11
|
+
//
|
|
12
|
+
// SECURITY INVARIANTS enforced here:
|
|
13
|
+
// 1. Whitelist: classifyOpen runs on the OPENED Open meta; a non-whitelisted/non-canonical path is
|
|
14
|
+
// Reset(StreamRefused) + audited, NEVER a loopback request (the "not a general localhost proxy"
|
|
15
|
+
// gate). /api/ui/session is hard-denied.
|
|
16
|
+
// 2. Per-boot secret: every loopback request/upgrade is stamped via stampLoopbackHeaders with the
|
|
17
|
+
// injected loopbackSecret + the resolved deviceId; response heads are sanitized so Set-Cookie /
|
|
18
|
+
// x-hive-* never cross back to the phone.
|
|
19
|
+
// 5. E2E integrity: a frame that fails to open (tamper/replay/unknown device) is dropped + audited,
|
|
20
|
+
// NEVER bridged. deviceId is ONLY ever the result of a successful open — never a clear-text field.
|
|
21
|
+
// 6. Audit: this layer is the single collection point (http / ws_open / ws_input / reject).
|
|
22
|
+
import { request as httpRequest } from 'node:http';
|
|
23
|
+
import WebSocketClient from 'ws';
|
|
24
|
+
import { classifyOpen } from '../shared/remote-bridge-routing.js';
|
|
25
|
+
import { createOpener, createSealer, generateConnSalt as defaultGenerateConnSalt, deriveConnectionKeys, openNext, REMOTE_CRYPTO_VERSION, sealNext, } from '../shared/remote-crypto.js';
|
|
26
|
+
import { CHANNEL_STREAM_ID, CONN_SALT_STREAM_ID, createFlowController, createStreamMachine, decodeConnSalt, decodeHeader, decodeHttpData, decodeOpenPayload, decodeWsMessage, encodeConnSalt, encodeHeader, encodeHttpBodyChunk, encodeHttpHead, encodeResetPayload, encodeWsMessage, FrameKind, HEADER_BYTES, isConnSaltPayload, ResetCode, } from '../shared/remote-protocol.js';
|
|
27
|
+
import { DAEMON_OPEN_DIRECTION, DAEMON_SEAL_DIRECTION, } from './remote-device-session.js';
|
|
28
|
+
import { sanitizeTunnelResponseHeaders, stampLoopbackHeaders } from './remote-loopback-auth.js';
|
|
29
|
+
// ── loopback transport seam (real node:http / ws by default; stubbed in unit tests) ─────────────
|
|
30
|
+
const LOOPBACK_WS_PENDING_BYTES_LIMIT = 256 * 1024;
|
|
31
|
+
const realLoopbackTransports = {
|
|
32
|
+
openHttp(args, handlers) {
|
|
33
|
+
const req = httpRequest({
|
|
34
|
+
host: '127.0.0.1',
|
|
35
|
+
port: args.port,
|
|
36
|
+
method: args.method,
|
|
37
|
+
path: args.path,
|
|
38
|
+
headers: args.headers,
|
|
39
|
+
}, (res) => {
|
|
40
|
+
const headerList = [];
|
|
41
|
+
const raw = res.rawHeaders;
|
|
42
|
+
for (let i = 0; i + 1 < raw.length; i += 2) {
|
|
43
|
+
headerList.push([raw[i], raw[i + 1]]);
|
|
44
|
+
}
|
|
45
|
+
handlers.onHead({ status: res.statusCode ?? 0, headers: headerList });
|
|
46
|
+
res.on('data', (chunk) => handlers.onBody(new Uint8Array(chunk)));
|
|
47
|
+
res.on('end', () => handlers.onEnd());
|
|
48
|
+
res.on('error', (err) => handlers.onError(err));
|
|
49
|
+
});
|
|
50
|
+
req.on('error', (err) => handlers.onError(err));
|
|
51
|
+
return {
|
|
52
|
+
onData: (chunk) => {
|
|
53
|
+
req.write(Buffer.from(chunk));
|
|
54
|
+
},
|
|
55
|
+
onEnd: () => {
|
|
56
|
+
req.end();
|
|
57
|
+
},
|
|
58
|
+
abort: () => {
|
|
59
|
+
req.destroy();
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
openWs(args, handlers) {
|
|
64
|
+
const ws = new WebSocketClient(`ws://127.0.0.1:${args.port}${args.path}`, {
|
|
65
|
+
headers: args.headers,
|
|
66
|
+
});
|
|
67
|
+
let closed = false;
|
|
68
|
+
let queuedBytes = 0;
|
|
69
|
+
const queued = [];
|
|
70
|
+
const sendNow = (data, isText) => {
|
|
71
|
+
ws.send(data, { binary: !isText }, (err) => {
|
|
72
|
+
if (err)
|
|
73
|
+
handlers.onError(err);
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
const flushQueued = () => {
|
|
77
|
+
for (const item of queued.splice(0)) {
|
|
78
|
+
if (closed || ws.readyState !== WebSocketClient.OPEN)
|
|
79
|
+
break;
|
|
80
|
+
sendNow(item.data, item.isText);
|
|
81
|
+
}
|
|
82
|
+
queuedBytes = 0;
|
|
83
|
+
};
|
|
84
|
+
ws.binaryType = 'arraybuffer';
|
|
85
|
+
ws.on('open', () => {
|
|
86
|
+
handlers.onOpen();
|
|
87
|
+
flushQueued();
|
|
88
|
+
});
|
|
89
|
+
ws.on('message', (data, isBinary) => {
|
|
90
|
+
const buf = Buffer.isBuffer(data)
|
|
91
|
+
? data
|
|
92
|
+
: Array.isArray(data)
|
|
93
|
+
? Buffer.concat(data)
|
|
94
|
+
: Buffer.from(data);
|
|
95
|
+
handlers.onMessage(new Uint8Array(buf), !isBinary);
|
|
96
|
+
});
|
|
97
|
+
ws.on('close', () => {
|
|
98
|
+
closed = true;
|
|
99
|
+
queued.length = 0;
|
|
100
|
+
queuedBytes = 0;
|
|
101
|
+
handlers.onClose();
|
|
102
|
+
});
|
|
103
|
+
ws.on('error', (err) => handlers.onError(err));
|
|
104
|
+
return {
|
|
105
|
+
onData: (data, isText) => {
|
|
106
|
+
const buf = Buffer.from(data);
|
|
107
|
+
if (ws.readyState === WebSocketClient.OPEN) {
|
|
108
|
+
sendNow(buf, isText);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (closed || ws.readyState !== WebSocketClient.CONNECTING)
|
|
112
|
+
return;
|
|
113
|
+
queuedBytes += buf.byteLength;
|
|
114
|
+
if (queuedBytes > LOOPBACK_WS_PENDING_BYTES_LIMIT) {
|
|
115
|
+
closed = true;
|
|
116
|
+
queued.length = 0;
|
|
117
|
+
queuedBytes = 0;
|
|
118
|
+
handlers.onError(new Error('loopback websocket pending buffer exceeded'));
|
|
119
|
+
try {
|
|
120
|
+
ws.terminate();
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// already gone
|
|
124
|
+
}
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
queued.push({ data: buf, isText });
|
|
128
|
+
},
|
|
129
|
+
onClose: () => {
|
|
130
|
+
closed = true;
|
|
131
|
+
queued.length = 0;
|
|
132
|
+
queuedBytes = 0;
|
|
133
|
+
try {
|
|
134
|
+
ws.close();
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
// already gone
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
abort: () => {
|
|
141
|
+
closed = true;
|
|
142
|
+
queued.length = 0;
|
|
143
|
+
queuedBytes = 0;
|
|
144
|
+
try {
|
|
145
|
+
ws.terminate();
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
// already gone
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
const te = new TextEncoder();
|
|
155
|
+
const bytesEqual = (a, b) => {
|
|
156
|
+
if (a.length !== b.length)
|
|
157
|
+
return false;
|
|
158
|
+
for (let i = 0; i < a.length; i++)
|
|
159
|
+
if (a[i] !== b[i])
|
|
160
|
+
return false;
|
|
161
|
+
return true;
|
|
162
|
+
};
|
|
163
|
+
// /ws/terminal/<runId>/io -> /ws/terminal/<runId>/control (same clientId space, default 'legacy').
|
|
164
|
+
const TERMINAL_IO_RE = /^\/ws\/terminal\/([^/?]+)\/io$/;
|
|
165
|
+
const terminalControlPathFor = (ioPath) => {
|
|
166
|
+
const m = TERMINAL_IO_RE.exec(ioPath);
|
|
167
|
+
if (!m)
|
|
168
|
+
return null;
|
|
169
|
+
return `/ws/terminal/${m[1]}/control`;
|
|
170
|
+
};
|
|
171
|
+
// Reattach the WS query (clientId/cols/rows) — which rode the separate StreamMeta.ws.query field —
|
|
172
|
+
// onto a bare loopback path. Each key/value is percent-encoded so it lands as a normal searchParam.
|
|
173
|
+
const appendQuery = (path, query) => {
|
|
174
|
+
if (!query || query.length === 0)
|
|
175
|
+
return path;
|
|
176
|
+
const qs = query.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join('&');
|
|
177
|
+
return `${path}?${qs}`;
|
|
178
|
+
};
|
|
179
|
+
export const createFrameBridge = (ctx) => {
|
|
180
|
+
const transports = ctx.loopbackTransports ?? realLoopbackTransports;
|
|
181
|
+
const { audit } = ctx;
|
|
182
|
+
const genConnSalt = ctx.generateConnSalt ?? defaultGenerateConnSalt;
|
|
183
|
+
let send = null;
|
|
184
|
+
// Per-CONNECTION (per-socket) bilateral salts (M6.1). The daemon draws its salt at attachSocket; each
|
|
185
|
+
// phone's arrives as an UNSEALED ConnSalt on CONN_SALT_STREAM_ID. Both feed deriveConnectionKeys so
|
|
186
|
+
// every (re)connect / page reload gets fresh AEAD keys over the SAME persisted root — no nonce reuse.
|
|
187
|
+
let daemonConnSalt = null;
|
|
188
|
+
// The relay multiplexes N phones over one daemon socket, and their ConnSalts can interleave with
|
|
189
|
+
// their sealed Hellos. The daemon can't know which device owns an anonymous ConnSalt until that
|
|
190
|
+
// device's Hello trial-opens — so it keeps a small ring of recently-seen phone salts and binds a
|
|
191
|
+
// device by the (candidate root × pending salt) pair that opens its Hello. Once bound, the device's
|
|
192
|
+
// connKeys are FROZEN (HARDEN major 4) — a later phone's salt never re-keys an already-bound device.
|
|
193
|
+
const pendingPhoneSalts = [];
|
|
194
|
+
const MAX_PENDING_SALTS = 16;
|
|
195
|
+
const rememberPhoneSalt = (salt) => {
|
|
196
|
+
// Drop a byte-identical duplicate (a broadcast re-emit), keep most-recent-first, bound the ring.
|
|
197
|
+
const i = pendingPhoneSalts.findIndex((s) => bytesEqual(s, salt));
|
|
198
|
+
if (i >= 0)
|
|
199
|
+
pendingPhoneSalts.splice(i, 1);
|
|
200
|
+
pendingPhoneSalts.unshift(salt);
|
|
201
|
+
if (pendingPhoneSalts.length > MAX_PENDING_SALTS)
|
|
202
|
+
pendingPhoneSalts.length = MAX_PENDING_SALTS;
|
|
203
|
+
};
|
|
204
|
+
// Per-device crypto state (connKeys/opener/sealer). deviceId is only ever set after a successful open.
|
|
205
|
+
const devices = new Map();
|
|
206
|
+
// Salts this daemon socket has already accepted for a device and then replaced. The daemon salt is
|
|
207
|
+
// fixed for the socket, so accepting an old phone salt again would recreate an old connKey and
|
|
208
|
+
// rewind seq. Clear this on every daemon socket attach, where the daemon salt rotates.
|
|
209
|
+
const retiredPhoneSalts = new Map();
|
|
210
|
+
// The demux table is keyed by (deviceId, streamId) — NOT streamId alone — so two devices reusing
|
|
211
|
+
// the same odd streamId never collide (M4 multi-device prerequisite, HARDEN minor 4c).
|
|
212
|
+
const streams = new Map();
|
|
213
|
+
// streamId -> deviceId binding, learned on the first successfully-opened frame for a stream.
|
|
214
|
+
const streamOwner = new Map();
|
|
215
|
+
const streamKey = (deviceId, streamId) => `${deviceId}${streamId}`;
|
|
216
|
+
// Derive a candidate DeviceState for (session, phoneConnSalt) WITHOUT caching it. Used to trial-open
|
|
217
|
+
// an unbound frame against each (root × pending salt) pair; only the pair that opens gets committed
|
|
218
|
+
// into `devices` (freezing the device's connKeys for the rest of the connection).
|
|
219
|
+
const deriveDeviceState = (session, phoneConnSalt) => {
|
|
220
|
+
if (!daemonConnSalt)
|
|
221
|
+
return null;
|
|
222
|
+
const connKeys = deriveConnectionKeys({
|
|
223
|
+
rootD2p: session.keys.d2p,
|
|
224
|
+
rootP2d: session.keys.p2d,
|
|
225
|
+
phoneConnSalt,
|
|
226
|
+
daemonConnSalt,
|
|
227
|
+
ids: {
|
|
228
|
+
daemonId: ctx.daemonId,
|
|
229
|
+
deviceId: session.deviceId,
|
|
230
|
+
protocolVersion: REMOTE_CRYPTO_VERSION,
|
|
231
|
+
},
|
|
232
|
+
});
|
|
233
|
+
return {
|
|
234
|
+
session,
|
|
235
|
+
connKeys,
|
|
236
|
+
phoneConnSalt,
|
|
237
|
+
opener: createOpener(DAEMON_OPEN_DIRECTION),
|
|
238
|
+
sealer: createSealer(DAEMON_SEAL_DIRECTION),
|
|
239
|
+
};
|
|
240
|
+
};
|
|
241
|
+
// Emit the UNSEALED daemon ConnSalt on CONN_SALT_STREAM_ID so the phone can derive the connKeys.
|
|
242
|
+
const emitDaemonSalt = () => {
|
|
243
|
+
if (!send || !daemonConnSalt)
|
|
244
|
+
return;
|
|
245
|
+
const header = encodeHeader({
|
|
246
|
+
version: REMOTE_CRYPTO_VERSION,
|
|
247
|
+
kind: FrameKind.Data,
|
|
248
|
+
flags: 0,
|
|
249
|
+
streamId: CONN_SALT_STREAM_ID,
|
|
250
|
+
seq: 0,
|
|
251
|
+
});
|
|
252
|
+
const body = encodeConnSalt({ role: 'daemon', salt: daemonConnSalt });
|
|
253
|
+
const frame = new Uint8Array(header.length + body.length);
|
|
254
|
+
frame.set(header, 0);
|
|
255
|
+
frame.set(body, header.length);
|
|
256
|
+
send(frame);
|
|
257
|
+
};
|
|
258
|
+
// Seal an outbound daemon->phone frame for a device's stream and push it onto the socket.
|
|
259
|
+
const sendFrame = (st, kind, streamId, payload, flags = 0) => {
|
|
260
|
+
if (!send)
|
|
261
|
+
return;
|
|
262
|
+
const headerBytes = encodeHeader({
|
|
263
|
+
version: REMOTE_CRYPTO_VERSION,
|
|
264
|
+
kind,
|
|
265
|
+
flags,
|
|
266
|
+
streamId,
|
|
267
|
+
seq: st.sealer.nextSeq,
|
|
268
|
+
});
|
|
269
|
+
// Observe the REAL connKey + header (no-reuse / no-downgrade invariant, mutation-tested). NOT a
|
|
270
|
+
// mock — sealNext still runs on the same key below; the root NEVER reaches this call.
|
|
271
|
+
ctx.onSeal?.({
|
|
272
|
+
key: st.connKeys[DAEMON_SEAL_DIRECTION],
|
|
273
|
+
direction: DAEMON_SEAL_DIRECTION,
|
|
274
|
+
headerBytes,
|
|
275
|
+
});
|
|
276
|
+
const { ciphertext } = sealNext(st.sealer, {
|
|
277
|
+
key: st.connKeys[DAEMON_SEAL_DIRECTION],
|
|
278
|
+
streamId,
|
|
279
|
+
headerBytes,
|
|
280
|
+
payload,
|
|
281
|
+
});
|
|
282
|
+
const out = new Uint8Array(headerBytes.length + ciphertext.length);
|
|
283
|
+
out.set(headerBytes, 0);
|
|
284
|
+
out.set(ciphertext, headerBytes.length);
|
|
285
|
+
send(out);
|
|
286
|
+
};
|
|
287
|
+
const sendReset = (st, streamId, code) => {
|
|
288
|
+
sendFrame(st, FrameKind.Reset, streamId, encodeResetPayload(code));
|
|
289
|
+
};
|
|
290
|
+
// Emit a terminal output_ack on the companion control socket for `bytes` drained off the io socket.
|
|
291
|
+
const emitIoSelfAck = (sb, bytes) => {
|
|
292
|
+
if (!sb.ioAckControl || bytes <= 0)
|
|
293
|
+
return;
|
|
294
|
+
sb.ioAckControl.onData(te.encode(JSON.stringify({ type: 'output_ack', bytes })), true);
|
|
295
|
+
};
|
|
296
|
+
// Self-ack the loopback io socket for bytes we just drained, but ONLY while the daemon->phone window
|
|
297
|
+
// could absorb them. `fitWindow` is the trySend result for this chunk: when it fit, the bytes are
|
|
298
|
+
// within the unacked budget so we ack them locally; when it did NOT fit (the phone is behind / never
|
|
299
|
+
// acks) we withhold the self-ack so the server's UNACKED_HIGH_WATER pauses the PTY — that local pause
|
|
300
|
+
// is what bounds daemon memory. Withheld bytes are released once a phone Ack frees the window.
|
|
301
|
+
const releaseIoSelfAck = (sb, justDrained, fitWindow) => {
|
|
302
|
+
if (!sb.ioAckControl)
|
|
303
|
+
return;
|
|
304
|
+
if (!fitWindow) {
|
|
305
|
+
sb.ioPendingSelfAck += justDrained;
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
// Window had room: ack the bytes we just drained plus any we'd withheld earlier.
|
|
309
|
+
const release = justDrained + sb.ioPendingSelfAck;
|
|
310
|
+
sb.ioPendingSelfAck = 0;
|
|
311
|
+
emitIoSelfAck(sb, release);
|
|
312
|
+
};
|
|
313
|
+
const tearDownStream = (key, reason) => {
|
|
314
|
+
const sb = streams.get(key);
|
|
315
|
+
if (!sb)
|
|
316
|
+
return;
|
|
317
|
+
sb.closed = true;
|
|
318
|
+
if (sb.http)
|
|
319
|
+
sb.http.abort();
|
|
320
|
+
if (sb.ws) {
|
|
321
|
+
if (reason === 'reset')
|
|
322
|
+
sb.ws.abort();
|
|
323
|
+
else
|
|
324
|
+
sb.ws.onClose();
|
|
325
|
+
}
|
|
326
|
+
if (sb.ioAckControl) {
|
|
327
|
+
if (reason === 'reset')
|
|
328
|
+
sb.ioAckControl.abort();
|
|
329
|
+
else
|
|
330
|
+
sb.ioAckControl.onClose();
|
|
331
|
+
}
|
|
332
|
+
streams.delete(key);
|
|
333
|
+
streamOwner.delete(extractStreamId(key));
|
|
334
|
+
};
|
|
335
|
+
const extractStreamId = (key) => Number(key.split('')[1]);
|
|
336
|
+
const retirePhoneSalt = (deviceId, salt) => {
|
|
337
|
+
const retired = retiredPhoneSalts.get(deviceId) ?? [];
|
|
338
|
+
if (!retired.some((s) => bytesEqual(s, salt)))
|
|
339
|
+
retired.unshift(Uint8Array.from(salt));
|
|
340
|
+
if (retired.length > MAX_PENDING_SALTS)
|
|
341
|
+
retired.length = MAX_PENDING_SALTS;
|
|
342
|
+
retiredPhoneSalts.set(deviceId, retired);
|
|
343
|
+
};
|
|
344
|
+
const hasRetiredPhoneSalt = (deviceId, salt) => retiredPhoneSalts.get(deviceId)?.some((s) => bytesEqual(s, salt)) ?? false;
|
|
345
|
+
const tearDownDeviceStreams = (deviceId) => {
|
|
346
|
+
for (const key of [...streams.keys()]) {
|
|
347
|
+
const sb = streams.get(key);
|
|
348
|
+
if (!sb || sb.deviceId !== deviceId)
|
|
349
|
+
continue;
|
|
350
|
+
tearDownStream(key, 'reset');
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
// Resolve the device for a frame on `streamId`. Three tiers:
|
|
354
|
+
// 1. Stream already bound → its owner's FROZEN connKeys.
|
|
355
|
+
// 2. Any ALREADY-BOUND device (cached connKeys) — covers H-NET-4 (two devices share an odd id).
|
|
356
|
+
// 3. UNBOUND: trial-open against each (candidate root × pending phone salt). The pair that opens
|
|
357
|
+
// identifies (device, this connection's salt); commit it into `devices`, freezing its connKeys.
|
|
358
|
+
// Only the open authenticates — deviceId is never read from a clear-text field (invariant 5).
|
|
359
|
+
const resolveAndOpen = (streamId, seq, headerBytes, ciphertext) => {
|
|
360
|
+
const tryOpen = (st) => {
|
|
361
|
+
try {
|
|
362
|
+
return openNext(st.opener, {
|
|
363
|
+
key: st.connKeys[DAEMON_OPEN_DIRECTION],
|
|
364
|
+
streamId,
|
|
365
|
+
headerBytes,
|
|
366
|
+
ciphertext,
|
|
367
|
+
seq,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
catch {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
const boundDeviceId = streamOwner.get(streamId);
|
|
375
|
+
if (boundDeviceId !== undefined) {
|
|
376
|
+
const st = devices.get(boundDeviceId);
|
|
377
|
+
// H-NET-4: a frame that fails against the bound owner is likely a DIFFERENT device reusing the
|
|
378
|
+
// same odd id — fall through to the cached/candidate scan instead of black-holing it. A genuine
|
|
379
|
+
// tamper/replay still fails everywhere below and is dropped.
|
|
380
|
+
if (st) {
|
|
381
|
+
const pt = tryOpen(st);
|
|
382
|
+
if (pt)
|
|
383
|
+
return { device: st, plaintext: pt };
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
// Tier 2: any other already-bound device (frozen connKeys). Never re-derives, so device A is never
|
|
387
|
+
// desynced when device B handshakes on the same socket (HARDEN major 4).
|
|
388
|
+
for (const st of devices.values()) {
|
|
389
|
+
if (st.session.deviceId === boundDeviceId)
|
|
390
|
+
continue;
|
|
391
|
+
const pt = tryOpen(st);
|
|
392
|
+
if (pt) {
|
|
393
|
+
if (streamId !== CHANNEL_STREAM_ID)
|
|
394
|
+
streamOwner.set(streamId, st.session.deviceId);
|
|
395
|
+
return { device: st, plaintext: pt };
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
// Tier 3: UNBOUND — trial-open each candidate ROOT against each pending phone salt. The matching
|
|
399
|
+
// (device, salt) pair commits + freezes. Requires both salts known (else no connKey can exist).
|
|
400
|
+
if (!daemonConnSalt || pendingPhoneSalts.length === 0)
|
|
401
|
+
return null;
|
|
402
|
+
for (const session of ctx.deviceSessions.candidates()) {
|
|
403
|
+
const existing = devices.get(session.deviceId);
|
|
404
|
+
for (const salt of pendingPhoneSalts) {
|
|
405
|
+
if (existing && bytesEqual(existing.phoneConnSalt, salt))
|
|
406
|
+
continue;
|
|
407
|
+
if (hasRetiredPhoneSalt(session.deviceId, salt))
|
|
408
|
+
continue;
|
|
409
|
+
const st = deriveDeviceState(session, salt);
|
|
410
|
+
if (!st)
|
|
411
|
+
continue;
|
|
412
|
+
const pt = tryOpen(st);
|
|
413
|
+
if (pt) {
|
|
414
|
+
if (existing) {
|
|
415
|
+
retirePhoneSalt(session.deviceId, existing.phoneConnSalt);
|
|
416
|
+
tearDownDeviceStreams(session.deviceId);
|
|
417
|
+
}
|
|
418
|
+
devices.set(session.deviceId, st); // freeze this device's connKeys for the connection
|
|
419
|
+
if (streamId !== CHANNEL_STREAM_ID)
|
|
420
|
+
streamOwner.set(streamId, session.deviceId);
|
|
421
|
+
return { device: st, plaintext: pt };
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return null;
|
|
426
|
+
};
|
|
427
|
+
// ── per-kind handling AFTER a successful open (the header is now trusted) ─────────────────────
|
|
428
|
+
const onOpenFrame = (st, streamId, plaintext) => {
|
|
429
|
+
const deviceId = st.session.deviceId;
|
|
430
|
+
let meta;
|
|
431
|
+
try {
|
|
432
|
+
meta = decodeOpenPayload(plaintext);
|
|
433
|
+
}
|
|
434
|
+
catch {
|
|
435
|
+
sendReset(st, streamId, ResetCode.ProtocolError);
|
|
436
|
+
audit.enqueue({
|
|
437
|
+
action: 'reject',
|
|
438
|
+
result: 'rejected',
|
|
439
|
+
rejectReason: 'malformed_meta',
|
|
440
|
+
deviceId,
|
|
441
|
+
});
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
const decision = classifyOpen(meta);
|
|
445
|
+
if (!decision.ok) {
|
|
446
|
+
sendReset(st, streamId, ResetCode.StreamRefused);
|
|
447
|
+
audit.enqueue({
|
|
448
|
+
action: 'reject',
|
|
449
|
+
result: 'rejected',
|
|
450
|
+
rejectReason: decision.reason,
|
|
451
|
+
endpoint: meta.http?.path ?? meta.ws?.path ?? null,
|
|
452
|
+
deviceId,
|
|
453
|
+
});
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
const key = streamKey(deviceId, streamId);
|
|
457
|
+
const machine = createStreamMachine();
|
|
458
|
+
machine.onRecv(FrameKind.Open);
|
|
459
|
+
const sb = {
|
|
460
|
+
deviceId,
|
|
461
|
+
transport: decision.transport,
|
|
462
|
+
path: decision.path,
|
|
463
|
+
machine,
|
|
464
|
+
recvFlow: createFlowController(),
|
|
465
|
+
sendFlow: createFlowController(),
|
|
466
|
+
ioPendingSelfAck: 0,
|
|
467
|
+
closed: false,
|
|
468
|
+
};
|
|
469
|
+
streams.set(key, sb);
|
|
470
|
+
// HTTP carries a header list in its meta; WS does not (the loopback upgrade only needs the
|
|
471
|
+
// stamped tunnel headers). Either way we strip any phone-supplied tunnel-header copies.
|
|
472
|
+
const headers = stampLoopbackHeaders(meta.http?.headers ?? [], ctx.loopbackSecret, deviceId);
|
|
473
|
+
if (decision.transport === 'http') {
|
|
474
|
+
sb.http = transports.openHttp({ port: ctx.loopbackPort, method: decision.method, path: decision.path, headers }, {
|
|
475
|
+
onHead: (head) => {
|
|
476
|
+
if (sb.closed)
|
|
477
|
+
return;
|
|
478
|
+
const safe = {
|
|
479
|
+
status: head.status,
|
|
480
|
+
headers: sanitizeTunnelResponseHeaders(head.headers),
|
|
481
|
+
};
|
|
482
|
+
sendFrame(st, FrameKind.Data, streamId, encodeHttpHead(safe));
|
|
483
|
+
},
|
|
484
|
+
onBody: (chunk) => {
|
|
485
|
+
if (sb.closed)
|
|
486
|
+
return;
|
|
487
|
+
sendFrame(st, FrameKind.Data, streamId, encodeHttpBodyChunk(chunk));
|
|
488
|
+
},
|
|
489
|
+
onEnd: () => {
|
|
490
|
+
if (sb.closed)
|
|
491
|
+
return;
|
|
492
|
+
sb.closed = true;
|
|
493
|
+
sendFrame(st, FrameKind.End, streamId, new Uint8Array(0));
|
|
494
|
+
audit.enqueue({ action: 'http', result: 'ok', endpoint: sb.path, deviceId });
|
|
495
|
+
streams.delete(key);
|
|
496
|
+
streamOwner.delete(streamId);
|
|
497
|
+
},
|
|
498
|
+
onError: () => {
|
|
499
|
+
if (sb.closed)
|
|
500
|
+
return;
|
|
501
|
+
sb.closed = true;
|
|
502
|
+
sendReset(st, streamId, ResetCode.InternalError);
|
|
503
|
+
audit.enqueue({ action: 'http', result: 'error', endpoint: sb.path, deviceId });
|
|
504
|
+
streams.delete(key);
|
|
505
|
+
streamOwner.delete(streamId);
|
|
506
|
+
},
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
// The WS query (clientId/cols/rows) rode the separate decision.query field (classifyOpen keeps
|
|
511
|
+
// the path bare). Reattach it onto the loopback URL so terminal-ws-server reads clientId/cols/
|
|
512
|
+
// rows from url.searchParams exactly as a same-origin upgrade does.
|
|
513
|
+
const loopbackWsPath = appendQuery(decision.path, decision.query);
|
|
514
|
+
// Terminal io self-ack: open a companion loopback CONTROL socket so the bridge can emit
|
|
515
|
+
// output_ack as it drains PTY output (HARDEN major). The companion shares the io stream's
|
|
516
|
+
// clientId space (default 'legacy'), so its acks decrement the same viewer's UNACKED counter —
|
|
517
|
+
// so it must carry the SAME query (the clientId) as the io socket.
|
|
518
|
+
const controlPath = terminalControlPathFor(decision.path);
|
|
519
|
+
if (controlPath) {
|
|
520
|
+
sb.ioAckControl = transports.openWs({ port: ctx.loopbackPort, path: appendQuery(controlPath, decision.query), headers }, {
|
|
521
|
+
onOpen: () => { },
|
|
522
|
+
// The control socket receives restore/exit/error frames; the bridge ignores them for
|
|
523
|
+
// self-ack purposes (the phone's real control stream, if any, carries those separately).
|
|
524
|
+
onMessage: () => { },
|
|
525
|
+
onClose: () => { },
|
|
526
|
+
onError: () => { },
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
sb.ws = transports.openWs({ port: ctx.loopbackPort, path: loopbackWsPath, headers }, {
|
|
530
|
+
onOpen: () => {
|
|
531
|
+
audit.enqueue({ action: 'ws_open', result: 'ok', endpoint: sb.path, deviceId });
|
|
532
|
+
},
|
|
533
|
+
onMessage: (data, isText) => {
|
|
534
|
+
if (sb.closed)
|
|
535
|
+
return;
|
|
536
|
+
// The bytes are already in RAM (the loopback handed them to us); forward them now so the
|
|
537
|
+
// phone gets every byte in order. The sender window doesn't gate the forward — it gates
|
|
538
|
+
// the SELF-ACK, which is the real backpressure lever: withholding it leaves the bytes
|
|
539
|
+
// unacked on the server so UNACKED_HIGH_WATER pauses the PTY (no more chunks arrive).
|
|
540
|
+
const fit = sb.sendFlow.trySend(data.length).ok;
|
|
541
|
+
sendFrame(st, FrameKind.Data, streamId, encodeWsMessage(data, isText));
|
|
542
|
+
// Self-ack the bytes we drained ONLY while the daemon->phone window could absorb them. When
|
|
543
|
+
// the phone falls behind (never acks) the window stays exhausted, the self-ack is withheld
|
|
544
|
+
// (accumulated in ioPendingSelfAck), and the local pause backstops daemon memory. A phone
|
|
545
|
+
// Ack (onAckFrame -> applyAck) releases the withheld self-acks and resumes the PTY.
|
|
546
|
+
releaseIoSelfAck(sb, data.length, fit);
|
|
547
|
+
},
|
|
548
|
+
onClose: () => {
|
|
549
|
+
if (sb.closed)
|
|
550
|
+
return;
|
|
551
|
+
sb.closed = true;
|
|
552
|
+
sb.ioAckControl?.onClose();
|
|
553
|
+
sendFrame(st, FrameKind.End, streamId, new Uint8Array(0));
|
|
554
|
+
streams.delete(key);
|
|
555
|
+
streamOwner.delete(streamId);
|
|
556
|
+
},
|
|
557
|
+
onError: () => {
|
|
558
|
+
if (sb.closed)
|
|
559
|
+
return;
|
|
560
|
+
sb.closed = true;
|
|
561
|
+
sb.ioAckControl?.abort();
|
|
562
|
+
sendReset(st, streamId, ResetCode.InternalError);
|
|
563
|
+
streams.delete(key);
|
|
564
|
+
streamOwner.delete(streamId);
|
|
565
|
+
},
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
const onDataFrame = (st, streamId, plaintext) => {
|
|
570
|
+
const key = streamKey(st.session.deviceId, streamId);
|
|
571
|
+
const sb = streams.get(key);
|
|
572
|
+
if (!sb || sb.closed)
|
|
573
|
+
return;
|
|
574
|
+
const recv = sb.machine.onRecv(FrameKind.Data);
|
|
575
|
+
if (!recv.ok) {
|
|
576
|
+
sendReset(st, streamId, recv.reset);
|
|
577
|
+
tearDownStream(key, 'reset');
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
// M1 receiver flow: ack consumed bytes so the phone can keep sending large bodies/pastes.
|
|
581
|
+
const ack = sb.recvFlow.onConsume(plaintext.length);
|
|
582
|
+
if (ack) {
|
|
583
|
+
// Ack rides the channel as an Ack frame on this stream id (cumulative byte count).
|
|
584
|
+
const payload = new Uint8Array(4);
|
|
585
|
+
new DataView(payload.buffer).setUint32(0, ack.ackCumulative);
|
|
586
|
+
sendFrame(st, FrameKind.Ack, streamId, payload);
|
|
587
|
+
}
|
|
588
|
+
if (sb.transport === 'http') {
|
|
589
|
+
const chunk = decodeHttpData(plaintext);
|
|
590
|
+
if (chunk.kind === 'body')
|
|
591
|
+
sb.http?.onData(chunk.data);
|
|
592
|
+
// a 'head' on the request side is illegal; ignore.
|
|
593
|
+
}
|
|
594
|
+
else {
|
|
595
|
+
const msg = decodeWsMessage(plaintext);
|
|
596
|
+
audit.enqueue({
|
|
597
|
+
action: 'ws_input',
|
|
598
|
+
result: 'ok',
|
|
599
|
+
endpoint: sb.path,
|
|
600
|
+
deviceId: st.session.deviceId,
|
|
601
|
+
byteCount: msg.data.length,
|
|
602
|
+
preview: msg.isText ? new TextDecoder().decode(msg.data) : null,
|
|
603
|
+
});
|
|
604
|
+
sb.ws?.onData(msg.data, msg.isText);
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
const onEndFrame = (st, streamId) => {
|
|
608
|
+
const key = streamKey(st.session.deviceId, streamId);
|
|
609
|
+
const sb = streams.get(key);
|
|
610
|
+
if (!sb || sb.closed)
|
|
611
|
+
return;
|
|
612
|
+
sb.machine.onRecv(FrameKind.End);
|
|
613
|
+
// Phone finished its half (request body / outbound ws). Tell loopback the input is done.
|
|
614
|
+
if (sb.transport === 'http')
|
|
615
|
+
sb.http?.onEnd();
|
|
616
|
+
else
|
|
617
|
+
sb.ws?.onClose();
|
|
618
|
+
};
|
|
619
|
+
const onResetFrame = (st, streamId) => {
|
|
620
|
+
const key = streamKey(st.session.deviceId, streamId);
|
|
621
|
+
tearDownStream(key, 'reset');
|
|
622
|
+
};
|
|
623
|
+
const onAckFrame = (st, streamId, plaintext) => {
|
|
624
|
+
// M6 sender-window ack from the phone (VULN-RELIABILITY-1). The Ack payload is a 4-byte cumulative
|
|
625
|
+
// byte count. applyAck advances the window; when it RESUMES (the phone caught up enough to free the
|
|
626
|
+
// window) we release the self-acks we withheld so the server's terminal flow control un-pauses the
|
|
627
|
+
// PTY. Without this the daemon either grows memory unbounded (no window) or deadlocks (a window
|
|
628
|
+
// that never resumes).
|
|
629
|
+
const sb = streams.get(streamKey(st.session.deviceId, streamId));
|
|
630
|
+
if (!sb)
|
|
631
|
+
return;
|
|
632
|
+
if (plaintext.length < 4)
|
|
633
|
+
return;
|
|
634
|
+
const cumulative = new DataView(plaintext.buffer, plaintext.byteOffset, plaintext.byteLength).getUint32(0);
|
|
635
|
+
const { resumed } = sb.sendFlow.applyAck(cumulative);
|
|
636
|
+
if (resumed && sb.ioPendingSelfAck > 0) {
|
|
637
|
+
const release = sb.ioPendingSelfAck;
|
|
638
|
+
sb.ioPendingSelfAck = 0;
|
|
639
|
+
emitIoSelfAck(sb, release);
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
const handleOpened = (st, header, plaintext) => {
|
|
643
|
+
if (header.streamId === CHANNEL_STREAM_ID) {
|
|
644
|
+
// Hello / channel control. Opening it bound the device; nothing else to bridge in M3.
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
switch (header.kind) {
|
|
648
|
+
case FrameKind.Open:
|
|
649
|
+
onOpenFrame(st, header.streamId, plaintext);
|
|
650
|
+
break;
|
|
651
|
+
case FrameKind.Data:
|
|
652
|
+
onDataFrame(st, header.streamId, plaintext);
|
|
653
|
+
break;
|
|
654
|
+
case FrameKind.End:
|
|
655
|
+
onEndFrame(st, header.streamId);
|
|
656
|
+
break;
|
|
657
|
+
case FrameKind.Reset:
|
|
658
|
+
onResetFrame(st, header.streamId);
|
|
659
|
+
break;
|
|
660
|
+
case FrameKind.Ack:
|
|
661
|
+
onAckFrame(st, header.streamId, plaintext);
|
|
662
|
+
break;
|
|
663
|
+
case FrameKind.Ping:
|
|
664
|
+
break;
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
return {
|
|
668
|
+
attachSocket(sink) {
|
|
669
|
+
send = sink;
|
|
670
|
+
// A fresh socket = a fresh connection. Draw a fresh daemon salt, drop any prior connection state
|
|
671
|
+
// (per-device connKeys/opener/sealer + stream bindings + pending phone salts), and announce the
|
|
672
|
+
// salt UNSEALED so each phone can derive its connKeys. This re-keys on every daemon reconnect for
|
|
673
|
+
// free (the controller rebuilds the bridge per socket open). Each phone's salt arrives next as an
|
|
674
|
+
// inbound ConnSalt.
|
|
675
|
+
daemonConnSalt = genConnSalt();
|
|
676
|
+
pendingPhoneSalts.length = 0;
|
|
677
|
+
devices.clear();
|
|
678
|
+
retiredPhoneSalts.clear();
|
|
679
|
+
streamOwner.clear();
|
|
680
|
+
emitDaemonSalt();
|
|
681
|
+
},
|
|
682
|
+
onInbound(frame) {
|
|
683
|
+
const bytes = frame instanceof ArrayBuffer ? new Uint8Array(frame) : frame;
|
|
684
|
+
if (bytes.byteLength < HEADER_BYTES) {
|
|
685
|
+
audit.enqueue({ action: 'reject', result: 'rejected', rejectReason: 'short frame' });
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
const headerBytes = bytes.subarray(0, HEADER_BYTES);
|
|
689
|
+
const ciphertext = bytes.subarray(HEADER_BYTES);
|
|
690
|
+
let header;
|
|
691
|
+
try {
|
|
692
|
+
header = decodeHeader(headerBytes);
|
|
693
|
+
}
|
|
694
|
+
catch {
|
|
695
|
+
// No trusted streamId to Reset against — drop + audit (invariant 5).
|
|
696
|
+
audit.enqueue({ action: 'reject', result: 'rejected', rejectReason: 'bad_header' });
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
// UNSEALED phone ConnSalt (M6.1) — handled BEFORE any open. We branch on the cleartext streamId
|
|
700
|
+
// (CONN_SALT_STREAM_ID), never on a payload byte (a sealed frame's byte 0 is uniform-random
|
|
701
|
+
// ciphertext). The salt is PUBLIC (HKDF salt needs no secrecy); device AUTHENTICATION is the
|
|
702
|
+
// sealed Hello trial-open under the derived connKey, not this frame.
|
|
703
|
+
if (header.streamId === CONN_SALT_STREAM_ID) {
|
|
704
|
+
if (!isConnSaltPayload(ciphertext)) {
|
|
705
|
+
audit.enqueue({ action: 'reject', result: 'rejected', rejectReason: 'bad_header' });
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
let msg;
|
|
709
|
+
try {
|
|
710
|
+
msg = decodeConnSalt(ciphertext);
|
|
711
|
+
}
|
|
712
|
+
catch {
|
|
713
|
+
audit.enqueue({ action: 'reject', result: 'rejected', rejectReason: 'bad_header' });
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
if (msg.role === 'device') {
|
|
717
|
+
// Remember this phone's salt as a CANDIDATE for the next unbound device to bind under. We do
|
|
718
|
+
// NOT clear the device cache (HARDEN major 4): the relay multiplexes N phones over one socket,
|
|
719
|
+
// so wiping every device's frozen connKeys on a second phone's ConnSalt would desync the
|
|
720
|
+
// already-bound ones. The (root × pending salt) trial-open in resolveAndOpen pairs a salt to
|
|
721
|
+
// the right device when its Hello arrives — so interleaved phone handshakes can't cross-bind.
|
|
722
|
+
// Re-emit the daemon salt so a phone that began its channel on an already-open socket (no
|
|
723
|
+
// fresh attachSocket) gets the bytes it needs to derive.
|
|
724
|
+
rememberPhoneSalt(msg.salt);
|
|
725
|
+
emitDaemonSalt();
|
|
726
|
+
}
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
const opened = resolveAndOpen(header.streamId, header.seq, headerBytes, ciphertext);
|
|
730
|
+
if (!opened) {
|
|
731
|
+
// AEAD/seq failure OR no device key. We never trust an unopened frame's routing, so we do
|
|
732
|
+
// NOT emit a Reset (we cannot seal one for an unauthenticated stream) — just drop + audit.
|
|
733
|
+
const known = streamOwner.has(header.streamId) || ctx.deviceSessions.candidates().length > 0;
|
|
734
|
+
audit.enqueue({
|
|
735
|
+
action: 'reject',
|
|
736
|
+
result: 'rejected',
|
|
737
|
+
rejectReason: known ? 'open_failed' : 'no_session',
|
|
738
|
+
});
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
handleOpened(opened.device, header, opened.plaintext);
|
|
742
|
+
},
|
|
743
|
+
resetAllStreams(reason, opts) {
|
|
744
|
+
const keepSink = opts?.keepSink ?? false;
|
|
745
|
+
for (const key of [...streams.keys()]) {
|
|
746
|
+
const sb = streams.get(key);
|
|
747
|
+
if (sb && !sb.closed) {
|
|
748
|
+
const st = devices.get(sb.deviceId);
|
|
749
|
+
if (st && send) {
|
|
750
|
+
try {
|
|
751
|
+
sendReset(st, extractStreamId(key), ResetCode.InternalError);
|
|
752
|
+
}
|
|
753
|
+
catch {
|
|
754
|
+
// socket gone; just tear down locally
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
tearDownStream(key, 'reset');
|
|
759
|
+
}
|
|
760
|
+
if (keepSink) {
|
|
761
|
+
// peer-offline: the socket is still up. Reset the streams, audit it as a stream-reset, but
|
|
762
|
+
// leave the sink intact so the phone can re-open streams on the same socket once it's back.
|
|
763
|
+
// NOT a session_close — no session/socket closed (the controller keeps the daemon socket).
|
|
764
|
+
audit.enqueue({ action: 'reject', result: 'rejected', rejectReason: reason });
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
audit.enqueue({ action: 'session_close', result: 'ok', rejectReason: reason });
|
|
768
|
+
send = null;
|
|
769
|
+
},
|
|
770
|
+
closeDevice(deviceId, reason) {
|
|
771
|
+
for (const key of [...streams.keys()]) {
|
|
772
|
+
const sb = streams.get(key);
|
|
773
|
+
if (!sb || sb.deviceId !== deviceId)
|
|
774
|
+
continue;
|
|
775
|
+
const st = devices.get(deviceId);
|
|
776
|
+
if (st && send) {
|
|
777
|
+
try {
|
|
778
|
+
sendReset(st, extractStreamId(key), ResetCode.InternalError);
|
|
779
|
+
}
|
|
780
|
+
catch {
|
|
781
|
+
// socket gone; tear down locally below
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
tearDownStream(key, 'reset');
|
|
785
|
+
}
|
|
786
|
+
// Drop the per-device opener/sealer so a re-pair of the same id starts clean (no stale seq).
|
|
787
|
+
devices.delete(deviceId);
|
|
788
|
+
audit.enqueue({ action: 'revoke', deviceId, result: 'ok', rejectReason: reason });
|
|
789
|
+
},
|
|
790
|
+
};
|
|
791
|
+
};
|