@minionry/minion 0.7.36
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/LICENSE +14 -0
- package/PRIVACY.md +357 -0
- package/README.md +57 -0
- package/THIRD_PARTY_NOTICES.md +245 -0
- package/bin/branding.js +4 -0
- package/bin/commands/browser-install.js +483 -0
- package/bin/commands/create-sandbox.js +542 -0
- package/bin/commands/fingerprint.js +101 -0
- package/bin/commands/login.js +400 -0
- package/bin/commands/logout.js +93 -0
- package/bin/commands/minions.js +211 -0
- package/bin/commands/sandboxes.js +309 -0
- package/bin/commands/status.js +298 -0
- package/bin/commands/whoami.js +153 -0
- package/bin/launch-env.js +35 -0
- package/bin/machine-identity.js +221 -0
- package/bin/machine-layout.js +200 -0
- package/bin/minion.js +839 -0
- package/bin/platform-endpoints.js +8 -0
- package/bin/postinstall.js +52 -0
- package/bin/pty-doctor.js +326 -0
- package/bin/pty-locator.js +110 -0
- package/bin/pty-provision.js +186 -0
- package/bin/sandbox/Dockerfile +78 -0
- package/bin/sandbox/entrypoint.sh +14 -0
- package/bin/sandbox/install-repo.sh +53 -0
- package/bin/sandbox/seccomp-userns.json +883 -0
- package/bin/sandbox-config.js +306 -0
- package/dist/bin/machine-identity.js +221 -0
- package/dist/bin/machine-layout.js +200 -0
- package/dist/electron-host/agent-lease.js +141 -0
- package/dist/electron-host/dom-driver.js +642 -0
- package/dist/electron-host/ipc-protocol.js +89 -0
- package/dist/electron-host/js-driver.js +216 -0
- package/dist/electron-host/view-manager.js +254 -0
- package/dist/server/cli/engine-resume.js +49 -0
- package/dist/server/cli/eta-estimator.js +278 -0
- package/dist/server/cli/headless/background-activity.js +109 -0
- package/dist/server/cli/headless/claude-invoker-process.js +614 -0
- package/dist/server/cli/headless/claude-invoker-stall.js +150 -0
- package/dist/server/cli/headless/claude-invoker-stream.js +555 -0
- package/dist/server/cli/headless/claude-invoker-tools.js +148 -0
- package/dist/server/cli/headless/claude-invoker.js +151 -0
- package/dist/server/cli/headless/engine-availability.js +92 -0
- package/dist/server/cli/headless/engine-notice.js +185 -0
- package/dist/server/cli/headless/haiku-assessments.js +293 -0
- package/dist/server/cli/headless/headless-logger.js +75 -0
- package/dist/server/cli/headless/index.js +9 -0
- package/dist/server/cli/headless/mcp-config.js +314 -0
- package/dist/server/cli/headless/native-timeout-detector.js +72 -0
- package/dist/server/cli/headless/output-utils.js +71 -0
- package/dist/server/cli/headless/prompt-utils.js +82 -0
- package/dist/server/cli/headless/resilient-runner.js +326 -0
- package/dist/server/cli/headless/retry-strategies.js +292 -0
- package/dist/server/cli/headless/runner.js +245 -0
- package/dist/server/cli/headless/stall-assessor.js +295 -0
- package/dist/server/cli/headless/tool-watchdog.js +447 -0
- package/dist/server/cli/headless/types.js +18 -0
- package/dist/server/cli/history-portability.js +85 -0
- package/dist/server/cli/improvisation-attachments.js +99 -0
- package/dist/server/cli/improvisation-history-store.js +121 -0
- package/dist/server/cli/improvisation-movements.js +137 -0
- package/dist/server/cli/improvisation-output-queue.js +34 -0
- package/dist/server/cli/improvisation-retry.js +8 -0
- package/dist/server/cli/improvisation-session-manager.js +1016 -0
- package/dist/server/cli/improvisation-types.js +31 -0
- package/dist/server/cli/plan-lifecycle.js +72 -0
- package/dist/server/cli/prompt-builders.js +371 -0
- package/dist/server/cli/retry/retry-best-result.js +28 -0
- package/dist/server/cli/retry/retry-context-loss.js +75 -0
- package/dist/server/cli/retry/retry-recovery-strategies.js +213 -0
- package/dist/server/cli/retry/retry-resume-strategy.js +20 -0
- package/dist/server/cli/retry/retry-runner-factory.js +92 -0
- package/dist/server/cli/retry/retry-tool-results.js +22 -0
- package/dist/server/cli/retry/retry-types.js +3 -0
- package/dist/server/engines/EngineEvent.js +57 -0
- package/dist/server/engines/claude/ClaudeCodeEngine.js +316 -0
- package/dist/server/engines/claude/claude-command.js +32 -0
- package/dist/server/engines/codex/CodexEngine.js +469 -0
- package/dist/server/engines/codex/codex-auth.js +203 -0
- package/dist/server/engines/codex/codex-invoker.js +205 -0
- package/dist/server/engines/codex/codex-provision.js +215 -0
- package/dist/server/engines/credentials/credential-probe.js +88 -0
- package/dist/server/engines/credentials/model-profiles.js +249 -0
- package/dist/server/engines/credentials/provider-credentials.js +146 -0
- package/dist/server/engines/engine-event-stream.js +52 -0
- package/dist/server/engines/execution/engine-session.js +380 -0
- package/dist/server/engines/factory.js +42 -0
- package/dist/server/engines/hermes/HermesEngine.js +628 -0
- package/dist/server/engines/hermes/hermes-auth.js +159 -0
- package/dist/server/engines/hermes/hermes-gateway-home.js +271 -0
- package/dist/server/engines/hermes/hermes-invoker.js +350 -0
- package/dist/server/engines/hermes/hermes-provision.js +318 -0
- package/dist/server/engines/read-only.js +20 -0
- package/dist/server/engines/types.js +27 -0
- package/dist/server/handlers/company-execution-registry.js +67 -0
- package/dist/server/handlers/company-handlers.js +514 -0
- package/dist/server/handlers/company-triggers.js +189 -0
- package/dist/server/handlers/company-types.js +3 -0
- package/dist/server/index.js +244 -0
- package/dist/server/mcp/app-tools-bridge.js +110 -0
- package/dist/server/mcp/app-tools-mcp-server.js +99 -0
- package/dist/server/mcp/bouncer-integration.js +744 -0
- package/dist/server/mcp/browser-mcp-server.js +915 -0
- package/dist/server/mcp/browser-redaction.js +103 -0
- package/dist/server/mcp/classifier/BouncerClassifier.js +3 -0
- package/dist/server/mcp/classifier/ClaudeBouncerClassifier.js +212 -0
- package/dist/server/mcp/classifier/factory.js +124 -0
- package/dist/server/mcp/classifier/health.js +54 -0
- package/dist/server/mcp/classifier/telemetry.js +0 -0
- package/dist/server/mcp/company-patterns.js +104 -0
- package/dist/server/mcp/department-profiles.js +133 -0
- package/dist/server/mcp/quality-bridge.js +153 -0
- package/dist/server/mcp/quality-mcp-server.js +105 -0
- package/dist/server/mcp/security-analysis.js +384 -0
- package/dist/server/mcp/security-audit.js +152 -0
- package/dist/server/mcp/security-patterns.js +314 -0
- package/dist/server/mcp/server.js +194 -0
- package/dist/server/mcp/test-utils/agent-simulator.js +301 -0
- package/dist/server/mcp/test-utils/e2e-agent.js +103 -0
- package/dist/server/mcp/test-utils/e2e-capture.js +92 -0
- package/dist/server/mcp/test-utils/e2e-client.js +116 -0
- package/dist/server/mcp/test-utils/e2e-fixtures.js +87 -0
- package/dist/server/mcp/test-utils/e2e-gating.js +8 -0
- package/dist/server/mcp/test-utils/e2e-harness.js +8 -0
- package/dist/server/mcp/test-utils/e2e-scenarios.js +105 -0
- package/dist/server/routes/files.js +18 -0
- package/dist/server/routes/improvise.js +64 -0
- package/dist/server/routes/index.js +7 -0
- package/dist/server/routes/instances.js +41 -0
- package/dist/server/routes/internal.js +900 -0
- package/dist/server/routes/notifications.js +110 -0
- package/dist/server/server-setup.js +282 -0
- package/dist/server/services/agent-push.js +148 -0
- package/dist/server/services/analytics.js +197 -0
- package/dist/server/services/apps/build.js +249 -0
- package/dist/server/services/apps/greenlight.js +199 -0
- package/dist/server/services/apps/install.js +101 -0
- package/dist/server/services/apps/layout.js +35 -0
- package/dist/server/services/apps/serve.js +665 -0
- package/dist/server/services/apps/space-apps.js +248 -0
- package/dist/server/services/auth.js +48 -0
- package/dist/server/services/browser/chrome-sandbox.js +245 -0
- package/dist/server/services/browser/chromium-launch-spec.js +51 -0
- package/dist/server/services/browser/claude-reasoner.js +669 -0
- package/dist/server/services/browser/debug-log.js +3 -0
- package/dist/server/services/browser/driver-state.js +546 -0
- package/dist/server/services/browser/electron-provision.js +256 -0
- package/dist/server/services/browser/host-internal.js +245 -0
- package/dist/server/services/browser/host-mock.js +334 -0
- package/dist/server/services/browser/host-types.js +3 -0
- package/dist/server/services/browser/host.js +1067 -0
- package/dist/server/services/browser/index.js +4 -0
- package/dist/server/services/browser/ipc-client.js +354 -0
- package/dist/server/services/browser/pipewire-sink.js +199 -0
- package/dist/server/services/browser/preflight.js +590 -0
- package/dist/server/services/browser/reasoning-agent.js +659 -0
- package/dist/server/services/browser/sandbox-policy.js +169 -0
- package/dist/server/services/browser/stderr-noise-filter.js +26 -0
- package/dist/server/services/browser/telemetry.js +107 -0
- package/dist/server/services/browser/xvfb-supervisor.js +139 -0
- package/dist/server/services/browser-frame-policy.js +56 -0
- package/dist/server/services/client-id.js +32 -0
- package/dist/server/services/collab/registry.js +408 -0
- package/dist/server/services/collab/session.js +489 -0
- package/dist/server/services/collab/text-diff.js +28 -0
- package/dist/server/services/collab/types.js +19 -0
- package/dist/server/services/e2ee-group-key.js +197 -0
- package/dist/server/services/e2ee-identity.js +203 -0
- package/dist/server/services/file-explorer-ops.js +328 -0
- package/dist/server/services/file-transfer-zip.js +188 -0
- package/dist/server/services/files.js +111 -0
- package/dist/server/services/git/branch-ops.js +62 -0
- package/dist/server/services/git/command.js +68 -0
- package/dist/server/services/git/commit-message.js +74 -0
- package/dist/server/services/git/diff-ops.js +56 -0
- package/dist/server/services/git/haiku.js +76 -0
- package/dist/server/services/git/head-watcher.js +226 -0
- package/dist/server/services/git/index.js +17 -0
- package/dist/server/services/git/log-ops.js +98 -0
- package/dist/server/services/git/merge-ops.js +314 -0
- package/dist/server/services/git/parsers.js +401 -0
- package/dist/server/services/git/remote-ops.js +192 -0
- package/dist/server/services/git/status-ops.js +105 -0
- package/dist/server/services/git/suggestion-ops.js +80 -0
- package/dist/server/services/git/tag-ops.js +36 -0
- package/dist/server/services/git/types.js +4 -0
- package/dist/server/services/git/worktree-ops.js +114 -0
- package/dist/server/services/instances.js +337 -0
- package/dist/server/services/model-catalog.js +247 -0
- package/dist/server/services/observability/posthog.js +153 -0
- package/dist/server/services/pathUtils.js +114 -0
- package/dist/server/services/plan/agent-loader.js +97 -0
- package/dist/server/services/plan/agent-manager.js +363 -0
- package/dist/server/services/plan/agent-resolver.js +74 -0
- package/dist/server/services/plan/agents/assess-stall.md +33 -0
- package/dist/server/services/plan/agents/browser-planner.md +53 -0
- package/dist/server/services/plan/agents/browser-reasoner.md +114 -0
- package/dist/server/services/plan/agents/check-injection.md +95 -0
- package/dist/server/services/plan/agents/classify-error.md +30 -0
- package/dist/server/services/plan/agents/code-review.md +142 -0
- package/dist/server/services/plan/agents/commit-message.md +28 -0
- package/dist/server/services/plan/agents/detect-context-loss.md +30 -0
- package/dist/server/services/plan/agents/execute-issue.md +58 -0
- package/dist/server/services/plan/agents/fix-quality.md +25 -0
- package/dist/server/services/plan/agents/plan-coordinator.md +72 -0
- package/dist/server/services/plan/agents/pr-description.md +29 -0
- package/dist/server/services/plan/agents/retry-task.md +27 -0
- package/dist/server/services/plan/agents/review-code.md +38 -0
- package/dist/server/services/plan/agents/review-quality.md +45 -0
- package/dist/server/services/plan/agents/triage-findings.md +78 -0
- package/dist/server/services/plan/agents/verify-review.md +57 -0
- package/dist/server/services/plan/approval-evaluator.js +143 -0
- package/dist/server/services/plan/board-archive.js +102 -0
- package/dist/server/services/plan/board-config.js +108 -0
- package/dist/server/services/plan/board-export.js +214 -0
- package/dist/server/services/plan/composer-prompt.js +115 -0
- package/dist/server/services/plan/composer.js +664 -0
- package/dist/server/services/plan/config-installer.js +103 -0
- package/dist/server/services/plan/dependency-resolver.js +137 -0
- package/dist/server/services/plan/executor.js +696 -0
- package/dist/server/services/plan/front-matter.js +43 -0
- package/dist/server/services/plan/issue-classification.js +10 -0
- package/dist/server/services/plan/issue-effort.js +182 -0
- package/dist/server/services/plan/issue-loader.js +35 -0
- package/dist/server/services/plan/issue-prompt-builder.js +152 -0
- package/dist/server/services/plan/issue-retry.js +42 -0
- package/dist/server/services/plan/issue-writer.js +102 -0
- package/dist/server/services/plan/metrics-collector.js +272 -0
- package/dist/server/services/plan/output-manager.js +77 -0
- package/dist/server/services/plan/parser-core.js +435 -0
- package/dist/server/services/plan/parser-migration.js +108 -0
- package/dist/server/services/plan/parser.js +258 -0
- package/dist/server/services/plan/plan-runner-factory.js +68 -0
- package/dist/server/services/plan/pm-timeline.js +320 -0
- package/dist/server/services/plan/progress-log.js +101 -0
- package/dist/server/services/plan/prompt-builder.js +142 -0
- package/dist/server/services/plan/quality-delta.js +274 -0
- package/dist/server/services/plan/readiness-planner.js +34 -0
- package/dist/server/services/plan/review-gate.js +409 -0
- package/dist/server/services/plan/review-report.js +134 -0
- package/dist/server/services/plan/review-verdict-stream.js +161 -0
- package/dist/server/services/plan/state-reconciler.js +238 -0
- package/dist/server/services/plan/template-diff.js +175 -0
- package/dist/server/services/plan/template-instantiator.js +516 -0
- package/dist/server/services/plan/template-manifest.js +56 -0
- package/dist/server/services/plan/template-types.js +3 -0
- package/dist/server/services/plan/trigger-evaluator.js +211 -0
- package/dist/server/services/plan/types.js +3 -0
- package/dist/server/services/plan/watcher.js +73 -0
- package/dist/server/services/platform-credentials.js +56 -0
- package/dist/server/services/platform-e2ee.js +421 -0
- package/dist/server/services/platform-envelope.js +102 -0
- package/dist/server/services/platform-heartbeat.js +88 -0
- package/dist/server/services/platform-reconnect.js +60 -0
- package/dist/server/services/platform-token-lifecycle.js +95 -0
- package/dist/server/services/platform-url.js +17 -0
- package/dist/server/services/platform.js +656 -0
- package/dist/server/services/runtime-info.js +9 -0
- package/dist/server/services/schedule/agent-schedule-store.js +232 -0
- package/dist/server/services/schedule/agent-scheduler.js +323 -0
- package/dist/server/services/schedule/daily-timing.js +105 -0
- package/dist/server/services/schedule/quality-schedule-launch.js +11 -0
- package/dist/server/services/schedule/quality-schedule-store.js +211 -0
- package/dist/server/services/schedule/quality-scheduler.js +215 -0
- package/dist/server/services/schedule/run-bootstrap.js +329 -0
- package/dist/server/services/schedule/schedule-store.js +238 -0
- package/dist/server/services/schedule/schedule-wire.js +77 -0
- package/dist/server/services/schedule/scheduler.js +310 -0
- package/dist/server/services/schedule/template-source.js +97 -0
- package/dist/server/services/sdk/agent-sessions.js +264 -0
- package/dist/server/services/sdk/agents.js +557 -0
- package/dist/server/services/sdk/app-endpoints.js +48 -0
- package/dist/server/services/sdk/app-tools.js +303 -0
- package/dist/server/services/sdk/audit.js +48 -0
- package/dist/server/services/sdk/browser-transport.js +188 -0
- package/dist/server/services/sdk/browser.js +605 -0
- package/dist/server/services/sdk/composer.js +129 -0
- package/dist/server/services/sdk/connect.js +273 -0
- package/dist/server/services/sdk/consent-cooldown.js +31 -0
- package/dist/server/services/sdk/endpoint-url.js +141 -0
- package/dist/server/services/sdk/endpoints.js +314 -0
- package/dist/server/services/sdk/file-change-notify.js +36 -0
- package/dist/server/services/sdk/files-app.js +163 -0
- package/dist/server/services/sdk/files-search.js +131 -0
- package/dist/server/services/sdk/files-transfer.js +279 -0
- package/dist/server/services/sdk/files-workspace.js +233 -0
- package/dist/server/services/sdk/git.js +520 -0
- package/dist/server/services/sdk/grant-store.js +318 -0
- package/dist/server/services/sdk/host-ops.js +84 -0
- package/dist/server/services/sdk/inference-claude.js +214 -0
- package/dist/server/services/sdk/inference.js +247 -0
- package/dist/server/services/sdk/live-registry.js +42 -0
- package/dist/server/services/sdk/local-app-bindings.js +71 -0
- package/dist/server/services/sdk/pipeline.js +190 -0
- package/dist/server/services/sdk/pm-execute.js +228 -0
- package/dist/server/services/sdk/pm-planning-tap.js +29 -0
- package/dist/server/services/sdk/pm-schedule.js +165 -0
- package/dist/server/services/sdk/pm.js +634 -0
- package/dist/server/services/sdk/quality-change-notify.js +20 -0
- package/dist/server/services/sdk/quality-schedule.js +165 -0
- package/dist/server/services/sdk/quality.js +602 -0
- package/dist/server/services/sdk/rate-limits.js +198 -0
- package/dist/server/services/sdk/registry.js +597 -0
- package/dist/server/services/sdk/revoke.js +86 -0
- package/dist/server/services/sdk/run-profile.js +41 -0
- package/dist/server/services/sdk/sandbox.js +89 -0
- package/dist/server/services/sdk/storage.js +110 -0
- package/dist/server/services/sdk/streams.js +147 -0
- package/dist/server/services/sdk/terminal.js +201 -0
- package/dist/server/services/sdk/test-utils/workshop-browser-app.js +466 -0
- package/dist/server/services/sentry.js +99 -0
- package/dist/server/services/settings.js +374 -0
- package/dist/server/services/snippets/expander.js +87 -0
- package/dist/server/services/snippets/index.js +6 -0
- package/dist/server/services/snippets/loader.js +119 -0
- package/dist/server/services/snippets/parser.js +52 -0
- package/dist/server/services/snippets/types.js +3 -0
- package/dist/server/services/snippets/watcher.js +60 -0
- package/dist/server/services/space-layout.js +80 -0
- package/dist/server/services/system-prompts.js +219 -0
- package/dist/server/services/terminal/pty-manager.js +206 -0
- package/dist/server/services/terminal/pty-utils.js +150 -0
- package/dist/server/services/timeline/assembler.js +890 -0
- package/dist/server/services/timeline/contract.js +196 -0
- package/dist/server/services/timeline/index.js +4 -0
- package/dist/server/services/websocket/agent-schedule-handlers.js +217 -0
- package/dist/server/services/websocket/apps-handlers.js +399 -0
- package/dist/server/services/websocket/ask-user-question-bridge.js +112 -0
- package/dist/server/services/websocket/autocomplete-index.js +415 -0
- package/dist/server/services/websocket/autocomplete-match.js +243 -0
- package/dist/server/services/websocket/autocomplete.js +407 -0
- package/dist/server/services/websocket/browser-agent-runtime.js +208 -0
- package/dist/server/services/websocket/browser-device-profile.js +46 -0
- package/dist/server/services/websocket/browser-handlers.js +377 -0
- package/dist/server/services/websocket/browser-host-broadcast.js +179 -0
- package/dist/server/services/websocket/browser-ice-config.js +121 -0
- package/dist/server/services/websocket/browser-payloads.js +222 -0
- package/dist/server/services/websocket/browser-viewer-lifecycle.js +84 -0
- package/dist/server/services/websocket/browser-viewer-registry.js +118 -0
- package/dist/server/services/websocket/collab-broadcast.js +11 -0
- package/dist/server/services/websocket/collab-handlers.js +116 -0
- package/dist/server/services/websocket/commit-suggestion-service.js +132 -0
- package/dist/server/services/websocket/debug-log.js +38 -0
- package/dist/server/services/websocket/dispatch-util.js +10 -0
- package/dist/server/services/websocket/engine-auth-handlers.js +323 -0
- package/dist/server/services/websocket/engine-credential-watcher.js +83 -0
- package/dist/server/services/websocket/engine-profile-handlers.js +92 -0
- package/dist/server/services/websocket/file-definition-handlers.js +180 -0
- package/dist/server/services/websocket/file-download-handler.js +291 -0
- package/dist/server/services/websocket/file-explorer-handlers.js +291 -0
- package/dist/server/services/websocket/file-search-handlers.js +267 -0
- package/dist/server/services/websocket/file-share-handlers.js +328 -0
- package/dist/server/services/websocket/file-transfer-http.js +361 -0
- package/dist/server/services/websocket/file-upload-handler.js +267 -0
- package/dist/server/services/websocket/file-utils.js +346 -0
- package/dist/server/services/websocket/git-branch-handlers.js +90 -0
- package/dist/server/services/websocket/git-diff-handlers.js +59 -0
- package/dist/server/services/websocket/git-handlers.js +212 -0
- package/dist/server/services/websocket/git-head-watcher.js +85 -0
- package/dist/server/services/websocket/git-log-handlers.js +104 -0
- package/dist/server/services/websocket/git-pr-handlers.js +56 -0
- package/dist/server/services/websocket/git-tag-handlers.js +53 -0
- package/dist/server/services/websocket/git-utils.js +11 -0
- package/dist/server/services/websocket/git-worktree-handlers.js +521 -0
- package/dist/server/services/websocket/handler-context.js +113 -0
- package/dist/server/services/websocket/handler.js +1197 -0
- package/dist/server/services/websocket/index.js +5 -0
- package/dist/server/services/websocket/movement-written-files.js +26 -0
- package/dist/server/services/websocket/msg-id-tracker.js +48 -0
- package/dist/server/services/websocket/plan-board-handlers.js +410 -0
- package/dist/server/services/websocket/plan-execution-handlers.js +244 -0
- package/dist/server/services/websocket/plan-handlers.js +67 -0
- package/dist/server/services/websocket/plan-helpers.js +161 -0
- package/dist/server/services/websocket/plan-issue-handlers.js +168 -0
- package/dist/server/services/websocket/plan-sprint-handlers.js +190 -0
- package/dist/server/services/websocket/plan-transfer-handlers.js +310 -0
- package/dist/server/services/websocket/pm-timeline-broadcast.js +28 -0
- package/dist/server/services/websocket/post-session-scan.js +132 -0
- package/dist/server/services/websocket/prompt-queue-handlers.js +96 -0
- package/dist/server/services/websocket/quality-agent-cost.js +229 -0
- package/dist/server/services/websocket/quality-agent-notes.js +113 -0
- package/dist/server/services/websocket/quality-change-activity.js +187 -0
- package/dist/server/services/websocket/quality-complexity.js +345 -0
- package/dist/server/services/websocket/quality-dependencies.js +445 -0
- package/dist/server/services/websocket/quality-duplication.js +162 -0
- package/dist/server/services/websocket/quality-eta.js +65 -0
- package/dist/server/services/websocket/quality-fingerprint.js +104 -0
- package/dist/server/services/websocket/quality-grading.js +575 -0
- package/dist/server/services/websocket/quality-handlers.js +48 -0
- package/dist/server/services/websocket/quality-linting.js +270 -0
- package/dist/server/services/websocket/quality-operations.js +54 -0
- package/dist/server/services/websocket/quality-persistence.js +410 -0
- package/dist/server/services/websocket/quality-review-agent.js +526 -0
- package/dist/server/services/websocket/quality-run.js +220 -0
- package/dist/server/services/websocket/quality-secrets.js +0 -0
- package/dist/server/services/websocket/quality-service.js +775 -0
- package/dist/server/services/websocket/quality-tools.js +383 -0
- package/dist/server/services/websocket/quality-triage-agent.js +101 -0
- package/dist/server/services/websocket/quality-types.js +149 -0
- package/dist/server/services/websocket/recommended-answers.js +28 -0
- package/dist/server/services/websocket/schedule-handlers.js +197 -0
- package/dist/server/services/websocket/sdk-agent-host.js +569 -0
- package/dist/server/services/websocket/sdk-browser-host.js +459 -0
- package/dist/server/services/websocket/sdk-composer-host.js +10 -0
- package/dist/server/services/websocket/sdk-git-host.js +115 -0
- package/dist/server/services/websocket/sdk-handlers.js +242 -0
- package/dist/server/services/websocket/sdk-pm-execution-host.js +63 -0
- package/dist/server/services/websocket/sdk-pm-schedule-host.js +21 -0
- package/dist/server/services/websocket/sdk-quality-schedule-host.js +34 -0
- package/dist/server/services/websocket/sdk-terminal-host.js +140 -0
- package/dist/server/services/websocket/session-handlers.js +565 -0
- package/dist/server/services/websocket/session-history.js +335 -0
- package/dist/server/services/websocket/session-initialization.js +315 -0
- package/dist/server/services/websocket/session-registry.js +340 -0
- package/dist/server/services/websocket/session-role-directory.js +39 -0
- package/dist/server/services/websocket/settings-handlers.js +276 -0
- package/dist/server/services/websocket/skill-handlers.js +324 -0
- package/dist/server/services/websocket/skill-watcher.js +83 -0
- package/dist/server/services/websocket/snippet-handlers.js +261 -0
- package/dist/server/services/websocket/tab-broadcast.js +35 -0
- package/dist/server/services/websocket/tab-event-buffer.js +110 -0
- package/dist/server/services/websocket/tab-event-replay.js +47 -0
- package/dist/server/services/websocket/tab-handlers.js +211 -0
- package/dist/server/services/websocket/terminal-handlers.js +268 -0
- package/dist/server/services/websocket/types.js +96 -0
- package/dist/server/types/company-messages.js +102 -0
- package/dist/server/utils/agent-manager.js +202 -0
- package/dist/server/utils/console-timestamps.js +24 -0
- package/dist/server/utils/file-store.js +112 -0
- package/dist/server/utils/paths.js +28 -0
- package/dist/server/utils/port-manager.js +43 -0
- package/dist/server/utils/port.js +204 -0
- package/electron-host/dist/agent-lease.js +141 -0
- package/electron-host/dist/capture-recovery.js +77 -0
- package/electron-host/dist/codec-policy.js +37 -0
- package/electron-host/dist/controller-channel.js +60 -0
- package/electron-host/dist/controller-handlers.js +153 -0
- package/electron-host/dist/device-emulator.js +106 -0
- package/electron-host/dist/dom-driver.js +642 -0
- package/electron-host/dist/domain-registry.js +159 -0
- package/electron-host/dist/domain-validators.js +308 -0
- package/electron-host/dist/editable-focus-reporter.js +143 -0
- package/electron-host/dist/fake-peer.test-util.js +105 -0
- package/electron-host/dist/host-log.js +31 -0
- package/electron-host/dist/host-wire.js +11 -0
- package/electron-host/dist/input-router.js +421 -0
- package/electron-host/dist/ipc-protocol.js +89 -0
- package/electron-host/dist/ipc-server.js +193 -0
- package/electron-host/dist/js-driver.js +216 -0
- package/electron-host/dist/main-guards.js +70 -0
- package/electron-host/dist/main.js +716 -0
- package/electron-host/dist/roster-sync.js +87 -0
- package/electron-host/dist/rpc-context.js +1 -0
- package/electron-host/dist/rpc-tab-methods.js +138 -0
- package/electron-host/dist/rpc-webrtc-methods.js +121 -0
- package/electron-host/dist/screen-capture-cache.js +147 -0
- package/electron-host/dist/sdk-injector.js +166 -0
- package/electron-host/dist/sdp.js +102 -0
- package/electron-host/dist/session-surface.js +78 -0
- package/electron-host/dist/surface-sizing.js +59 -0
- package/electron-host/dist/view-manager.js +254 -0
- package/electron-host/dist/viewer-registry.js +46 -0
- package/electron-host/dist/viewer-session.js +288 -0
- package/electron-host/dist/webrtc-channels.js +14 -0
- package/electron-host/dist/webrtc-controller.js +57 -0
- package/electron-host/dist/webrtc-ice-filter.js +13 -0
- package/electron-host/dist/webrtc-peer.js +45 -0
- package/electron-host/package.json +12 -0
- package/electron-host/tsconfig.json +23 -0
- package/electron-host/webrtc-controller-preload.cjs +3 -0
- package/electron-host/webrtc-controller.html +14 -0
- package/package.json +85 -0
- package/vendor/e2ee/LICENSE +14 -0
- package/vendor/e2ee/package.json +14 -0
- package/vendor/e2ee/src/index.js +310 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Minionry, Inc.
|
|
2
|
+
// SPDX-License-Identifier: LicenseRef-Minionry-Software
|
|
3
|
+
import { ModelProfileStore, profileModelValue, } from '../../engines/credentials/model-profiles.js';
|
|
4
|
+
import { CLAUDE_MODEL_PRESETS, CODEX_MODEL_PRESETS, ENGINE_EFFORT_LEVELS, ENGINE_IDS, engineForModel, } from '../../engines/types.js';
|
|
5
|
+
import { peekModelCatalog } from '../model-catalog.js';
|
|
6
|
+
import { getEffortLevel, getEngine, getFastMode, getModel } from '../settings.js';
|
|
7
|
+
import { collectAllSkills } from '../websocket/skill-handlers.js';
|
|
8
|
+
import { rankAllSnippets, rankQuerySnippets } from '../websocket/snippet-handlers.js';
|
|
9
|
+
import { isAppOwnedProfile } from './app-endpoints.js';
|
|
10
|
+
import { inferenceEngines } from './inference.js';
|
|
11
|
+
import { SdkMethodError } from './registry.js';
|
|
12
|
+
let composerHost = null;
|
|
13
|
+
export function setSdkComposerHost(host) {
|
|
14
|
+
composerHost = host;
|
|
15
|
+
}
|
|
16
|
+
export function resetSdkComposerHost() {
|
|
17
|
+
composerHost = null;
|
|
18
|
+
}
|
|
19
|
+
function requireHost() {
|
|
20
|
+
if (!composerHost) {
|
|
21
|
+
throw new SdkMethodError('INTERNAL', 'composer host is not wired — the WebSocket handler installs it at startup');
|
|
22
|
+
}
|
|
23
|
+
return composerHost;
|
|
24
|
+
}
|
|
25
|
+
function paramsObject(params) {
|
|
26
|
+
return typeof params === 'object' && params !== null && !Array.isArray(params) ? params : {};
|
|
27
|
+
}
|
|
28
|
+
export function composerAutocompleteHandler(args) {
|
|
29
|
+
const raw = paramsObject(args.params);
|
|
30
|
+
if (typeof raw.query !== 'string') {
|
|
31
|
+
throw new SdkMethodError('BAD_REQUEST', 'composer.autocomplete requires params { query }');
|
|
32
|
+
}
|
|
33
|
+
return requireHost().autocomplete(args.workingDir, raw.query);
|
|
34
|
+
}
|
|
35
|
+
export function composerSkillsHandler(args) {
|
|
36
|
+
return collectAllSkills(args.workingDir);
|
|
37
|
+
}
|
|
38
|
+
export function composerSnippetsHandler(args) {
|
|
39
|
+
const raw = paramsObject(args.params);
|
|
40
|
+
if (raw.query !== undefined && typeof raw.query !== 'string') {
|
|
41
|
+
throw new SdkMethodError('BAD_REQUEST', 'composer.snippets query must be a string when present');
|
|
42
|
+
}
|
|
43
|
+
const query = typeof raw.query === 'string' ? raw.query.trim() : '';
|
|
44
|
+
const all = requireHost().snippets();
|
|
45
|
+
const ranked = query.length === 0 ? rankAllSnippets(all) : rankQuerySnippets(all, query);
|
|
46
|
+
return ranked.map(({ name, aliases, description, origin, preview, bodyLength }) => ({
|
|
47
|
+
name,
|
|
48
|
+
aliases,
|
|
49
|
+
description,
|
|
50
|
+
origin,
|
|
51
|
+
preview,
|
|
52
|
+
bodyLength,
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
function presetRow(preset, engine) {
|
|
56
|
+
return {
|
|
57
|
+
value: preset.value,
|
|
58
|
+
engine,
|
|
59
|
+
label: preset.label,
|
|
60
|
+
description: preset.description,
|
|
61
|
+
...(preset.isDefault ? { isDefault: true } : {}),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function customProfileRows() {
|
|
65
|
+
try {
|
|
66
|
+
return new ModelProfileStore()
|
|
67
|
+
.list()
|
|
68
|
+
.filter((profile) => !isAppOwnedProfile(profile))
|
|
69
|
+
.map((profile) => ({
|
|
70
|
+
value: profileModelValue(profile),
|
|
71
|
+
engine: profile.engine,
|
|
72
|
+
label: profile.label,
|
|
73
|
+
description: profile.model,
|
|
74
|
+
...(profile.contextLength !== undefined ? { contextLength: profile.contextLength } : {}),
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function catalogRow(row) {
|
|
82
|
+
return {
|
|
83
|
+
value: row.value,
|
|
84
|
+
engine: engineForModel(row.value),
|
|
85
|
+
label: row.label,
|
|
86
|
+
...(row.description !== undefined ? { description: row.description } : {}),
|
|
87
|
+
...(row.isDefault ? { isDefault: true } : {}),
|
|
88
|
+
...(row.contextLength !== undefined ? { contextLength: row.contextLength } : {}),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function buildModelCatalog() {
|
|
92
|
+
const models = [
|
|
93
|
+
...CLAUDE_MODEL_PRESETS.map((preset) => presetRow(preset, 'claude-code')),
|
|
94
|
+
...CODEX_MODEL_PRESETS.map((preset) => presetRow(preset, 'codex')),
|
|
95
|
+
...customProfileRows(),
|
|
96
|
+
];
|
|
97
|
+
const payload = peekModelCatalog();
|
|
98
|
+
const seen = new Set(models.map((model) => model.value));
|
|
99
|
+
for (const row of [
|
|
100
|
+
...(payload.hermes?.nous ?? []),
|
|
101
|
+
...(payload.hermes?.openrouter ?? []),
|
|
102
|
+
...(payload.openrouter?.models ?? []),
|
|
103
|
+
]) {
|
|
104
|
+
if (seen.has(row.value))
|
|
105
|
+
continue;
|
|
106
|
+
seen.add(row.value);
|
|
107
|
+
models.push(catalogRow(row));
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
models,
|
|
111
|
+
status: { fetchedAt: payload.fetchedAt, stale: payload.stale, refreshing: payload.refreshing },
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
export function composerSettingsHandler(_args) {
|
|
115
|
+
const catalog = buildModelCatalog();
|
|
116
|
+
return {
|
|
117
|
+
engines: [...ENGINE_IDS],
|
|
118
|
+
defaults: {
|
|
119
|
+
engine: getEngine(),
|
|
120
|
+
model: getModel(),
|
|
121
|
+
effortLevel: getEffortLevel(),
|
|
122
|
+
fastMode: getFastMode(),
|
|
123
|
+
},
|
|
124
|
+
models: catalog.models,
|
|
125
|
+
modelsStatus: catalog.status,
|
|
126
|
+
effortLevels: Object.fromEntries(ENGINE_IDS.map((engine) => [engine, [...ENGINE_EFFORT_LEVELS[engine]]])),
|
|
127
|
+
inferenceEngines: inferenceEngines(),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Minionry, Inc.
|
|
2
|
+
// SPDX-License-Identifier: LicenseRef-Minionry-Software
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { basename } from 'node:path';
|
|
5
|
+
import { isScope } from '@minionry/app-manifest';
|
|
6
|
+
import { can, RANK } from '@minionry/roles';
|
|
7
|
+
import { getCredentials } from '../platform-credentials.js';
|
|
8
|
+
import { logAppAudit } from './audit.js';
|
|
9
|
+
import { checkConsentCooldown, recordConsentApproval, recordConsentDismissal, } from './consent-cooldown.js';
|
|
10
|
+
import { computeScopeDelta, getGrant, getSessionGrant, setGrant, setSessionGrant, } from './grant-store.js';
|
|
11
|
+
import { isLocalhostFamilyOrigin } from './local-app-bindings.js';
|
|
12
|
+
import { deriveSpaceId, SdkMethodError } from './registry.js';
|
|
13
|
+
import { isValidAppId } from './sandbox.js';
|
|
14
|
+
export const VIEWER_GRANTABLE_SCOPES = new Set([
|
|
15
|
+
'identity',
|
|
16
|
+
'pm:read',
|
|
17
|
+
'files:workspace:read',
|
|
18
|
+
'settings:read',
|
|
19
|
+
'quality:read',
|
|
20
|
+
]);
|
|
21
|
+
export function canPersistGrants(role) {
|
|
22
|
+
return (RANK[role] ?? 0) > RANK.viewer;
|
|
23
|
+
}
|
|
24
|
+
export const CONSENT_TIMEOUT_MS = 5 * 60 * 1000;
|
|
25
|
+
let consentTimeoutOverrideMs;
|
|
26
|
+
export function setSdkConsentTimeoutMsForTests(ms) {
|
|
27
|
+
consentTimeoutOverrideMs = ms;
|
|
28
|
+
}
|
|
29
|
+
const pendingConsents = new Map();
|
|
30
|
+
function registerPendingConsent(consentId, accept) {
|
|
31
|
+
return new Promise((resolve) => {
|
|
32
|
+
const timer = setTimeout(() => {
|
|
33
|
+
if (pendingConsents.delete(consentId))
|
|
34
|
+
resolve({ outcome: 'timeout' });
|
|
35
|
+
}, consentTimeoutOverrideMs ?? CONSENT_TIMEOUT_MS);
|
|
36
|
+
pendingConsents.set(consentId, { consentId, accept, resolve, timer });
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function responderAllowed(accept, responder) {
|
|
40
|
+
if (accept.kind === 'requester') {
|
|
41
|
+
return accept.viewerId === responder.viewerId ? null : 'wrong-viewer';
|
|
42
|
+
}
|
|
43
|
+
if (responder.viewerId === undefined || !accept.viewerIds.has(responder.viewerId)) {
|
|
44
|
+
return 'wrong-viewer';
|
|
45
|
+
}
|
|
46
|
+
return can(responder.role, 'editSettings') ? null : 'wrong-role';
|
|
47
|
+
}
|
|
48
|
+
export function resolveSdkConsent(consentId, decision, responder) {
|
|
49
|
+
const entry = pendingConsents.get(consentId);
|
|
50
|
+
if (!entry)
|
|
51
|
+
return 'unknown-consent';
|
|
52
|
+
const refusal = responderAllowed(entry.accept, responder);
|
|
53
|
+
if (refusal)
|
|
54
|
+
return refusal;
|
|
55
|
+
pendingConsents.delete(consentId);
|
|
56
|
+
clearTimeout(entry.timer);
|
|
57
|
+
if (decision.approved) {
|
|
58
|
+
entry.resolve({
|
|
59
|
+
outcome: 'approved',
|
|
60
|
+
granted: decision.granted.filter(isScope),
|
|
61
|
+
remember: decision.remember === true,
|
|
62
|
+
responder,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
entry.resolve({ outcome: 'denied' });
|
|
67
|
+
}
|
|
68
|
+
return 'resolved';
|
|
69
|
+
}
|
|
70
|
+
export function pendingSdkConsentCount() {
|
|
71
|
+
return pendingConsents.size;
|
|
72
|
+
}
|
|
73
|
+
export function clearAllPendingSdkConsents() {
|
|
74
|
+
for (const entry of pendingConsents.values()) {
|
|
75
|
+
clearTimeout(entry.timer);
|
|
76
|
+
entry.resolve({ outcome: 'denied' });
|
|
77
|
+
}
|
|
78
|
+
pendingConsents.clear();
|
|
79
|
+
}
|
|
80
|
+
function parsePinnedManifest(value) {
|
|
81
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
82
|
+
return null;
|
|
83
|
+
const raw = value;
|
|
84
|
+
if (typeof raw.hash !== 'string' || raw.hash === '')
|
|
85
|
+
return null;
|
|
86
|
+
if (!Array.isArray(raw.scopes))
|
|
87
|
+
return null;
|
|
88
|
+
const optional = raw.optionalScopes === undefined ? [] : raw.optionalScopes;
|
|
89
|
+
if (!Array.isArray(optional))
|
|
90
|
+
return null;
|
|
91
|
+
return {
|
|
92
|
+
hash: raw.hash,
|
|
93
|
+
scopes: raw.scopes.filter(isScope),
|
|
94
|
+
optionalScopes: optional.filter(isScope),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function parseRequestedScopes(params) {
|
|
98
|
+
if (params === undefined || params === null)
|
|
99
|
+
return [];
|
|
100
|
+
if (typeof params !== 'object' || Array.isArray(params))
|
|
101
|
+
return null;
|
|
102
|
+
const raw = params.scopes;
|
|
103
|
+
if (raw === undefined)
|
|
104
|
+
return [];
|
|
105
|
+
if (!Array.isArray(raw) || !raw.every((scope) => typeof scope === 'string')) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
return [...new Set(raw)];
|
|
109
|
+
}
|
|
110
|
+
function hashBoundScopes(grant, manifestHash) {
|
|
111
|
+
return grant && grant.manifestHash === manifestHash ? grant.scopes : [];
|
|
112
|
+
}
|
|
113
|
+
function connectResult(args) {
|
|
114
|
+
const persisted = getGrant(args.appId, args.spaceKey);
|
|
115
|
+
const session = args.viewerId
|
|
116
|
+
? getSessionGrant(args.viewerId, args.appId, args.spaceKey)
|
|
117
|
+
: undefined;
|
|
118
|
+
const grants = [...new Set([...(persisted?.scopes ?? []), ...(session?.scopes ?? [])])];
|
|
119
|
+
let user = null;
|
|
120
|
+
if (args.role !== 'viewer') {
|
|
121
|
+
const creds = getCredentials();
|
|
122
|
+
if (creds)
|
|
123
|
+
user = { id: creds.userId, name: creds.name ?? creds.email, role: args.role };
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
space: { id: deriveSpaceId(args.spaceKey), name: basename(args.workingDir) },
|
|
127
|
+
user,
|
|
128
|
+
grants,
|
|
129
|
+
viewOnly: !can(args.role, 'execute'),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function applyApprovedConsent(args, manifest, onCard, outcome) {
|
|
133
|
+
const { appId, origin, spaceKey, viewerId } = args;
|
|
134
|
+
const { responder } = outcome;
|
|
135
|
+
let granted = onCard.filter((scope) => outcome.granted.includes(scope));
|
|
136
|
+
if (responder.role === 'viewer') {
|
|
137
|
+
granted = granted.filter((scope) => VIEWER_GRANTABLE_SCOPES.has(scope));
|
|
138
|
+
}
|
|
139
|
+
if (granted.length === 0)
|
|
140
|
+
return;
|
|
141
|
+
const persist = outcome.remember &&
|
|
142
|
+
canPersistGrants(responder.role) &&
|
|
143
|
+
(!isLocalhostFamilyOrigin(origin) || args.verifiedLocalBinding === true);
|
|
144
|
+
if (persist) {
|
|
145
|
+
const base = hashBoundScopes(getGrant(appId, spaceKey), manifest.hash);
|
|
146
|
+
const grant = setGrant({
|
|
147
|
+
appId,
|
|
148
|
+
spaceKey,
|
|
149
|
+
origin,
|
|
150
|
+
scopes: [...new Set([...base, ...granted])],
|
|
151
|
+
manifestHash: manifest.hash,
|
|
152
|
+
...(args.verifiedLocalBinding === true ? { verifiedLocalBinding: true } : {}),
|
|
153
|
+
});
|
|
154
|
+
args.notify?.({
|
|
155
|
+
type: 'sdkGrantsChanged',
|
|
156
|
+
data: { appId, grants: [...grant.scopes] },
|
|
157
|
+
broadcast: true,
|
|
158
|
+
});
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (!viewerId) {
|
|
162
|
+
throw new SdkMethodError('INTERNAL', 'no viewer identity — a session-scoped grant cannot be stored');
|
|
163
|
+
}
|
|
164
|
+
const base = hashBoundScopes(getSessionGrant(viewerId, appId, spaceKey), manifest.hash);
|
|
165
|
+
const grant = setSessionGrant(viewerId, {
|
|
166
|
+
appId,
|
|
167
|
+
spaceKey,
|
|
168
|
+
origin,
|
|
169
|
+
scopes: [...new Set([...base, ...granted])],
|
|
170
|
+
manifestHash: manifest.hash,
|
|
171
|
+
});
|
|
172
|
+
const effective = [
|
|
173
|
+
...new Set([...(getGrant(appId, spaceKey)?.scopes ?? []), ...grant.scopes]),
|
|
174
|
+
];
|
|
175
|
+
args.notify?.({
|
|
176
|
+
type: 'sdkGrantsChanged',
|
|
177
|
+
data: { appId, grants: effective },
|
|
178
|
+
targetViewerId: viewerId,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
export async function runConnect(args) {
|
|
182
|
+
const { appId, viewerId, spaceKey } = args;
|
|
183
|
+
if (!isValidAppId(appId)) {
|
|
184
|
+
throw new SdkMethodError('BAD_REQUEST', `invalid appId '${appId}' — expected the D5 grammar (lowercase [a-z0-9-])`);
|
|
185
|
+
}
|
|
186
|
+
const requested = parseRequestedScopes(args.params);
|
|
187
|
+
if (requested === null) {
|
|
188
|
+
throw new SdkMethodError('BAD_REQUEST', 'connect params.scopes must be an array of scope strings');
|
|
189
|
+
}
|
|
190
|
+
const manifest = parsePinnedManifest(args.manifest);
|
|
191
|
+
if (manifest === null) {
|
|
192
|
+
throw new SdkMethodError('BAD_REQUEST', 'sdkConnect must carry the pinned manifest envelope { hash, scopes, optionalScopes } stamped by the host');
|
|
193
|
+
}
|
|
194
|
+
const cap = new Set([...manifest.scopes, ...manifest.optionalScopes]);
|
|
195
|
+
const outsideCap = requested.filter((scope) => !cap.has(scope));
|
|
196
|
+
if (outsideCap.length > 0) {
|
|
197
|
+
throw new SdkMethodError('SCOPE_DENIED', `scope(s) not declared by the app's pinned manifest: ${outsideCap.join(', ')}`);
|
|
198
|
+
}
|
|
199
|
+
const requestedScopes = requested;
|
|
200
|
+
const persisted = getGrant(appId, spaceKey);
|
|
201
|
+
const session = viewerId ? getSessionGrant(viewerId, appId, spaceKey) : undefined;
|
|
202
|
+
const prior = [
|
|
203
|
+
...new Set([
|
|
204
|
+
...hashBoundScopes(persisted, manifest.hash),
|
|
205
|
+
...hashBoundScopes(session, manifest.hash),
|
|
206
|
+
]),
|
|
207
|
+
];
|
|
208
|
+
const missing = computeScopeDelta(requestedScopes, prior);
|
|
209
|
+
if (missing.length === 0) {
|
|
210
|
+
return connectResult(args);
|
|
211
|
+
}
|
|
212
|
+
const outcome = await promptForConsent(args, missing, prior);
|
|
213
|
+
applyApprovedConsent(args, manifest, missing, outcome);
|
|
214
|
+
return connectResult(args);
|
|
215
|
+
}
|
|
216
|
+
function resolveConsentRouting(args) {
|
|
217
|
+
if (args.consentRouting?.mode !== 'owner-sessions') {
|
|
218
|
+
return { accept: { kind: 'requester', viewerId: args.viewerId }, cardTargets: [args.viewerId] };
|
|
219
|
+
}
|
|
220
|
+
const sessions = args.consentRouting.listSessions().filter((id) => id !== '');
|
|
221
|
+
if (sessions.length === 0) {
|
|
222
|
+
autoDenyAudit(args, 'no owner session is connected to approve a Browser-transport consent');
|
|
223
|
+
throw new SdkMethodError('USER_REJECTED', 'no owner session is connected to approve this request — open this Space in Minionry as its owner and try again');
|
|
224
|
+
}
|
|
225
|
+
return { accept: { kind: 'owner-sessions', viewerIds: new Set(sessions) }, cardTargets: sessions };
|
|
226
|
+
}
|
|
227
|
+
async function promptForConsent(args, missing, prior) {
|
|
228
|
+
const { appId, origin, role } = args;
|
|
229
|
+
if (!args.notify) {
|
|
230
|
+
throw new SdkMethodError('USER_REJECTED', 'this transport has no consent surface attached — the connect request cannot be prompted');
|
|
231
|
+
}
|
|
232
|
+
const cooldown = checkConsentCooldown(origin);
|
|
233
|
+
if (cooldown.blocked) {
|
|
234
|
+
autoDenyAudit(args, `consent cooldown after ${cooldown.dismissals} consecutive dismissal(s) — retry in ${Math.ceil(cooldown.retryAfterMs / 1000)}s`);
|
|
235
|
+
throw new SdkMethodError('USER_REJECTED', 'the connect request was auto-denied — this origin is cooling down after repeated consent dismissals');
|
|
236
|
+
}
|
|
237
|
+
const { accept, cardTargets } = resolveConsentRouting(args);
|
|
238
|
+
const consentId = randomUUID();
|
|
239
|
+
const card = {
|
|
240
|
+
consentId,
|
|
241
|
+
appId,
|
|
242
|
+
origin,
|
|
243
|
+
requestedScopes: [...missing],
|
|
244
|
+
priorScopes: [...prior],
|
|
245
|
+
mode: prior.length > 0 ? 'delta' : 'full',
|
|
246
|
+
variant: accept.kind === 'owner-sessions' || role !== 'viewer' ? 'member' : 'viewer',
|
|
247
|
+
...(accept.kind === 'owner-sessions' ? { transport: 'browser' } : {}),
|
|
248
|
+
};
|
|
249
|
+
const pending = registerPendingConsent(consentId, accept);
|
|
250
|
+
for (const target of cardTargets) {
|
|
251
|
+
args.notify({ type: 'sdkConsentRequest', data: card, targetViewerId: target });
|
|
252
|
+
}
|
|
253
|
+
const outcome = await pending;
|
|
254
|
+
if (outcome.outcome === 'denied' || outcome.outcome === 'timeout') {
|
|
255
|
+
recordConsentDismissal(origin);
|
|
256
|
+
throw new SdkMethodError('USER_REJECTED', outcome.outcome === 'denied'
|
|
257
|
+
? 'the user declined the connect request'
|
|
258
|
+
: 'the connect request timed out without an answer');
|
|
259
|
+
}
|
|
260
|
+
recordConsentApproval(origin);
|
|
261
|
+
return outcome;
|
|
262
|
+
}
|
|
263
|
+
function autoDenyAudit(args, reason) {
|
|
264
|
+
logAppAudit({
|
|
265
|
+
appId: args.appId,
|
|
266
|
+
origin: args.origin,
|
|
267
|
+
method: 'connect.autoDenied',
|
|
268
|
+
decision: 'deny',
|
|
269
|
+
requestId: args.requestId,
|
|
270
|
+
spaceKey: args.spaceKey,
|
|
271
|
+
reason,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Minionry, Inc.
|
|
2
|
+
// SPDX-License-Identifier: LicenseRef-Minionry-Software
|
|
3
|
+
export const CONSENT_COOLDOWN_BASE_MS = 30_000;
|
|
4
|
+
export const CONSENT_COOLDOWN_FACTOR = 4;
|
|
5
|
+
export const CONSENT_COOLDOWN_MAX_MS = 30 * 60_000;
|
|
6
|
+
const cooldowns = new Map();
|
|
7
|
+
let now = Date.now;
|
|
8
|
+
export function setSdkConsentCooldownClockForTests(clock) {
|
|
9
|
+
now = clock ?? Date.now;
|
|
10
|
+
}
|
|
11
|
+
export function checkConsentCooldown(origin) {
|
|
12
|
+
const state = cooldowns.get(origin);
|
|
13
|
+
if (!state)
|
|
14
|
+
return { blocked: false };
|
|
15
|
+
const remaining = state.blockedUntil - now();
|
|
16
|
+
if (remaining <= 0)
|
|
17
|
+
return { blocked: false };
|
|
18
|
+
return { blocked: true, retryAfterMs: remaining, dismissals: state.dismissals };
|
|
19
|
+
}
|
|
20
|
+
export function recordConsentDismissal(origin) {
|
|
21
|
+
const dismissals = (cooldowns.get(origin)?.dismissals ?? 0) + 1;
|
|
22
|
+
const blockedForMs = Math.min(CONSENT_COOLDOWN_BASE_MS * CONSENT_COOLDOWN_FACTOR ** (dismissals - 1), CONSENT_COOLDOWN_MAX_MS);
|
|
23
|
+
cooldowns.set(origin, { dismissals, blockedUntil: now() + blockedForMs });
|
|
24
|
+
return { dismissals, blockedForMs };
|
|
25
|
+
}
|
|
26
|
+
export function recordConsentApproval(origin) {
|
|
27
|
+
cooldowns.delete(origin);
|
|
28
|
+
}
|
|
29
|
+
export function clearAllConsentCooldowns() {
|
|
30
|
+
cooldowns.clear();
|
|
31
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Minionry, Inc.
|
|
2
|
+
// SPDX-License-Identifier: LicenseRef-Minionry-Software
|
|
3
|
+
import { isIP } from 'node:net';
|
|
4
|
+
export const ENDPOINT_BLOCKED_IP_RANGES = [
|
|
5
|
+
{ cidr: '169.254.169.254/32', reason: 'cloud metadata endpoint' },
|
|
6
|
+
{ cidr: '169.254.0.0/16', reason: 'IPv4 link-local (RFC 3927)' },
|
|
7
|
+
{ cidr: '127.0.0.0/8', reason: 'IPv4 loopback' },
|
|
8
|
+
{ cidr: '0.0.0.0/8', reason: '"this network" (reaches loopback)' },
|
|
9
|
+
{ cidr: '10.0.0.0/8', reason: 'RFC 1918 private' },
|
|
10
|
+
{ cidr: '172.16.0.0/12', reason: 'RFC 1918 private' },
|
|
11
|
+
{ cidr: '192.168.0.0/16', reason: 'RFC 1918 private' },
|
|
12
|
+
{ cidr: '100.64.0.0/10', reason: 'carrier-grade NAT (RFC 6598)' },
|
|
13
|
+
{ cidr: '192.0.0.0/24', reason: 'IETF protocol assignments (RFC 6890)' },
|
|
14
|
+
{ cidr: '198.18.0.0/15', reason: 'network benchmarking (RFC 2544)' },
|
|
15
|
+
{ cidr: '224.0.0.0/4', reason: 'IPv4 multicast' },
|
|
16
|
+
{ cidr: '240.0.0.0/4', reason: 'reserved (RFC 1112)' },
|
|
17
|
+
{ cidr: '::/128', reason: 'IPv6 unspecified' },
|
|
18
|
+
{ cidr: '::1/128', reason: 'IPv6 loopback' },
|
|
19
|
+
{ cidr: 'fc00::/7', reason: 'IPv6 unique-local (RFC 4193)' },
|
|
20
|
+
{ cidr: 'fe80::/10', reason: 'IPv6 link-local' },
|
|
21
|
+
{ cidr: 'fec0::/10', reason: 'IPv6 site-local (RFC 3879)' },
|
|
22
|
+
{ cidr: 'ff00::/8', reason: 'IPv6 multicast' },
|
|
23
|
+
{ cidr: '64:ff9b::/96', reason: 'NAT64 (RFC 6052)' },
|
|
24
|
+
{ cidr: '2002::/16', reason: '6to4 (RFC 3056)' },
|
|
25
|
+
];
|
|
26
|
+
function ipv4Value(address) {
|
|
27
|
+
if (isIP(address) !== 4)
|
|
28
|
+
return undefined;
|
|
29
|
+
return address.split('.').reduce((value, octet) => ((value << 8) | Number(octet)) >>> 0, 0);
|
|
30
|
+
}
|
|
31
|
+
function ipv6Value(address) {
|
|
32
|
+
if (isIP(address) !== 6)
|
|
33
|
+
return undefined;
|
|
34
|
+
let text = address;
|
|
35
|
+
const lastColon = text.lastIndexOf(':');
|
|
36
|
+
const tail = text.slice(lastColon + 1);
|
|
37
|
+
if (tail.includes('.')) {
|
|
38
|
+
const v4 = ipv4Value(tail);
|
|
39
|
+
if (v4 === undefined)
|
|
40
|
+
return undefined;
|
|
41
|
+
text = `${text.slice(0, lastColon + 1)}${(v4 >>> 16).toString(16)}:${(v4 & 0xffff).toString(16)}`;
|
|
42
|
+
}
|
|
43
|
+
const halves = text.split('::');
|
|
44
|
+
if (halves.length > 2)
|
|
45
|
+
return undefined;
|
|
46
|
+
const head = halves[0] ? halves[0].split(':') : [];
|
|
47
|
+
const rest = halves.length === 2 && halves[1] ? halves[1].split(':') : [];
|
|
48
|
+
const zeros = 8 - head.length - rest.length;
|
|
49
|
+
if (halves.length === 1 ? zeros !== 0 : zeros < 0)
|
|
50
|
+
return undefined;
|
|
51
|
+
const groups = [...head, ...new Array(Math.max(zeros, 0)).fill('0'), ...rest];
|
|
52
|
+
let value = 0n;
|
|
53
|
+
for (const group of groups) {
|
|
54
|
+
if (!/^[0-9a-f]{1,4}$/i.test(group))
|
|
55
|
+
return undefined;
|
|
56
|
+
value = (value << 16n) | BigInt(Number.parseInt(group, 16));
|
|
57
|
+
}
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
const IPV4_RULES = [];
|
|
61
|
+
const IPV6_RULES = [];
|
|
62
|
+
for (const { cidr, reason } of ENDPOINT_BLOCKED_IP_RANGES) {
|
|
63
|
+
const [address, prefixText] = cidr.split('/');
|
|
64
|
+
const prefix = Number(prefixText);
|
|
65
|
+
if (address.includes(':')) {
|
|
66
|
+
const base = ipv6Value(address);
|
|
67
|
+
if (base === undefined)
|
|
68
|
+
throw new Error(`invalid blocked range: ${cidr}`);
|
|
69
|
+
const shift = BigInt(128 - prefix);
|
|
70
|
+
IPV6_RULES.push({ base: base >> shift, shift, reason });
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
const base = ipv4Value(address);
|
|
74
|
+
if (base === undefined)
|
|
75
|
+
throw new Error(`invalid blocked range: ${cidr}`);
|
|
76
|
+
const mask = (0xffffffff << (32 - prefix)) >>> 0;
|
|
77
|
+
IPV4_RULES.push({ base: (base & mask) >>> 0, mask, reason });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function blockedIpv4Reason(value) {
|
|
81
|
+
return IPV4_RULES.find((rule) => ((value & rule.mask) >>> 0) === rule.base)?.reason;
|
|
82
|
+
}
|
|
83
|
+
function blockedIpv6Reason(value) {
|
|
84
|
+
if (value >> 32n === 0xffffn) {
|
|
85
|
+
const reason = blockedIpv4Reason(Number(value & 0xffffffffn));
|
|
86
|
+
return reason === undefined ? undefined : `IPv4-mapped IPv6: ${reason}`;
|
|
87
|
+
}
|
|
88
|
+
const rule = IPV6_RULES.find((candidate) => value >> candidate.shift === candidate.base);
|
|
89
|
+
if (rule)
|
|
90
|
+
return rule.reason;
|
|
91
|
+
if (value >> 32n === 0n) {
|
|
92
|
+
const reason = blockedIpv4Reason(Number(value & 0xffffffffn));
|
|
93
|
+
return reason === undefined ? undefined : `IPv4-compatible IPv6: ${reason}`;
|
|
94
|
+
}
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
export function findBlockedEndpointAddressReason(address) {
|
|
98
|
+
const bare = address.split('%')[0];
|
|
99
|
+
const v4 = ipv4Value(bare);
|
|
100
|
+
if (v4 !== undefined)
|
|
101
|
+
return blockedIpv4Reason(v4) ?? null;
|
|
102
|
+
if (isIP(bare) === 6) {
|
|
103
|
+
const v6 = ipv6Value(bare);
|
|
104
|
+
return v6 === undefined ? 'unparseable address' : (blockedIpv6Reason(v6) ?? null);
|
|
105
|
+
}
|
|
106
|
+
return 'not an IP address';
|
|
107
|
+
}
|
|
108
|
+
export const MAX_ENDPOINT_URL_BYTES = 2048;
|
|
109
|
+
const LOCAL_ONLY_SUFFIXES = ['localhost', 'local', 'internal', 'home.arpa'];
|
|
110
|
+
export function validateEndpointUrl(value) {
|
|
111
|
+
let url;
|
|
112
|
+
try {
|
|
113
|
+
url = new URL(value);
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return { ok: false, reason: 'is not a valid absolute URL' };
|
|
117
|
+
}
|
|
118
|
+
if (url.protocol !== 'https:')
|
|
119
|
+
return { ok: false, reason: 'must use https' };
|
|
120
|
+
if (url.username !== '' || url.password !== '')
|
|
121
|
+
return { ok: false, reason: 'must not carry credentials' };
|
|
122
|
+
if (url.search !== '' || url.href.includes('?'))
|
|
123
|
+
return { ok: false, reason: 'must not carry a query' };
|
|
124
|
+
if (url.hash !== '' || url.href.includes('#'))
|
|
125
|
+
return { ok: false, reason: 'must not carry a fragment' };
|
|
126
|
+
const host = url.hostname;
|
|
127
|
+
const name = host.replace(/\.+$/, '').toLowerCase();
|
|
128
|
+
if (name === '')
|
|
129
|
+
return { ok: false, reason: 'must name a host' };
|
|
130
|
+
if (isIP(name.replace(/^\[|\]$/g, '')) !== 0) {
|
|
131
|
+
return { ok: false, reason: 'must name a hostname, not an IP address' };
|
|
132
|
+
}
|
|
133
|
+
if (LOCAL_ONLY_SUFFIXES.some((suffix) => name === suffix || name.endsWith(`.${suffix}`))) {
|
|
134
|
+
return { ok: false, reason: 'must not name a local-network host' };
|
|
135
|
+
}
|
|
136
|
+
const canonical = `${url.origin}${url.pathname === '/' ? '' : url.pathname}`;
|
|
137
|
+
if (Buffer.byteLength(canonical, 'utf8') > MAX_ENDPOINT_URL_BYTES) {
|
|
138
|
+
return { ok: false, reason: `must be at most ${MAX_ENDPOINT_URL_BYTES} bytes` };
|
|
139
|
+
}
|
|
140
|
+
return { ok: true, host, url: canonical };
|
|
141
|
+
}
|