@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,279 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Minionry, Inc.
|
|
2
|
+
// SPDX-License-Identifier: LicenseRef-Minionry-Software
|
|
3
|
+
import { mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { basename, dirname } from 'node:path';
|
|
5
|
+
import { zipSync } from 'fflate';
|
|
6
|
+
import { buildDirectoryZipBuffer, ZipTooLargeError } from '../file-transfer-zip.js';
|
|
7
|
+
import { notifyFileChangedAbsolute } from './file-change-notify.js';
|
|
8
|
+
import { appDataDir, guardAppDataPath } from './files-app.js';
|
|
9
|
+
import { guardWorkspacePath, parseWorkspacePath } from './files-workspace.js';
|
|
10
|
+
import { SdkMethodError } from './registry.js';
|
|
11
|
+
export const TRANSFER_CHUNK_BYTES = 256 * 1024;
|
|
12
|
+
export const MAX_TRANSFER_BYTES = 50 * 1024 * 1024;
|
|
13
|
+
const MAX_CHUNK_PAYLOAD_BYTES = TRANSFER_CHUNK_BYTES * 2;
|
|
14
|
+
const SESSION_IDLE_TIMEOUT_MS = 120_000;
|
|
15
|
+
function paramsObject(params) {
|
|
16
|
+
return typeof params === 'object' && params !== null && !Array.isArray(params)
|
|
17
|
+
? params
|
|
18
|
+
: {};
|
|
19
|
+
}
|
|
20
|
+
function requireString(value, name, method) {
|
|
21
|
+
if (typeof value !== 'string' || value === '') {
|
|
22
|
+
throw new SdkMethodError('BAD_REQUEST', `${method} requires a non-empty string '${name}'`);
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
function requireNonNegativeInt(value, name, method) {
|
|
27
|
+
if (typeof value !== 'number' || !Number.isInteger(value) || value < 0) {
|
|
28
|
+
throw new SdkMethodError('BAD_REQUEST', `${method} requires a non-negative integer '${name}'`);
|
|
29
|
+
}
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
function statOrUndefined(path) {
|
|
33
|
+
try {
|
|
34
|
+
return statSync(path);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function reguard(guard) {
|
|
41
|
+
return guard.rooting === 'app'
|
|
42
|
+
? guardAppDataPath(guard.root, guard.rel)
|
|
43
|
+
: guardWorkspacePath(guard.root, guard.rel).resolvedPath;
|
|
44
|
+
}
|
|
45
|
+
export function resolveGuardWithRoot(args, path) {
|
|
46
|
+
const workspaceTarget = parseWorkspacePath({ path });
|
|
47
|
+
if (workspaceTarget === null) {
|
|
48
|
+
const dataDir = appDataDir(args);
|
|
49
|
+
const guard = { rooting: 'app', root: dataDir, rel: path };
|
|
50
|
+
return { guard, resolvedPath: guardAppDataPath(dataDir, path), gitignoreRoot: dataDir };
|
|
51
|
+
}
|
|
52
|
+
const { realWorkingDir, resolvedPath } = guardWorkspacePath(args.workingDir, workspaceTarget.rel);
|
|
53
|
+
const guard = { rooting: 'workspace', root: args.workingDir, rel: workspaceTarget.rel };
|
|
54
|
+
return { guard, resolvedPath, gitignoreRoot: realWorkingDir };
|
|
55
|
+
}
|
|
56
|
+
const uploads = new Map();
|
|
57
|
+
const downloads = new Map();
|
|
58
|
+
let idCounter = 0;
|
|
59
|
+
function mintId(prefix) {
|
|
60
|
+
idCounter += 1;
|
|
61
|
+
return `${prefix}-${Date.now().toString(36)}-${idCounter}`;
|
|
62
|
+
}
|
|
63
|
+
function sweepExpired(sessions) {
|
|
64
|
+
const now = Date.now();
|
|
65
|
+
for (const [id, session] of sessions) {
|
|
66
|
+
if (now - session.lastActivity > SESSION_IDLE_TIMEOUT_MS)
|
|
67
|
+
sessions.delete(id);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function ownsSession(args, session) {
|
|
71
|
+
return session.appId === args.appId && session.spaceKey === args.spaceKey;
|
|
72
|
+
}
|
|
73
|
+
function guessMimeType(path) {
|
|
74
|
+
const ext = path.toLowerCase().split('.').pop() || '';
|
|
75
|
+
if (ext === 'svg')
|
|
76
|
+
return 'image/svg+xml';
|
|
77
|
+
if (ext === 'jpg' || ext === 'jpeg')
|
|
78
|
+
return 'image/jpeg';
|
|
79
|
+
if (ext === 'pdf')
|
|
80
|
+
return 'application/pdf';
|
|
81
|
+
if (['png', 'gif', 'webp', 'bmp', 'tiff', 'ico'].includes(ext))
|
|
82
|
+
return `image/${ext}`;
|
|
83
|
+
if (ext === 'json')
|
|
84
|
+
return 'application/json';
|
|
85
|
+
if (['txt', 'md', 'ts', 'tsx', 'js', 'jsx', 'css', 'html', 'yml', 'yaml'].includes(ext))
|
|
86
|
+
return 'text/plain';
|
|
87
|
+
return 'application/octet-stream';
|
|
88
|
+
}
|
|
89
|
+
export function filesUploadStartHandler(args) {
|
|
90
|
+
sweepExpired(uploads);
|
|
91
|
+
const raw = paramsObject(args.params);
|
|
92
|
+
const path = requireString(raw.path, 'path', args.method);
|
|
93
|
+
requireNonNegativeInt(raw.fileSize, 'fileSize', args.method);
|
|
94
|
+
const totalChunks = requireNonNegativeInt(raw.totalChunks, 'totalChunks', args.method);
|
|
95
|
+
if (totalChunks < 1) {
|
|
96
|
+
throw new SdkMethodError('BAD_REQUEST', `${args.method}: totalChunks must be at least 1`);
|
|
97
|
+
}
|
|
98
|
+
const fileSize = raw.fileSize;
|
|
99
|
+
if (fileSize > MAX_TRANSFER_BYTES) {
|
|
100
|
+
throw new SdkMethodError('BAD_REQUEST', `${args.method}: ${fileSize} bytes exceeds the ${MAX_TRANSFER_BYTES}-byte transfer cap`);
|
|
101
|
+
}
|
|
102
|
+
const overwrite = raw.overwrite === true;
|
|
103
|
+
const { guard, resolvedPath } = resolveGuardWithRoot(args, path);
|
|
104
|
+
const stat = statOrUndefined(resolvedPath);
|
|
105
|
+
if (stat?.isDirectory()) {
|
|
106
|
+
throw new SdkMethodError('BAD_REQUEST', `${args.method}: '${path}' is a directory — cannot upload over it`);
|
|
107
|
+
}
|
|
108
|
+
if (stat && !overwrite) {
|
|
109
|
+
throw new SdkMethodError('BAD_REQUEST', `${args.method}: '${path}' already exists — pass overwrite to replace it`);
|
|
110
|
+
}
|
|
111
|
+
const uploadId = mintId('upload');
|
|
112
|
+
uploads.set(uploadId, {
|
|
113
|
+
appId: args.appId,
|
|
114
|
+
spaceKey: args.spaceKey,
|
|
115
|
+
guard,
|
|
116
|
+
overwrite,
|
|
117
|
+
totalChunks,
|
|
118
|
+
chunks: new Array(totalChunks),
|
|
119
|
+
nextExpectedChunk: 0,
|
|
120
|
+
bytesWritten: 0,
|
|
121
|
+
lastActivity: Date.now(),
|
|
122
|
+
});
|
|
123
|
+
return { uploadId, chunkSize: TRANSFER_CHUNK_BYTES, nextExpectedChunk: 0 };
|
|
124
|
+
}
|
|
125
|
+
function finishUpload(args, uploadId, session) {
|
|
126
|
+
const resolvedPath = reguard(session.guard);
|
|
127
|
+
const stat = statOrUndefined(resolvedPath);
|
|
128
|
+
if (stat?.isDirectory()) {
|
|
129
|
+
uploads.delete(uploadId);
|
|
130
|
+
throw new SdkMethodError('BAD_REQUEST', `${args.method}: '${session.guard.rel}' is a directory — cannot upload over it`);
|
|
131
|
+
}
|
|
132
|
+
if (stat && !session.overwrite) {
|
|
133
|
+
uploads.delete(uploadId);
|
|
134
|
+
throw new SdkMethodError('BAD_REQUEST', `${args.method}: '${session.guard.rel}' already exists — pass overwrite to replace it`);
|
|
135
|
+
}
|
|
136
|
+
mkdirSync(dirname(resolvedPath), { recursive: true });
|
|
137
|
+
writeFileSync(resolvedPath, Buffer.concat(session.chunks));
|
|
138
|
+
uploads.delete(uploadId);
|
|
139
|
+
if (session.guard.rooting === 'workspace') {
|
|
140
|
+
notifyFileChangedAbsolute(session.spaceKey, resolvedPath, 'changed');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
export function filesUploadChunkHandler(args) {
|
|
144
|
+
const raw = paramsObject(args.params);
|
|
145
|
+
const uploadId = requireString(raw.uploadId, 'uploadId', args.method);
|
|
146
|
+
const chunkIndex = requireNonNegativeInt(raw.chunkIndex, 'chunkIndex', args.method);
|
|
147
|
+
const content = requireString(raw.content, 'content', args.method);
|
|
148
|
+
const session = uploads.get(uploadId);
|
|
149
|
+
if (!session || !ownsSession(args, session)) {
|
|
150
|
+
throw new SdkMethodError('BAD_REQUEST', `${args.method}: unknown uploadId '${uploadId}'`);
|
|
151
|
+
}
|
|
152
|
+
session.lastActivity = Date.now();
|
|
153
|
+
if (chunkIndex < session.nextExpectedChunk) {
|
|
154
|
+
return { chunkIndex, bytesWritten: session.bytesWritten };
|
|
155
|
+
}
|
|
156
|
+
if (chunkIndex !== session.nextExpectedChunk) {
|
|
157
|
+
throw new SdkMethodError('BAD_REQUEST', `${args.method}: expected chunk ${session.nextExpectedChunk}, got ${chunkIndex} — chunks must arrive in order`);
|
|
158
|
+
}
|
|
159
|
+
if (chunkIndex >= session.totalChunks) {
|
|
160
|
+
throw new SdkMethodError('BAD_REQUEST', `${args.method}: upload '${uploadId}' already has all ${session.totalChunks} chunks`);
|
|
161
|
+
}
|
|
162
|
+
let bytes;
|
|
163
|
+
try {
|
|
164
|
+
bytes = Buffer.from(content, 'base64');
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
throw new SdkMethodError('BAD_REQUEST', `${args.method}: 'content' is not valid base64`);
|
|
168
|
+
}
|
|
169
|
+
if (bytes.byteLength > MAX_CHUNK_PAYLOAD_BYTES) {
|
|
170
|
+
throw new SdkMethodError('BAD_REQUEST', `${args.method}: chunk ${chunkIndex} is ${bytes.byteLength} bytes — exceeds the per-chunk cap`);
|
|
171
|
+
}
|
|
172
|
+
if (session.bytesWritten + bytes.byteLength > MAX_TRANSFER_BYTES) {
|
|
173
|
+
throw new SdkMethodError('BAD_REQUEST', `${args.method}: upload '${uploadId}' would exceed the ${MAX_TRANSFER_BYTES}-byte transfer cap`);
|
|
174
|
+
}
|
|
175
|
+
reguard(session.guard);
|
|
176
|
+
session.chunks[chunkIndex] = bytes;
|
|
177
|
+
session.nextExpectedChunk = chunkIndex + 1;
|
|
178
|
+
session.bytesWritten += bytes.byteLength;
|
|
179
|
+
if (session.nextExpectedChunk === session.totalChunks) {
|
|
180
|
+
finishUpload(args, uploadId, session);
|
|
181
|
+
}
|
|
182
|
+
return { chunkIndex, bytesWritten: session.bytesWritten };
|
|
183
|
+
}
|
|
184
|
+
export function filesUploadStatusHandler(args) {
|
|
185
|
+
const raw = paramsObject(args.params);
|
|
186
|
+
const uploadId = requireString(raw.uploadId, 'uploadId', args.method);
|
|
187
|
+
const session = uploads.get(uploadId);
|
|
188
|
+
if (!session || !ownsSession(args, session)) {
|
|
189
|
+
throw new SdkMethodError('BAD_REQUEST', `${args.method}: unknown uploadId '${uploadId}'`);
|
|
190
|
+
}
|
|
191
|
+
return { nextExpectedChunk: session.nextExpectedChunk };
|
|
192
|
+
}
|
|
193
|
+
export function filesUploadCancelHandler(args) {
|
|
194
|
+
const raw = paramsObject(args.params);
|
|
195
|
+
const uploadId = requireString(raw.uploadId, 'uploadId', args.method);
|
|
196
|
+
const session = uploads.get(uploadId);
|
|
197
|
+
if (session && ownsSession(args, session))
|
|
198
|
+
uploads.delete(uploadId);
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
export function filesDownloadStartHandler(args) {
|
|
202
|
+
sweepExpired(downloads);
|
|
203
|
+
const raw = paramsObject(args.params);
|
|
204
|
+
const path = requireString(raw.path, 'path', args.method);
|
|
205
|
+
const asZip = raw.asZip === true;
|
|
206
|
+
const { resolvedPath, gitignoreRoot } = resolveGuardWithRoot(args, path);
|
|
207
|
+
const stat = statOrUndefined(resolvedPath);
|
|
208
|
+
if (!stat)
|
|
209
|
+
throw new SdkMethodError('BAD_REQUEST', `${args.method}: '${path}' not found`);
|
|
210
|
+
let content;
|
|
211
|
+
let mimeType;
|
|
212
|
+
let isZip;
|
|
213
|
+
if (stat.isDirectory()) {
|
|
214
|
+
try {
|
|
215
|
+
content = buildDirectoryZipBuffer(resolvedPath, { gitignoreRoot });
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
if (error instanceof ZipTooLargeError) {
|
|
219
|
+
throw new SdkMethodError('BAD_REQUEST', `${args.method}: ${error.message}`);
|
|
220
|
+
}
|
|
221
|
+
throw error;
|
|
222
|
+
}
|
|
223
|
+
mimeType = 'application/zip';
|
|
224
|
+
isZip = true;
|
|
225
|
+
}
|
|
226
|
+
else if (asZip) {
|
|
227
|
+
const entryName = basename(resolvedPath);
|
|
228
|
+
content = Buffer.from(zipSync({ [entryName]: readFileSync(resolvedPath) }, { level: 6 }));
|
|
229
|
+
mimeType = 'application/zip';
|
|
230
|
+
isZip = true;
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
if (stat.size > MAX_TRANSFER_BYTES) {
|
|
234
|
+
throw new SdkMethodError('BAD_REQUEST', `${args.method}: '${path}' is ${stat.size} bytes — exceeds the ${MAX_TRANSFER_BYTES}-byte transfer cap`);
|
|
235
|
+
}
|
|
236
|
+
content = readFileSync(resolvedPath);
|
|
237
|
+
mimeType = guessMimeType(resolvedPath);
|
|
238
|
+
isZip = false;
|
|
239
|
+
}
|
|
240
|
+
if (content.byteLength > MAX_TRANSFER_BYTES) {
|
|
241
|
+
throw new SdkMethodError('BAD_REQUEST', `${args.method}: '${path}' is ${content.byteLength} bytes zipped — exceeds the ${MAX_TRANSFER_BYTES}-byte transfer cap`);
|
|
242
|
+
}
|
|
243
|
+
const downloadId = mintId('download');
|
|
244
|
+
const totalChunks = Math.max(1, Math.ceil(content.byteLength / TRANSFER_CHUNK_BYTES));
|
|
245
|
+
downloads.set(downloadId, {
|
|
246
|
+
appId: args.appId,
|
|
247
|
+
spaceKey: args.spaceKey,
|
|
248
|
+
content,
|
|
249
|
+
totalChunks,
|
|
250
|
+
mimeType,
|
|
251
|
+
isZip,
|
|
252
|
+
lastActivity: Date.now(),
|
|
253
|
+
});
|
|
254
|
+
return { downloadId, fileSize: content.byteLength, totalChunks, chunkSize: TRANSFER_CHUNK_BYTES, mimeType, isZip };
|
|
255
|
+
}
|
|
256
|
+
export function filesDownloadChunkHandler(args) {
|
|
257
|
+
const raw = paramsObject(args.params);
|
|
258
|
+
const downloadId = requireString(raw.downloadId, 'downloadId', args.method);
|
|
259
|
+
const chunkIndex = requireNonNegativeInt(raw.chunkIndex, 'chunkIndex', args.method);
|
|
260
|
+
const session = downloads.get(downloadId);
|
|
261
|
+
if (!session || !ownsSession(args, session)) {
|
|
262
|
+
throw new SdkMethodError('BAD_REQUEST', `${args.method}: unknown downloadId '${downloadId}'`);
|
|
263
|
+
}
|
|
264
|
+
if (chunkIndex >= session.totalChunks) {
|
|
265
|
+
throw new SdkMethodError('BAD_REQUEST', `${args.method}: download '${downloadId}' has only ${session.totalChunks} chunks`);
|
|
266
|
+
}
|
|
267
|
+
session.lastActivity = Date.now();
|
|
268
|
+
const from = chunkIndex * TRANSFER_CHUNK_BYTES;
|
|
269
|
+
const to = Math.min(from + TRANSFER_CHUNK_BYTES, session.content.byteLength);
|
|
270
|
+
return { content: session.content.subarray(from, to).toString('base64') };
|
|
271
|
+
}
|
|
272
|
+
export function filesDownloadCancelHandler(args) {
|
|
273
|
+
const raw = paramsObject(args.params);
|
|
274
|
+
const downloadId = requireString(raw.downloadId, 'downloadId', args.method);
|
|
275
|
+
const session = downloads.get(downloadId);
|
|
276
|
+
if (session && ownsSession(args, session))
|
|
277
|
+
downloads.delete(downloadId);
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Minionry, Inc.
|
|
2
|
+
// SPDX-License-Identifier: LicenseRef-Minionry-Software
|
|
3
|
+
import { readdirSync, realpathSync, statSync } from 'node:fs';
|
|
4
|
+
import { basename, resolve, sep } from 'node:path';
|
|
5
|
+
import { createDirectory, deleteFile, listDirectory, renameFile, writeFile } from '../files.js';
|
|
6
|
+
import { SPACE_DIRNAME, SPACE_LAYOUT } from '../space-layout.js';
|
|
7
|
+
import { readFileContent } from '../websocket/file-utils.js';
|
|
8
|
+
import { notifyFileChangedAbsolute } from './file-change-notify.js';
|
|
9
|
+
import { FILES_APP_MAX_WRITE_BYTES, filesDeleteHandler, filesListHandler, filesMkdirHandler, filesReadEntryHandler, filesReadHandler, filesRenameHandler, filesWriteHandler, } from './files-app.js';
|
|
10
|
+
import { SdkMethodError } from './registry.js';
|
|
11
|
+
import { resolveWithinRoot } from './sandbox.js';
|
|
12
|
+
export const WORKSPACE_PATH_PREFIX = 'workspace:';
|
|
13
|
+
function paramsObject(params) {
|
|
14
|
+
return typeof params === 'object' && params !== null && !Array.isArray(params)
|
|
15
|
+
? params
|
|
16
|
+
: {};
|
|
17
|
+
}
|
|
18
|
+
export function parseWorkspacePath(params) {
|
|
19
|
+
const raw = paramsObject(params);
|
|
20
|
+
if (typeof raw.path !== 'string' || !raw.path.startsWith(WORKSPACE_PATH_PREFIX))
|
|
21
|
+
return null;
|
|
22
|
+
return {
|
|
23
|
+
original: raw.path,
|
|
24
|
+
rel: raw.path.slice(WORKSPACE_PATH_PREFIX.length).replace(/^\/+/, ''),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function resolveFilesReadScopes(params) {
|
|
28
|
+
return parseWorkspacePath(params) === null ? ['files:app'] : ['files:workspace:read'];
|
|
29
|
+
}
|
|
30
|
+
export function resolveFilesWriteScopes(params) {
|
|
31
|
+
return parseWorkspacePath(params) === null ? ['files:app'] : ['files:workspace:write'];
|
|
32
|
+
}
|
|
33
|
+
function entryKey(name) {
|
|
34
|
+
const colon = name.indexOf(':');
|
|
35
|
+
return (colon === -1 ? name : name.slice(0, colon)).replace(/[. ]+$/, '').toLowerCase();
|
|
36
|
+
}
|
|
37
|
+
const SPACE_DIR = entryKey(SPACE_DIRNAME);
|
|
38
|
+
const APP_AREA_ENTRIES = new Set([SPACE_LAYOUT.apps, SPACE_LAYOUT.appsRoster].map(entryKey));
|
|
39
|
+
const APP_AREA_DENIED = "Access denied: a Space's app area ('.minionry/apps', '.minionry/apps.json') is excluded from workspace file access — an app reaches its own sandbox via app-relative paths (files:app), never via workspace paths, and a Space's apps change only through that Space";
|
|
40
|
+
function inSpaceAppArea(segments) {
|
|
41
|
+
for (let index = 0; index + 1 < segments.length; index++) {
|
|
42
|
+
if (entryKey(segments[index]) === SPACE_DIR && APP_AREA_ENTRIES.has(entryKey(segments[index + 1]))) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
export function guardWorkspacePath(workingDir, rel) {
|
|
49
|
+
let realWorkingDir;
|
|
50
|
+
try {
|
|
51
|
+
realWorkingDir = realpathSync(resolve(workingDir));
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
throw new SdkMethodError('INTERNAL', `Space working directory unavailable: ${error instanceof Error ? error.message : String(error)}`);
|
|
55
|
+
}
|
|
56
|
+
const guarded = resolveWithinRoot(realWorkingDir, rel);
|
|
57
|
+
if (!guarded.valid) {
|
|
58
|
+
throw new SdkMethodError('SCOPE_DENIED', 'Access denied: path escapes the Space workspace');
|
|
59
|
+
}
|
|
60
|
+
const below = guarded.resolvedPath.slice(realWorkingDir.length).split(sep).filter(Boolean);
|
|
61
|
+
if (inSpaceAppArea(below))
|
|
62
|
+
throw new SdkMethodError('SCOPE_DENIED', APP_AREA_DENIED);
|
|
63
|
+
return { realWorkingDir, resolvedPath: guarded.resolvedPath };
|
|
64
|
+
}
|
|
65
|
+
export const MAX_APP_AREA_SEARCH_DIRS = 50_000;
|
|
66
|
+
export function containsSpaceAppArea(dir, limit = MAX_APP_AREA_SEARCH_DIRS) {
|
|
67
|
+
const pending = [dir];
|
|
68
|
+
let visited = 0;
|
|
69
|
+
while (pending.length > 0) {
|
|
70
|
+
const current = pending.pop();
|
|
71
|
+
visited += 1;
|
|
72
|
+
if (visited > limit) {
|
|
73
|
+
throw new SdkMethodError('SCOPE_DENIED', `Access denied: this directory is too large to verify that it holds no Space's app area (more than ${limit} directories)`);
|
|
74
|
+
}
|
|
75
|
+
let entries;
|
|
76
|
+
try {
|
|
77
|
+
entries = readdirSync(current, { withFileTypes: true });
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
throw new SdkMethodError('SCOPE_DENIED', "Access denied: part of this directory cannot be read, so it cannot be verified to hold no Space's app area");
|
|
81
|
+
}
|
|
82
|
+
const isSpaceDir = entryKey(basename(current)) === SPACE_DIR;
|
|
83
|
+
for (const entry of entries) {
|
|
84
|
+
if (isSpaceDir && APP_AREA_ENTRIES.has(entryKey(entry.name)))
|
|
85
|
+
return true;
|
|
86
|
+
if (entry.isDirectory())
|
|
87
|
+
pending.push(`${current}${sep}${entry.name}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
function refuseAppAreaTree(resolvedPath) {
|
|
93
|
+
if (statOrUndefined(resolvedPath)?.isDirectory() && containsSpaceAppArea(resolvedPath)) {
|
|
94
|
+
throw new SdkMethodError('SCOPE_DENIED', APP_AREA_DENIED);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function statOrUndefined(resolved) {
|
|
98
|
+
try {
|
|
99
|
+
return statSync(resolved);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function notFound(original) {
|
|
106
|
+
return new SdkMethodError('BAD_REQUEST', `'${original}' not found in the Space workspace`);
|
|
107
|
+
}
|
|
108
|
+
function serviceError(error) {
|
|
109
|
+
const message = error ?? 'file operation failed';
|
|
110
|
+
return /denied|dangerous|invalid path/i.test(message)
|
|
111
|
+
? new SdkMethodError('SCOPE_DENIED', message)
|
|
112
|
+
: new SdkMethodError('BAD_REQUEST', message);
|
|
113
|
+
}
|
|
114
|
+
export function filesReadRoutedHandler(args) {
|
|
115
|
+
const target = parseWorkspacePath(args.params);
|
|
116
|
+
if (target === null)
|
|
117
|
+
return filesReadHandler(args);
|
|
118
|
+
const { realWorkingDir, resolvedPath } = guardWorkspacePath(args.workingDir, target.rel);
|
|
119
|
+
const stat = statOrUndefined(resolvedPath);
|
|
120
|
+
if (!stat)
|
|
121
|
+
throw notFound(target.original);
|
|
122
|
+
if (stat.isDirectory()) {
|
|
123
|
+
throw new SdkMethodError('BAD_REQUEST', `'${target.original}' is a directory — files.read reads files (use files.list)`);
|
|
124
|
+
}
|
|
125
|
+
const result = readFileContent(resolvedPath, realWorkingDir);
|
|
126
|
+
if (result.error)
|
|
127
|
+
throw serviceError(result.error);
|
|
128
|
+
return result.content;
|
|
129
|
+
}
|
|
130
|
+
export function filesWriteRoutedHandler(args) {
|
|
131
|
+
const target = parseWorkspacePath(args.params);
|
|
132
|
+
if (target === null)
|
|
133
|
+
return filesWriteHandler(args);
|
|
134
|
+
const raw = paramsObject(args.params);
|
|
135
|
+
if (typeof raw.content !== 'string') {
|
|
136
|
+
throw new SdkMethodError('BAD_REQUEST', `${args.method} requires params { path, content } with string content`);
|
|
137
|
+
}
|
|
138
|
+
const bytes = Buffer.byteLength(raw.content, 'utf-8');
|
|
139
|
+
if (bytes > FILES_APP_MAX_WRITE_BYTES) {
|
|
140
|
+
throw new SdkMethodError('BAD_REQUEST', `content is ${bytes} bytes — exceeds the ${FILES_APP_MAX_WRITE_BYTES}-byte per-file files.write cap`);
|
|
141
|
+
}
|
|
142
|
+
const { realWorkingDir, resolvedPath } = guardWorkspacePath(args.workingDir, target.rel);
|
|
143
|
+
const result = writeFile(resolvedPath, raw.content, realWorkingDir);
|
|
144
|
+
if (!result.success)
|
|
145
|
+
throw serviceError(result.error);
|
|
146
|
+
notifyFileChangedAbsolute(args.spaceKey, resolvedPath, 'changed');
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
export function filesListRoutedHandler(args) {
|
|
150
|
+
const target = parseWorkspacePath(args.params);
|
|
151
|
+
if (target === null)
|
|
152
|
+
return filesListHandler(args);
|
|
153
|
+
const { realWorkingDir, resolvedPath } = guardWorkspacePath(args.workingDir, target.rel);
|
|
154
|
+
const stat = statOrUndefined(resolvedPath);
|
|
155
|
+
if (!stat)
|
|
156
|
+
throw notFound(target.original);
|
|
157
|
+
if (!stat.isDirectory()) {
|
|
158
|
+
throw new SdkMethodError('BAD_REQUEST', `'${target.original}' is not a directory — files.list lists directories`);
|
|
159
|
+
}
|
|
160
|
+
const result = listDirectory(resolvedPath, realWorkingDir, false);
|
|
161
|
+
if (!result.success || !result.entries)
|
|
162
|
+
throw serviceError(result.error);
|
|
163
|
+
return result.entries.map((entry) => ({
|
|
164
|
+
name: entry.name,
|
|
165
|
+
dir: entry.type === 'directory',
|
|
166
|
+
kind: entry.type,
|
|
167
|
+
size: entry.size,
|
|
168
|
+
modifiedAt: entry.modifiedAt,
|
|
169
|
+
}));
|
|
170
|
+
}
|
|
171
|
+
export function filesReadEntryRoutedHandler(args) {
|
|
172
|
+
const target = parseWorkspacePath(args.params);
|
|
173
|
+
if (target === null)
|
|
174
|
+
return filesReadEntryHandler(args);
|
|
175
|
+
const { realWorkingDir, resolvedPath } = guardWorkspacePath(args.workingDir, target.rel);
|
|
176
|
+
const stat = statOrUndefined(resolvedPath);
|
|
177
|
+
if (!stat)
|
|
178
|
+
throw notFound(target.original);
|
|
179
|
+
if (stat.isDirectory()) {
|
|
180
|
+
throw new SdkMethodError('BAD_REQUEST', `'${target.original}' is a directory — files.readEntry reads files (use files.list)`);
|
|
181
|
+
}
|
|
182
|
+
const result = readFileContent(resolvedPath, realWorkingDir);
|
|
183
|
+
if (result.error)
|
|
184
|
+
throw serviceError(result.error);
|
|
185
|
+
return result.isImage
|
|
186
|
+
? { kind: 'binary', content: result.content, mimeType: result.mimeType ?? 'application/octet-stream' }
|
|
187
|
+
: { kind: 'text', content: result.content };
|
|
188
|
+
}
|
|
189
|
+
export function filesMkdirRoutedHandler(args) {
|
|
190
|
+
const target = parseWorkspacePath(args.params);
|
|
191
|
+
if (target === null)
|
|
192
|
+
return filesMkdirHandler(args);
|
|
193
|
+
const { realWorkingDir, resolvedPath } = guardWorkspacePath(args.workingDir, target.rel);
|
|
194
|
+
const result = createDirectory(resolvedPath, realWorkingDir);
|
|
195
|
+
if (!result.success)
|
|
196
|
+
throw serviceError(result.error);
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
export function filesRenameRoutedHandler(args) {
|
|
200
|
+
const raw = paramsObject(args.params);
|
|
201
|
+
const source = typeof raw.path === 'string' ? parseWorkspacePath({ path: raw.path }) : null;
|
|
202
|
+
const dest = typeof raw.newPath === 'string' ? parseWorkspacePath({ path: raw.newPath }) : null;
|
|
203
|
+
if (source === null && dest === null)
|
|
204
|
+
return filesRenameHandler(args);
|
|
205
|
+
if (source === null || dest === null) {
|
|
206
|
+
throw new SdkMethodError('BAD_REQUEST', 'files.rename: path and newPath must share the same rooting — both workspace: paths or both app-rooted paths');
|
|
207
|
+
}
|
|
208
|
+
const { realWorkingDir, resolvedPath: resolvedOld } = guardWorkspacePath(args.workingDir, source.rel);
|
|
209
|
+
const { resolvedPath: resolvedNew } = guardWorkspacePath(args.workingDir, dest.rel);
|
|
210
|
+
refuseAppAreaTree(resolvedOld);
|
|
211
|
+
if (entryKey(basename(resolvedNew)) === SPACE_DIR && statOrUndefined(resolvedOld)?.isDirectory()) {
|
|
212
|
+
const carried = readdirSync(resolvedOld).some((name) => APP_AREA_ENTRIES.has(entryKey(name)));
|
|
213
|
+
if (carried)
|
|
214
|
+
throw new SdkMethodError('SCOPE_DENIED', APP_AREA_DENIED);
|
|
215
|
+
}
|
|
216
|
+
const result = renameFile(resolvedOld, resolvedNew, realWorkingDir);
|
|
217
|
+
if (!result.success)
|
|
218
|
+
throw serviceError(result.error);
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
export function filesDeleteRoutedHandler(args) {
|
|
222
|
+
const target = parseWorkspacePath(args.params);
|
|
223
|
+
if (target === null)
|
|
224
|
+
return filesDeleteHandler(args);
|
|
225
|
+
const { realWorkingDir, resolvedPath } = guardWorkspacePath(args.workingDir, target.rel);
|
|
226
|
+
if (!statOrUndefined(resolvedPath))
|
|
227
|
+
throw notFound(target.original);
|
|
228
|
+
refuseAppAreaTree(resolvedPath);
|
|
229
|
+
const result = deleteFile(resolvedPath, realWorkingDir);
|
|
230
|
+
if (!result.success)
|
|
231
|
+
throw serviceError(result.error);
|
|
232
|
+
return null;
|
|
233
|
+
}
|