@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,314 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Minionry, Inc.
|
|
2
|
+
// SPDX-License-Identifier: LicenseRef-Minionry-Software
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { lookup as dnsLookup } from 'node:dns/promises';
|
|
5
|
+
import { ModelProfileError, ModelProfileStore, parseProfileExpiry, profileCredentialId, profileModelValue, } from '../../engines/credentials/model-profiles.js';
|
|
6
|
+
import { ProviderCredentialStore } from '../../engines/credentials/provider-credentials.js';
|
|
7
|
+
import { cancelRunsForModel } from './agents.js';
|
|
8
|
+
import { appProfileId, isAppOwnedProfile, listAppProfiles, purgeExpiredAppProfiles, removeAppProfiles, } from './app-endpoints.js';
|
|
9
|
+
import { logAppAudit } from './audit.js';
|
|
10
|
+
import { findBlockedEndpointAddressReason, MAX_ENDPOINT_URL_BYTES, validateEndpointUrl } from './endpoint-url.js';
|
|
11
|
+
import { SdkMethodError } from './registry.js';
|
|
12
|
+
export { validateEndpointUrl } from './endpoint-url.js';
|
|
13
|
+
export const MAX_ENDPOINTS_PER_APP = 8;
|
|
14
|
+
export const DEFAULT_ENDPOINT_TTL_MS = 3_600_000;
|
|
15
|
+
export const MAX_ENDPOINT_TTL_MS = 86_400_000;
|
|
16
|
+
export const ENDPOINT_REAPER_INTERVAL_MS = 60_000;
|
|
17
|
+
const ENDPOINT_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,31}$/;
|
|
18
|
+
const MAX_LABEL_CHARS = 80;
|
|
19
|
+
const MAX_MODEL_CHARS = 200;
|
|
20
|
+
const MAX_API_KEY_BYTES = 4096;
|
|
21
|
+
const ENDPOINT_ENGINES = ['hermes'];
|
|
22
|
+
const CONTROL_CHARS = /[\u0000-\u001f\u007f]/;
|
|
23
|
+
const WHITESPACE_OR_CONTROL = /[\s\u0000-\u001f\u007f]/;
|
|
24
|
+
const REGISTER_SHAPE = '{ id, label, engine, baseUrl, apiKey, model, contextLength?, ttlMs? }';
|
|
25
|
+
const REGISTER_KEYS = new Set([
|
|
26
|
+
'id',
|
|
27
|
+
'label',
|
|
28
|
+
'engine',
|
|
29
|
+
'baseUrl',
|
|
30
|
+
'apiKey',
|
|
31
|
+
'model',
|
|
32
|
+
'contextLength',
|
|
33
|
+
'ttlMs',
|
|
34
|
+
]);
|
|
35
|
+
const UNREGISTER_KEYS = new Set(['id']);
|
|
36
|
+
const defaultLookup = (hostname) => dnsLookup(hostname, { all: true, verbatim: true });
|
|
37
|
+
let lookup = defaultLookup;
|
|
38
|
+
export function setSdkEndpointsLookup(fn) {
|
|
39
|
+
lookup = fn;
|
|
40
|
+
}
|
|
41
|
+
export function resetSdkEndpoints() {
|
|
42
|
+
lookup = defaultLookup;
|
|
43
|
+
stopAppEndpointReaper();
|
|
44
|
+
}
|
|
45
|
+
export async function resolveAndCheck(host, resolve = lookup) {
|
|
46
|
+
let addresses;
|
|
47
|
+
try {
|
|
48
|
+
addresses = await resolve(host);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
throw new SdkMethodError('ENDPOINT_INVALID', 'endpoints.register: the endpoint hostname did not resolve');
|
|
52
|
+
}
|
|
53
|
+
if (addresses.length === 0) {
|
|
54
|
+
throw new SdkMethodError('ENDPOINT_INVALID', 'endpoints.register: the endpoint hostname did not resolve');
|
|
55
|
+
}
|
|
56
|
+
for (const { address } of addresses) {
|
|
57
|
+
const reason = findBlockedEndpointAddressReason(address);
|
|
58
|
+
if (reason !== null) {
|
|
59
|
+
throw new SdkMethodError('ENDPOINT_INVALID', `endpoints.register: the endpoint hostname resolves to a refused address range (${reason})`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function badRequest(message) {
|
|
64
|
+
return new SdkMethodError('BAD_REQUEST', message);
|
|
65
|
+
}
|
|
66
|
+
function strictParams(params, method, shape, keys) {
|
|
67
|
+
if (typeof params !== 'object' || params === null || Array.isArray(params)) {
|
|
68
|
+
throw badRequest(`${method} requires params ${shape}`);
|
|
69
|
+
}
|
|
70
|
+
const raw = params;
|
|
71
|
+
for (const key of Object.keys(raw)) {
|
|
72
|
+
if (!keys.has(key)) {
|
|
73
|
+
const named = key.length > 64 ? `${key.slice(0, 64)}…` : key;
|
|
74
|
+
throw badRequest(`${method}: unknown param '${named}' — expected ${shape}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return raw;
|
|
78
|
+
}
|
|
79
|
+
function parseEndpointId(raw, method) {
|
|
80
|
+
if (typeof raw !== 'string' || !ENDPOINT_ID_PATTERN.test(raw)) {
|
|
81
|
+
throw badRequest(`${method}: id must match ${ENDPOINT_ID_PATTERN.source}`);
|
|
82
|
+
}
|
|
83
|
+
return raw;
|
|
84
|
+
}
|
|
85
|
+
function parseText(raw, field, max) {
|
|
86
|
+
if (typeof raw !== 'string' || raw.trim() === '' || raw.length > max || CONTROL_CHARS.test(raw)) {
|
|
87
|
+
throw badRequest(`endpoints.register: ${field} must be a non-empty string of at most ${max} characters, without control characters`);
|
|
88
|
+
}
|
|
89
|
+
return raw.trim();
|
|
90
|
+
}
|
|
91
|
+
function parsePositiveInteger(raw, field) {
|
|
92
|
+
if (raw === undefined || raw === null)
|
|
93
|
+
return undefined;
|
|
94
|
+
if (typeof raw !== 'number' || !Number.isSafeInteger(raw) || raw <= 0) {
|
|
95
|
+
throw badRequest(`endpoints.register: ${field} must be a positive integer when present`);
|
|
96
|
+
}
|
|
97
|
+
return raw;
|
|
98
|
+
}
|
|
99
|
+
function parseRegisterParams(params) {
|
|
100
|
+
const raw = strictParams(params, 'endpoints.register', REGISTER_SHAPE, REGISTER_KEYS);
|
|
101
|
+
const id = parseEndpointId(raw.id, 'endpoints.register');
|
|
102
|
+
const label = parseText(raw.label, 'label', MAX_LABEL_CHARS);
|
|
103
|
+
if (typeof raw.engine !== 'string' || !ENDPOINT_ENGINES.includes(raw.engine)) {
|
|
104
|
+
throw badRequest(`endpoints.register: engine must be one of ${ENDPOINT_ENGINES.join(', ')}`);
|
|
105
|
+
}
|
|
106
|
+
const engine = raw.engine;
|
|
107
|
+
if (typeof raw.baseUrl !== 'string' || Buffer.byteLength(raw.baseUrl, 'utf8') > MAX_ENDPOINT_URL_BYTES) {
|
|
108
|
+
throw badRequest(`endpoints.register: baseUrl must be a string of at most ${MAX_ENDPOINT_URL_BYTES} bytes`);
|
|
109
|
+
}
|
|
110
|
+
if (typeof raw.apiKey !== 'string' ||
|
|
111
|
+
raw.apiKey === '' ||
|
|
112
|
+
Buffer.byteLength(raw.apiKey, 'utf8') > MAX_API_KEY_BYTES ||
|
|
113
|
+
WHITESPACE_OR_CONTROL.test(raw.apiKey)) {
|
|
114
|
+
throw badRequest(`endpoints.register: apiKey is required — a string of at most ${MAX_API_KEY_BYTES} bytes, without whitespace or control characters`);
|
|
115
|
+
}
|
|
116
|
+
const model = parseText(raw.model, 'model', MAX_MODEL_CHARS);
|
|
117
|
+
const contextLength = parsePositiveInteger(raw.contextLength, 'contextLength');
|
|
118
|
+
const ttlMs = Math.min(parsePositiveInteger(raw.ttlMs, 'ttlMs') ?? DEFAULT_ENDPOINT_TTL_MS, MAX_ENDPOINT_TTL_MS);
|
|
119
|
+
return {
|
|
120
|
+
id,
|
|
121
|
+
label,
|
|
122
|
+
engine,
|
|
123
|
+
baseUrl: raw.baseUrl,
|
|
124
|
+
apiKey: raw.apiKey,
|
|
125
|
+
model,
|
|
126
|
+
...(contextLength !== undefined ? { contextLength } : {}),
|
|
127
|
+
ttlMs,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
export function endpointsRegisterBouncerInput(params) {
|
|
131
|
+
const raw = typeof params === 'object' && params !== null && !Array.isArray(params) ? params : {};
|
|
132
|
+
const verdict = typeof raw.baseUrl === 'string' ? validateEndpointUrl(raw.baseUrl) : undefined;
|
|
133
|
+
return {
|
|
134
|
+
id: typeof raw.id === 'string' ? raw.id.slice(0, 32) : 'invalid',
|
|
135
|
+
engine: typeof raw.engine === 'string' ? raw.engine.slice(0, 32) : 'invalid',
|
|
136
|
+
model: typeof raw.model === 'string' ? raw.model.slice(0, MAX_MODEL_CHARS) : 'invalid',
|
|
137
|
+
host: verdict?.ok ? verdict.host : 'invalid',
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function isExpiredAt(profile, now) {
|
|
141
|
+
const expiresMs = parseProfileExpiry(profile.expiresAt);
|
|
142
|
+
return expiresMs === undefined || expiresMs <= now;
|
|
143
|
+
}
|
|
144
|
+
function endpointIdOf(profile) {
|
|
145
|
+
return profile.id.slice(appProfileId(profile.owner.appId, '').length);
|
|
146
|
+
}
|
|
147
|
+
function hostOf(profile) {
|
|
148
|
+
try {
|
|
149
|
+
return profile.baseUrl ? new URL(profile.baseUrl).hostname : 'none';
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
return 'invalid';
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function toRegisteredEndpoint(profile) {
|
|
156
|
+
return {
|
|
157
|
+
id: endpointIdOf(profile),
|
|
158
|
+
label: profile.label,
|
|
159
|
+
engine: profile.engine,
|
|
160
|
+
modelValue: profileModelValue(profile),
|
|
161
|
+
model: profile.model,
|
|
162
|
+
...(profile.contextLength !== undefined ? { contextLength: profile.contextLength } : {}),
|
|
163
|
+
expiresAt: profile.expiresAt,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function auditFields(profile) {
|
|
167
|
+
return `id=${endpointIdOf(profile)} engine=${profile.engine} model=${profile.model} host=${hostOf(profile)}`;
|
|
168
|
+
}
|
|
169
|
+
function retireEndpoints(profiles, cause) {
|
|
170
|
+
for (const profile of profiles) {
|
|
171
|
+
const { appId, spaceKey } = profile.owner;
|
|
172
|
+
const cancelled = cancelRunsForModel(appId, spaceKey, profileModelValue(profile));
|
|
173
|
+
logAppAudit({
|
|
174
|
+
appId,
|
|
175
|
+
origin: '',
|
|
176
|
+
method: 'endpoints.removed',
|
|
177
|
+
decision: 'allow',
|
|
178
|
+
requestId: randomUUID(),
|
|
179
|
+
spaceKey,
|
|
180
|
+
reason: `endpoints.removed ${auditFields(profile)} (${cause})${cancelled > 0 ? ` cancelledRuns=${cancelled}` : ''}`,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
export async function endpointsRegisterHandler(args) {
|
|
185
|
+
const request = parseRegisterParams(args.params);
|
|
186
|
+
const verdict = validateEndpointUrl(request.baseUrl);
|
|
187
|
+
if (!verdict.ok) {
|
|
188
|
+
throw new SdkMethodError('ENDPOINT_INVALID', `endpoints.register: baseUrl ${verdict.reason}`);
|
|
189
|
+
}
|
|
190
|
+
await resolveAndCheck(verdict.host);
|
|
191
|
+
const owner = { appId: args.appId, spaceKey: args.spaceKey };
|
|
192
|
+
const profileId = appProfileId(args.appId, request.id);
|
|
193
|
+
const profiles = new ModelProfileStore();
|
|
194
|
+
const credentials = new ProviderCredentialStore();
|
|
195
|
+
const now = Date.now();
|
|
196
|
+
const others = listAppProfiles(profiles, owner).filter((profile) => profile.id !== profileId && !isExpiredAt(profile, now));
|
|
197
|
+
if (others.length >= MAX_ENDPOINTS_PER_APP) {
|
|
198
|
+
throw new SdkMethodError('QUOTA_EXCEEDED', `endpoints.register: this app already holds ${MAX_ENDPOINTS_PER_APP} endpoints in this Space — unregister one first`);
|
|
199
|
+
}
|
|
200
|
+
let saved;
|
|
201
|
+
try {
|
|
202
|
+
saved = profiles.save({
|
|
203
|
+
id: profileId,
|
|
204
|
+
label: request.label,
|
|
205
|
+
engine: request.engine,
|
|
206
|
+
model: request.model,
|
|
207
|
+
baseUrl: verdict.url,
|
|
208
|
+
owner,
|
|
209
|
+
expiresAt: new Date(now + request.ttlMs).toISOString(),
|
|
210
|
+
...(request.contextLength !== undefined ? { contextLength: request.contextLength } : {}),
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
if (error instanceof ModelProfileError) {
|
|
215
|
+
throw badRequest(error.code === 'OWNER_CONFLICT'
|
|
216
|
+
? 'endpoints.register: that id is already registered by another owner on this machine — choose another id'
|
|
217
|
+
: `endpoints.register: ${error.message}`);
|
|
218
|
+
}
|
|
219
|
+
throw new SdkMethodError('INTERNAL', 'endpoints.register: the endpoint could not be stored');
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
credentials.setApiKey(profileCredentialId(saved.id), request.apiKey);
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
try {
|
|
226
|
+
credentials.remove(profileCredentialId(saved.id));
|
|
227
|
+
profiles.remove(saved.id);
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
}
|
|
231
|
+
throw new SdkMethodError('INTERNAL', 'endpoints.register: the endpoint key could not be stored');
|
|
232
|
+
}
|
|
233
|
+
const registered = saved;
|
|
234
|
+
logAppAudit({
|
|
235
|
+
appId: args.appId,
|
|
236
|
+
origin: args.origin,
|
|
237
|
+
method: 'endpoints.register',
|
|
238
|
+
decision: 'allow',
|
|
239
|
+
requestId: args.requestId,
|
|
240
|
+
spaceKey: args.spaceKey,
|
|
241
|
+
reason: `endpoints.register ${auditFields(registered)}`,
|
|
242
|
+
});
|
|
243
|
+
return toRegisteredEndpoint(registered);
|
|
244
|
+
}
|
|
245
|
+
export function endpointsUnregisterHandler(args) {
|
|
246
|
+
const raw = strictParams(args.params, 'endpoints.unregister', '{ id }', UNREGISTER_KEYS);
|
|
247
|
+
const id = parseEndpointId(raw.id, 'endpoints.unregister');
|
|
248
|
+
const owner = { appId: args.appId, spaceKey: args.spaceKey };
|
|
249
|
+
const profileId = appProfileId(args.appId, id);
|
|
250
|
+
const profiles = new ModelProfileStore();
|
|
251
|
+
const profile = listAppProfiles(profiles, owner).find((candidate) => candidate.id === profileId);
|
|
252
|
+
if (!profile) {
|
|
253
|
+
throw badRequest(`unknown endpoint id '${id}' — endpoints.unregister removes only this app's endpoints in this Space`);
|
|
254
|
+
}
|
|
255
|
+
new ProviderCredentialStore().remove(profileCredentialId(profile.id));
|
|
256
|
+
profiles.remove(profile.id);
|
|
257
|
+
const cancelled = cancelRunsForModel(args.appId, args.spaceKey, profileModelValue(profile));
|
|
258
|
+
logAppAudit({
|
|
259
|
+
appId: args.appId,
|
|
260
|
+
origin: args.origin,
|
|
261
|
+
method: 'endpoints.unregister',
|
|
262
|
+
decision: 'allow',
|
|
263
|
+
requestId: args.requestId,
|
|
264
|
+
spaceKey: args.spaceKey,
|
|
265
|
+
reason: `endpoints.unregister ${auditFields(profile)}${cancelled > 0 ? ` cancelledRuns=${cancelled}` : ''}`,
|
|
266
|
+
});
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
export function endpointsListHandler(args) {
|
|
270
|
+
const now = Date.now();
|
|
271
|
+
return listAppProfiles(new ModelProfileStore(), { appId: args.appId, spaceKey: args.spaceKey })
|
|
272
|
+
.filter((profile) => !isExpiredAt(profile, now))
|
|
273
|
+
.map(toRegisteredEndpoint);
|
|
274
|
+
}
|
|
275
|
+
export function removeAppEndpoints(appId, spaceKey, cause) {
|
|
276
|
+
const profiles = new ModelProfileStore();
|
|
277
|
+
const owner = { appId, spaceKey };
|
|
278
|
+
const owned = listAppProfiles(profiles, owner);
|
|
279
|
+
if (owned.length === 0)
|
|
280
|
+
return [];
|
|
281
|
+
const removed = new Set(removeAppProfiles(profiles, new ProviderCredentialStore(), owner));
|
|
282
|
+
retireEndpoints(owned.filter((profile) => removed.has(profile.id)), cause);
|
|
283
|
+
return [...removed];
|
|
284
|
+
}
|
|
285
|
+
export function reapExpiredAppEndpoints() {
|
|
286
|
+
try {
|
|
287
|
+
const profiles = new ModelProfileStore();
|
|
288
|
+
const owned = new Map(profiles
|
|
289
|
+
.list()
|
|
290
|
+
.filter(isAppOwnedProfile)
|
|
291
|
+
.map((profile) => [profile.id, profile]));
|
|
292
|
+
const removed = purgeExpiredAppProfiles(profiles, new ProviderCredentialStore(), Date.now());
|
|
293
|
+
retireEndpoints(removed.map((id) => owned.get(id)).filter((profile) => profile !== undefined), 'expired');
|
|
294
|
+
return removed;
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
console.error('[SdkEndpoints] expired-endpoint sweep failed:', error instanceof Error ? error.message : error);
|
|
298
|
+
return [];
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
let reaperTimer = null;
|
|
302
|
+
export function startAppEndpointReaper() {
|
|
303
|
+
if (reaperTimer)
|
|
304
|
+
return;
|
|
305
|
+
reapExpiredAppEndpoints();
|
|
306
|
+
reaperTimer = setInterval(reapExpiredAppEndpoints, ENDPOINT_REAPER_INTERVAL_MS);
|
|
307
|
+
reaperTimer.unref();
|
|
308
|
+
}
|
|
309
|
+
export function stopAppEndpointReaper() {
|
|
310
|
+
if (!reaperTimer)
|
|
311
|
+
return;
|
|
312
|
+
clearInterval(reaperTimer);
|
|
313
|
+
reaperTimer = null;
|
|
314
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Minionry, Inc.
|
|
2
|
+
// SPDX-License-Identifier: LicenseRef-Minionry-Software
|
|
3
|
+
import { realpathSync } from 'node:fs';
|
|
4
|
+
import { join, relative, sep } from 'node:path';
|
|
5
|
+
const listeners = new Set();
|
|
6
|
+
export function registerFileChangeListener(spaceKey, root, cb) {
|
|
7
|
+
const listener = { spaceKey, root, cb };
|
|
8
|
+
listeners.add(listener);
|
|
9
|
+
return () => {
|
|
10
|
+
listeners.delete(listener);
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export function notifyFileChangedAbsolute(spaceKey, absolutePath, kind) {
|
|
14
|
+
for (const listener of listeners) {
|
|
15
|
+
if (listener.spaceKey !== spaceKey)
|
|
16
|
+
continue;
|
|
17
|
+
if (absolutePath !== listener.root && !absolutePath.startsWith(listener.root + sep))
|
|
18
|
+
continue;
|
|
19
|
+
listener.cb({ path: relative(listener.root, absolutePath), kind });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function notifyFileChanged(spaceKey, workingDir, path, kind) {
|
|
23
|
+
if (listeners.size === 0)
|
|
24
|
+
return;
|
|
25
|
+
let absolute;
|
|
26
|
+
try {
|
|
27
|
+
absolute = realpathSync(path.startsWith('/') ? path : join(workingDir, path));
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
notifyFileChangedAbsolute(spaceKey, absolute, kind);
|
|
33
|
+
}
|
|
34
|
+
export function resetFileChangeListeners() {
|
|
35
|
+
listeners.clear();
|
|
36
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Minionry, Inc.
|
|
2
|
+
// SPDX-License-Identifier: LicenseRef-Minionry-Software
|
|
3
|
+
import { mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { dirname, isAbsolute } from 'node:path';
|
|
5
|
+
import { createDirectory, renameFile } from '../files.js';
|
|
6
|
+
import { readFileContent } from '../websocket/file-utils.js';
|
|
7
|
+
import { SdkMethodError } from './registry.js';
|
|
8
|
+
import { provisionAppSandbox, resolveWithinRoot } from './sandbox.js';
|
|
9
|
+
export const FILES_APP_MAX_WRITE_BYTES = 5 * 1024 * 1024;
|
|
10
|
+
function paramsObject(params) {
|
|
11
|
+
return typeof params === 'object' && params !== null && !Array.isArray(params)
|
|
12
|
+
? params
|
|
13
|
+
: {};
|
|
14
|
+
}
|
|
15
|
+
function parsePathParam(params, method) {
|
|
16
|
+
const raw = paramsObject(params);
|
|
17
|
+
if (typeof raw.path !== 'string' || raw.path === '') {
|
|
18
|
+
throw new SdkMethodError('BAD_REQUEST', `${method} requires params { path } — a non-empty string relative to the app data dir ('.' is the root)`);
|
|
19
|
+
}
|
|
20
|
+
return raw.path;
|
|
21
|
+
}
|
|
22
|
+
function notFound(path) {
|
|
23
|
+
return new SdkMethodError('BAD_REQUEST', `'${path}' not found in the app data dir`);
|
|
24
|
+
}
|
|
25
|
+
function parseRenameParams(params, method) {
|
|
26
|
+
const raw = paramsObject(params);
|
|
27
|
+
if (typeof raw.path !== 'string' || raw.path === '' || typeof raw.newPath !== 'string' || raw.newPath === '') {
|
|
28
|
+
throw new SdkMethodError('BAD_REQUEST', `${method} requires params { path, newPath } — non-empty strings relative to the app data dir`);
|
|
29
|
+
}
|
|
30
|
+
return { path: raw.path, newPath: raw.newPath };
|
|
31
|
+
}
|
|
32
|
+
function serviceError(error) {
|
|
33
|
+
const message = error ?? 'file operation failed';
|
|
34
|
+
return /denied|dangerous|invalid path/i.test(message)
|
|
35
|
+
? new SdkMethodError('SCOPE_DENIED', message)
|
|
36
|
+
: new SdkMethodError('BAD_REQUEST', message);
|
|
37
|
+
}
|
|
38
|
+
export function appDataDir(args) {
|
|
39
|
+
try {
|
|
40
|
+
return provisionAppSandbox(args.workingDir, args.appId).dataDir;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
throw new SdkMethodError('INTERNAL', `app sandbox unavailable: ${error instanceof Error ? error.message : String(error)}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export function guardAppDataPath(dataDir, path) {
|
|
47
|
+
if (isAbsolute(path)) {
|
|
48
|
+
throw new SdkMethodError('SCOPE_DENIED', 'Access denied: absolute paths are not allowed — app file paths are relative to the app data dir');
|
|
49
|
+
}
|
|
50
|
+
const guarded = resolveWithinRoot(dataDir, path);
|
|
51
|
+
if (!guarded.valid) {
|
|
52
|
+
throw new SdkMethodError('SCOPE_DENIED', guarded.error ?? 'Access denied: path is outside the app sandbox');
|
|
53
|
+
}
|
|
54
|
+
return guarded.resolvedPath;
|
|
55
|
+
}
|
|
56
|
+
function statOrUndefined(resolved) {
|
|
57
|
+
try {
|
|
58
|
+
return statSync(resolved);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export function filesReadHandler(args) {
|
|
65
|
+
const path = parsePathParam(args.params, args.method);
|
|
66
|
+
const resolved = guardAppDataPath(appDataDir(args), path);
|
|
67
|
+
const stat = statOrUndefined(resolved);
|
|
68
|
+
if (!stat)
|
|
69
|
+
throw notFound(path);
|
|
70
|
+
if (stat.isDirectory()) {
|
|
71
|
+
throw new SdkMethodError('BAD_REQUEST', `'${path}' is a directory — files.read reads files (use files.list)`);
|
|
72
|
+
}
|
|
73
|
+
return readFileSync(resolved, 'utf-8');
|
|
74
|
+
}
|
|
75
|
+
export function filesReadEntryHandler(args) {
|
|
76
|
+
const path = parsePathParam(args.params, args.method);
|
|
77
|
+
const dataDir = appDataDir(args);
|
|
78
|
+
const resolved = guardAppDataPath(dataDir, path);
|
|
79
|
+
const stat = statOrUndefined(resolved);
|
|
80
|
+
if (!stat)
|
|
81
|
+
throw notFound(path);
|
|
82
|
+
if (stat.isDirectory()) {
|
|
83
|
+
throw new SdkMethodError('BAD_REQUEST', `'${path}' is a directory — files.readEntry reads files (use files.list)`);
|
|
84
|
+
}
|
|
85
|
+
const result = readFileContent(resolved, dataDir);
|
|
86
|
+
if (result.error)
|
|
87
|
+
throw serviceError(result.error);
|
|
88
|
+
return result.isImage
|
|
89
|
+
? { kind: 'binary', content: result.content, mimeType: result.mimeType ?? 'application/octet-stream' }
|
|
90
|
+
: { kind: 'text', content: result.content };
|
|
91
|
+
}
|
|
92
|
+
export function filesWriteHandler(args) {
|
|
93
|
+
const path = parsePathParam(args.params, args.method);
|
|
94
|
+
const raw = paramsObject(args.params);
|
|
95
|
+
if (typeof raw.content !== 'string') {
|
|
96
|
+
throw new SdkMethodError('BAD_REQUEST', `${args.method} requires params { path, content } with string content`);
|
|
97
|
+
}
|
|
98
|
+
const bytes = Buffer.byteLength(raw.content, 'utf-8');
|
|
99
|
+
if (bytes > FILES_APP_MAX_WRITE_BYTES) {
|
|
100
|
+
throw new SdkMethodError('BAD_REQUEST', `content is ${bytes} bytes — exceeds the ${FILES_APP_MAX_WRITE_BYTES}-byte per-file files.write cap`);
|
|
101
|
+
}
|
|
102
|
+
const resolved = guardAppDataPath(appDataDir(args), path);
|
|
103
|
+
if (statOrUndefined(resolved)?.isDirectory()) {
|
|
104
|
+
throw new SdkMethodError('BAD_REQUEST', `'${path}' is a directory — cannot write over it`);
|
|
105
|
+
}
|
|
106
|
+
mkdirSync(dirname(resolved), { recursive: true });
|
|
107
|
+
writeFileSync(resolved, raw.content, 'utf-8');
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
export function filesListHandler(args) {
|
|
111
|
+
const path = parsePathParam(args.params, args.method);
|
|
112
|
+
const resolved = guardAppDataPath(appDataDir(args), path);
|
|
113
|
+
const stat = statOrUndefined(resolved);
|
|
114
|
+
if (!stat)
|
|
115
|
+
throw notFound(path);
|
|
116
|
+
if (!stat.isDirectory()) {
|
|
117
|
+
throw new SdkMethodError('BAD_REQUEST', `'${path}' is not a directory — files.list lists directories`);
|
|
118
|
+
}
|
|
119
|
+
return readdirSync(resolved, { withFileTypes: true })
|
|
120
|
+
.map((entry) => {
|
|
121
|
+
const isDir = entry.isDirectory();
|
|
122
|
+
const entryStat = statOrUndefined(`${resolved}/${entry.name}`);
|
|
123
|
+
return {
|
|
124
|
+
name: entry.name,
|
|
125
|
+
dir: isDir,
|
|
126
|
+
kind: isDir ? 'directory' : 'file',
|
|
127
|
+
size: !isDir && entryStat ? entryStat.size : undefined,
|
|
128
|
+
modifiedAt: entryStat ? entryStat.mtime.toISOString() : undefined,
|
|
129
|
+
};
|
|
130
|
+
})
|
|
131
|
+
.sort((a, b) => (a.dir === b.dir ? a.name.localeCompare(b.name) : a.dir ? -1 : 1));
|
|
132
|
+
}
|
|
133
|
+
export function filesDeleteHandler(args) {
|
|
134
|
+
const path = parsePathParam(args.params, args.method);
|
|
135
|
+
const dataDir = appDataDir(args);
|
|
136
|
+
const resolved = guardAppDataPath(dataDir, path);
|
|
137
|
+
if (resolved === dataDir) {
|
|
138
|
+
throw new SdkMethodError('BAD_REQUEST', 'cannot delete the app data root');
|
|
139
|
+
}
|
|
140
|
+
if (!statOrUndefined(resolved))
|
|
141
|
+
throw notFound(path);
|
|
142
|
+
rmSync(resolved, { recursive: true });
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
export function filesMkdirHandler(args) {
|
|
146
|
+
const path = parsePathParam(args.params, args.method);
|
|
147
|
+
const dataDir = appDataDir(args);
|
|
148
|
+
const resolved = guardAppDataPath(dataDir, path);
|
|
149
|
+
const result = createDirectory(resolved, dataDir);
|
|
150
|
+
if (!result.success)
|
|
151
|
+
throw serviceError(result.error);
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
export function filesRenameHandler(args) {
|
|
155
|
+
const { path, newPath } = parseRenameParams(args.params, args.method);
|
|
156
|
+
const dataDir = appDataDir(args);
|
|
157
|
+
const resolvedOld = guardAppDataPath(dataDir, path);
|
|
158
|
+
const resolvedNew = guardAppDataPath(dataDir, newPath);
|
|
159
|
+
const result = renameFile(resolvedOld, resolvedNew, dataDir);
|
|
160
|
+
if (!result.success)
|
|
161
|
+
throw serviceError(result.error);
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Minionry, Inc.
|
|
2
|
+
// SPDX-License-Identifier: LicenseRef-Minionry-Software
|
|
3
|
+
import { statSync } from 'node:fs';
|
|
4
|
+
import { findDefinitions } from '../websocket/file-definition-handlers.js';
|
|
5
|
+
import { streamRgSearch } from '../websocket/file-search-handlers.js';
|
|
6
|
+
import { registerFileChangeListener } from './file-change-notify.js';
|
|
7
|
+
import { appDataDir, guardAppDataPath } from './files-app.js';
|
|
8
|
+
import { guardWorkspacePath, parseWorkspacePath, resolveFilesReadScopes } from './files-workspace.js';
|
|
9
|
+
import { getGrant } from './grant-store.js';
|
|
10
|
+
import { listLiveSubscriptions, registerLiveSubscription } from './live-registry.js';
|
|
11
|
+
import { SdkMethodError } from './registry.js';
|
|
12
|
+
import { dropSubscription } from './revoke.js';
|
|
13
|
+
export { resolveFilesReadScopes as resolveFilesSearchScopes };
|
|
14
|
+
function paramsObject(params) {
|
|
15
|
+
return typeof params === 'object' && params !== null && !Array.isArray(params)
|
|
16
|
+
? params
|
|
17
|
+
: {};
|
|
18
|
+
}
|
|
19
|
+
function requireStringParam(raw, name, method) {
|
|
20
|
+
const value = raw[name];
|
|
21
|
+
if (typeof value !== 'string' || value === '') {
|
|
22
|
+
throw new SdkMethodError('BAD_REQUEST', `${method} requires params { ${name} } — a non-empty string`);
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
function resolveFilesRoot(args, path) {
|
|
27
|
+
const target = parseWorkspacePath({ path });
|
|
28
|
+
const resolved = target === null ? guardAppDataPath(appDataDir(args), path) : guardWorkspacePath(args.workingDir, target.rel).resolvedPath;
|
|
29
|
+
let stat;
|
|
30
|
+
try {
|
|
31
|
+
stat = statSync(resolved);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
throw new SdkMethodError('BAD_REQUEST', `'${path}' not found`);
|
|
35
|
+
}
|
|
36
|
+
if (!stat.isDirectory()) {
|
|
37
|
+
throw new SdkMethodError('BAD_REQUEST', `'${path}' is not a directory`);
|
|
38
|
+
}
|
|
39
|
+
return resolved;
|
|
40
|
+
}
|
|
41
|
+
function refuseDuplicateStream(args) {
|
|
42
|
+
if (listLiveSubscriptions().some((sub) => sub.requestId === args.requestId)) {
|
|
43
|
+
throw new SdkMethodError('BAD_REQUEST', `requestId '${args.requestId}' already holds a live subscription`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function subscriptionViewerId(args) {
|
|
47
|
+
return getGrant(args.appId, args.spaceKey) ? undefined : args.viewerId;
|
|
48
|
+
}
|
|
49
|
+
function toWireMatch(match) {
|
|
50
|
+
return {
|
|
51
|
+
path: match.filePath,
|
|
52
|
+
line: match.line,
|
|
53
|
+
column: match.column,
|
|
54
|
+
lineContent: match.lineContent,
|
|
55
|
+
contextBefore: match.contextBefore,
|
|
56
|
+
contextAfter: match.contextAfter,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function parseSearchOptions(raw) {
|
|
60
|
+
return typeof raw.options === 'object' && raw.options !== null && !Array.isArray(raw.options)
|
|
61
|
+
? raw.options
|
|
62
|
+
: {};
|
|
63
|
+
}
|
|
64
|
+
export function filesSearchHandler(args) {
|
|
65
|
+
const raw = paramsObject(args.params);
|
|
66
|
+
const path = requireStringParam(raw, 'path', args.method);
|
|
67
|
+
const query = requireStringParam(raw, 'query', args.method);
|
|
68
|
+
const options = parseSearchOptions(raw);
|
|
69
|
+
const root = resolveFilesRoot(args, path);
|
|
70
|
+
refuseDuplicateStream(args);
|
|
71
|
+
const sub = {
|
|
72
|
+
requestId: args.requestId,
|
|
73
|
+
appId: args.appId,
|
|
74
|
+
origin: args.origin,
|
|
75
|
+
spaceKey: args.spaceKey,
|
|
76
|
+
kind: 'files.search',
|
|
77
|
+
viewerId: subscriptionViewerId(args),
|
|
78
|
+
teardown: () => handle.cancel(),
|
|
79
|
+
};
|
|
80
|
+
const handle = streamRgSearch(query, options, root, {
|
|
81
|
+
onBatch: (matches) => args.emit('results', { matches: matches.map(toWireMatch) }),
|
|
82
|
+
onComplete: (stats) => {
|
|
83
|
+
args.emit('complete', { totalMatches: stats.totalMatches, fileCount: stats.fileCount, truncated: stats.truncated });
|
|
84
|
+
dropSubscription(sub, 'files.search.complete', 'search finished');
|
|
85
|
+
},
|
|
86
|
+
onError: (message) => {
|
|
87
|
+
args.emit('complete', { totalMatches: 0, fileCount: 0, truncated: false, error: message });
|
|
88
|
+
dropSubscription(sub, 'files.search.complete', `search failed: ${message}`);
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
registerLiveSubscription(sub);
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
function toWireDefinition(entry) {
|
|
95
|
+
return { path: entry.filePath, line: entry.line, column: entry.column, lineContent: entry.lineContent, kind: entry.kind };
|
|
96
|
+
}
|
|
97
|
+
export async function filesDefinitionHandler(args) {
|
|
98
|
+
const raw = paramsObject(args.params);
|
|
99
|
+
const path = requireStringParam(raw, 'path', args.method);
|
|
100
|
+
const symbolName = requireStringParam(raw, 'symbolName', args.method);
|
|
101
|
+
const language = typeof raw.language === 'string' && raw.language !== '' ? raw.language : 'typescript';
|
|
102
|
+
const currentFile = typeof raw.currentFile === 'string' ? raw.currentFile : '';
|
|
103
|
+
const root = resolveFilesRoot(args, path);
|
|
104
|
+
const definitions = await findDefinitions(symbolName, language, currentFile, root);
|
|
105
|
+
return definitions.map(toWireDefinition);
|
|
106
|
+
}
|
|
107
|
+
export function filesSubscribeChangesHandler(args) {
|
|
108
|
+
const raw = paramsObject(args.params);
|
|
109
|
+
const path = requireStringParam(raw, 'path', args.method);
|
|
110
|
+
const root = resolveFilesRoot(args, path);
|
|
111
|
+
refuseDuplicateStream(args);
|
|
112
|
+
let active = true;
|
|
113
|
+
const unsubscribe = registerFileChangeListener(args.spaceKey, root, (change) => {
|
|
114
|
+
if (!active)
|
|
115
|
+
return;
|
|
116
|
+
args.emit('changed', change);
|
|
117
|
+
});
|
|
118
|
+
registerLiveSubscription({
|
|
119
|
+
requestId: args.requestId,
|
|
120
|
+
appId: args.appId,
|
|
121
|
+
origin: args.origin,
|
|
122
|
+
spaceKey: args.spaceKey,
|
|
123
|
+
kind: 'files.subscribeChanges',
|
|
124
|
+
viewerId: subscriptionViewerId(args),
|
|
125
|
+
teardown: () => {
|
|
126
|
+
active = false;
|
|
127
|
+
unsubscribe();
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
return { subscribed: true };
|
|
131
|
+
}
|