@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.
Files changed (476) hide show
  1. package/LICENSE +14 -0
  2. package/PRIVACY.md +357 -0
  3. package/README.md +57 -0
  4. package/THIRD_PARTY_NOTICES.md +245 -0
  5. package/bin/branding.js +4 -0
  6. package/bin/commands/browser-install.js +483 -0
  7. package/bin/commands/create-sandbox.js +542 -0
  8. package/bin/commands/fingerprint.js +101 -0
  9. package/bin/commands/login.js +400 -0
  10. package/bin/commands/logout.js +93 -0
  11. package/bin/commands/minions.js +211 -0
  12. package/bin/commands/sandboxes.js +309 -0
  13. package/bin/commands/status.js +298 -0
  14. package/bin/commands/whoami.js +153 -0
  15. package/bin/launch-env.js +35 -0
  16. package/bin/machine-identity.js +221 -0
  17. package/bin/machine-layout.js +200 -0
  18. package/bin/minion.js +839 -0
  19. package/bin/platform-endpoints.js +8 -0
  20. package/bin/postinstall.js +52 -0
  21. package/bin/pty-doctor.js +326 -0
  22. package/bin/pty-locator.js +110 -0
  23. package/bin/pty-provision.js +186 -0
  24. package/bin/sandbox/Dockerfile +78 -0
  25. package/bin/sandbox/entrypoint.sh +14 -0
  26. package/bin/sandbox/install-repo.sh +53 -0
  27. package/bin/sandbox/seccomp-userns.json +883 -0
  28. package/bin/sandbox-config.js +306 -0
  29. package/dist/bin/machine-identity.js +221 -0
  30. package/dist/bin/machine-layout.js +200 -0
  31. package/dist/electron-host/agent-lease.js +141 -0
  32. package/dist/electron-host/dom-driver.js +642 -0
  33. package/dist/electron-host/ipc-protocol.js +89 -0
  34. package/dist/electron-host/js-driver.js +216 -0
  35. package/dist/electron-host/view-manager.js +254 -0
  36. package/dist/server/cli/engine-resume.js +49 -0
  37. package/dist/server/cli/eta-estimator.js +278 -0
  38. package/dist/server/cli/headless/background-activity.js +109 -0
  39. package/dist/server/cli/headless/claude-invoker-process.js +614 -0
  40. package/dist/server/cli/headless/claude-invoker-stall.js +150 -0
  41. package/dist/server/cli/headless/claude-invoker-stream.js +555 -0
  42. package/dist/server/cli/headless/claude-invoker-tools.js +148 -0
  43. package/dist/server/cli/headless/claude-invoker.js +151 -0
  44. package/dist/server/cli/headless/engine-availability.js +92 -0
  45. package/dist/server/cli/headless/engine-notice.js +185 -0
  46. package/dist/server/cli/headless/haiku-assessments.js +293 -0
  47. package/dist/server/cli/headless/headless-logger.js +75 -0
  48. package/dist/server/cli/headless/index.js +9 -0
  49. package/dist/server/cli/headless/mcp-config.js +314 -0
  50. package/dist/server/cli/headless/native-timeout-detector.js +72 -0
  51. package/dist/server/cli/headless/output-utils.js +71 -0
  52. package/dist/server/cli/headless/prompt-utils.js +82 -0
  53. package/dist/server/cli/headless/resilient-runner.js +326 -0
  54. package/dist/server/cli/headless/retry-strategies.js +292 -0
  55. package/dist/server/cli/headless/runner.js +245 -0
  56. package/dist/server/cli/headless/stall-assessor.js +295 -0
  57. package/dist/server/cli/headless/tool-watchdog.js +447 -0
  58. package/dist/server/cli/headless/types.js +18 -0
  59. package/dist/server/cli/history-portability.js +85 -0
  60. package/dist/server/cli/improvisation-attachments.js +99 -0
  61. package/dist/server/cli/improvisation-history-store.js +121 -0
  62. package/dist/server/cli/improvisation-movements.js +137 -0
  63. package/dist/server/cli/improvisation-output-queue.js +34 -0
  64. package/dist/server/cli/improvisation-retry.js +8 -0
  65. package/dist/server/cli/improvisation-session-manager.js +1016 -0
  66. package/dist/server/cli/improvisation-types.js +31 -0
  67. package/dist/server/cli/plan-lifecycle.js +72 -0
  68. package/dist/server/cli/prompt-builders.js +371 -0
  69. package/dist/server/cli/retry/retry-best-result.js +28 -0
  70. package/dist/server/cli/retry/retry-context-loss.js +75 -0
  71. package/dist/server/cli/retry/retry-recovery-strategies.js +213 -0
  72. package/dist/server/cli/retry/retry-resume-strategy.js +20 -0
  73. package/dist/server/cli/retry/retry-runner-factory.js +92 -0
  74. package/dist/server/cli/retry/retry-tool-results.js +22 -0
  75. package/dist/server/cli/retry/retry-types.js +3 -0
  76. package/dist/server/engines/EngineEvent.js +57 -0
  77. package/dist/server/engines/claude/ClaudeCodeEngine.js +316 -0
  78. package/dist/server/engines/claude/claude-command.js +32 -0
  79. package/dist/server/engines/codex/CodexEngine.js +469 -0
  80. package/dist/server/engines/codex/codex-auth.js +203 -0
  81. package/dist/server/engines/codex/codex-invoker.js +205 -0
  82. package/dist/server/engines/codex/codex-provision.js +215 -0
  83. package/dist/server/engines/credentials/credential-probe.js +88 -0
  84. package/dist/server/engines/credentials/model-profiles.js +249 -0
  85. package/dist/server/engines/credentials/provider-credentials.js +146 -0
  86. package/dist/server/engines/engine-event-stream.js +52 -0
  87. package/dist/server/engines/execution/engine-session.js +380 -0
  88. package/dist/server/engines/factory.js +42 -0
  89. package/dist/server/engines/hermes/HermesEngine.js +628 -0
  90. package/dist/server/engines/hermes/hermes-auth.js +159 -0
  91. package/dist/server/engines/hermes/hermes-gateway-home.js +271 -0
  92. package/dist/server/engines/hermes/hermes-invoker.js +350 -0
  93. package/dist/server/engines/hermes/hermes-provision.js +318 -0
  94. package/dist/server/engines/read-only.js +20 -0
  95. package/dist/server/engines/types.js +27 -0
  96. package/dist/server/handlers/company-execution-registry.js +67 -0
  97. package/dist/server/handlers/company-handlers.js +514 -0
  98. package/dist/server/handlers/company-triggers.js +189 -0
  99. package/dist/server/handlers/company-types.js +3 -0
  100. package/dist/server/index.js +244 -0
  101. package/dist/server/mcp/app-tools-bridge.js +110 -0
  102. package/dist/server/mcp/app-tools-mcp-server.js +99 -0
  103. package/dist/server/mcp/bouncer-integration.js +744 -0
  104. package/dist/server/mcp/browser-mcp-server.js +915 -0
  105. package/dist/server/mcp/browser-redaction.js +103 -0
  106. package/dist/server/mcp/classifier/BouncerClassifier.js +3 -0
  107. package/dist/server/mcp/classifier/ClaudeBouncerClassifier.js +212 -0
  108. package/dist/server/mcp/classifier/factory.js +124 -0
  109. package/dist/server/mcp/classifier/health.js +54 -0
  110. package/dist/server/mcp/classifier/telemetry.js +0 -0
  111. package/dist/server/mcp/company-patterns.js +104 -0
  112. package/dist/server/mcp/department-profiles.js +133 -0
  113. package/dist/server/mcp/quality-bridge.js +153 -0
  114. package/dist/server/mcp/quality-mcp-server.js +105 -0
  115. package/dist/server/mcp/security-analysis.js +384 -0
  116. package/dist/server/mcp/security-audit.js +152 -0
  117. package/dist/server/mcp/security-patterns.js +314 -0
  118. package/dist/server/mcp/server.js +194 -0
  119. package/dist/server/mcp/test-utils/agent-simulator.js +301 -0
  120. package/dist/server/mcp/test-utils/e2e-agent.js +103 -0
  121. package/dist/server/mcp/test-utils/e2e-capture.js +92 -0
  122. package/dist/server/mcp/test-utils/e2e-client.js +116 -0
  123. package/dist/server/mcp/test-utils/e2e-fixtures.js +87 -0
  124. package/dist/server/mcp/test-utils/e2e-gating.js +8 -0
  125. package/dist/server/mcp/test-utils/e2e-harness.js +8 -0
  126. package/dist/server/mcp/test-utils/e2e-scenarios.js +105 -0
  127. package/dist/server/routes/files.js +18 -0
  128. package/dist/server/routes/improvise.js +64 -0
  129. package/dist/server/routes/index.js +7 -0
  130. package/dist/server/routes/instances.js +41 -0
  131. package/dist/server/routes/internal.js +900 -0
  132. package/dist/server/routes/notifications.js +110 -0
  133. package/dist/server/server-setup.js +282 -0
  134. package/dist/server/services/agent-push.js +148 -0
  135. package/dist/server/services/analytics.js +197 -0
  136. package/dist/server/services/apps/build.js +249 -0
  137. package/dist/server/services/apps/greenlight.js +199 -0
  138. package/dist/server/services/apps/install.js +101 -0
  139. package/dist/server/services/apps/layout.js +35 -0
  140. package/dist/server/services/apps/serve.js +665 -0
  141. package/dist/server/services/apps/space-apps.js +248 -0
  142. package/dist/server/services/auth.js +48 -0
  143. package/dist/server/services/browser/chrome-sandbox.js +245 -0
  144. package/dist/server/services/browser/chromium-launch-spec.js +51 -0
  145. package/dist/server/services/browser/claude-reasoner.js +669 -0
  146. package/dist/server/services/browser/debug-log.js +3 -0
  147. package/dist/server/services/browser/driver-state.js +546 -0
  148. package/dist/server/services/browser/electron-provision.js +256 -0
  149. package/dist/server/services/browser/host-internal.js +245 -0
  150. package/dist/server/services/browser/host-mock.js +334 -0
  151. package/dist/server/services/browser/host-types.js +3 -0
  152. package/dist/server/services/browser/host.js +1067 -0
  153. package/dist/server/services/browser/index.js +4 -0
  154. package/dist/server/services/browser/ipc-client.js +354 -0
  155. package/dist/server/services/browser/pipewire-sink.js +199 -0
  156. package/dist/server/services/browser/preflight.js +590 -0
  157. package/dist/server/services/browser/reasoning-agent.js +659 -0
  158. package/dist/server/services/browser/sandbox-policy.js +169 -0
  159. package/dist/server/services/browser/stderr-noise-filter.js +26 -0
  160. package/dist/server/services/browser/telemetry.js +107 -0
  161. package/dist/server/services/browser/xvfb-supervisor.js +139 -0
  162. package/dist/server/services/browser-frame-policy.js +56 -0
  163. package/dist/server/services/client-id.js +32 -0
  164. package/dist/server/services/collab/registry.js +408 -0
  165. package/dist/server/services/collab/session.js +489 -0
  166. package/dist/server/services/collab/text-diff.js +28 -0
  167. package/dist/server/services/collab/types.js +19 -0
  168. package/dist/server/services/e2ee-group-key.js +197 -0
  169. package/dist/server/services/e2ee-identity.js +203 -0
  170. package/dist/server/services/file-explorer-ops.js +328 -0
  171. package/dist/server/services/file-transfer-zip.js +188 -0
  172. package/dist/server/services/files.js +111 -0
  173. package/dist/server/services/git/branch-ops.js +62 -0
  174. package/dist/server/services/git/command.js +68 -0
  175. package/dist/server/services/git/commit-message.js +74 -0
  176. package/dist/server/services/git/diff-ops.js +56 -0
  177. package/dist/server/services/git/haiku.js +76 -0
  178. package/dist/server/services/git/head-watcher.js +226 -0
  179. package/dist/server/services/git/index.js +17 -0
  180. package/dist/server/services/git/log-ops.js +98 -0
  181. package/dist/server/services/git/merge-ops.js +314 -0
  182. package/dist/server/services/git/parsers.js +401 -0
  183. package/dist/server/services/git/remote-ops.js +192 -0
  184. package/dist/server/services/git/status-ops.js +105 -0
  185. package/dist/server/services/git/suggestion-ops.js +80 -0
  186. package/dist/server/services/git/tag-ops.js +36 -0
  187. package/dist/server/services/git/types.js +4 -0
  188. package/dist/server/services/git/worktree-ops.js +114 -0
  189. package/dist/server/services/instances.js +337 -0
  190. package/dist/server/services/model-catalog.js +247 -0
  191. package/dist/server/services/observability/posthog.js +153 -0
  192. package/dist/server/services/pathUtils.js +114 -0
  193. package/dist/server/services/plan/agent-loader.js +97 -0
  194. package/dist/server/services/plan/agent-manager.js +363 -0
  195. package/dist/server/services/plan/agent-resolver.js +74 -0
  196. package/dist/server/services/plan/agents/assess-stall.md +33 -0
  197. package/dist/server/services/plan/agents/browser-planner.md +53 -0
  198. package/dist/server/services/plan/agents/browser-reasoner.md +114 -0
  199. package/dist/server/services/plan/agents/check-injection.md +95 -0
  200. package/dist/server/services/plan/agents/classify-error.md +30 -0
  201. package/dist/server/services/plan/agents/code-review.md +142 -0
  202. package/dist/server/services/plan/agents/commit-message.md +28 -0
  203. package/dist/server/services/plan/agents/detect-context-loss.md +30 -0
  204. package/dist/server/services/plan/agents/execute-issue.md +58 -0
  205. package/dist/server/services/plan/agents/fix-quality.md +25 -0
  206. package/dist/server/services/plan/agents/plan-coordinator.md +72 -0
  207. package/dist/server/services/plan/agents/pr-description.md +29 -0
  208. package/dist/server/services/plan/agents/retry-task.md +27 -0
  209. package/dist/server/services/plan/agents/review-code.md +38 -0
  210. package/dist/server/services/plan/agents/review-quality.md +45 -0
  211. package/dist/server/services/plan/agents/triage-findings.md +78 -0
  212. package/dist/server/services/plan/agents/verify-review.md +57 -0
  213. package/dist/server/services/plan/approval-evaluator.js +143 -0
  214. package/dist/server/services/plan/board-archive.js +102 -0
  215. package/dist/server/services/plan/board-config.js +108 -0
  216. package/dist/server/services/plan/board-export.js +214 -0
  217. package/dist/server/services/plan/composer-prompt.js +115 -0
  218. package/dist/server/services/plan/composer.js +664 -0
  219. package/dist/server/services/plan/config-installer.js +103 -0
  220. package/dist/server/services/plan/dependency-resolver.js +137 -0
  221. package/dist/server/services/plan/executor.js +696 -0
  222. package/dist/server/services/plan/front-matter.js +43 -0
  223. package/dist/server/services/plan/issue-classification.js +10 -0
  224. package/dist/server/services/plan/issue-effort.js +182 -0
  225. package/dist/server/services/plan/issue-loader.js +35 -0
  226. package/dist/server/services/plan/issue-prompt-builder.js +152 -0
  227. package/dist/server/services/plan/issue-retry.js +42 -0
  228. package/dist/server/services/plan/issue-writer.js +102 -0
  229. package/dist/server/services/plan/metrics-collector.js +272 -0
  230. package/dist/server/services/plan/output-manager.js +77 -0
  231. package/dist/server/services/plan/parser-core.js +435 -0
  232. package/dist/server/services/plan/parser-migration.js +108 -0
  233. package/dist/server/services/plan/parser.js +258 -0
  234. package/dist/server/services/plan/plan-runner-factory.js +68 -0
  235. package/dist/server/services/plan/pm-timeline.js +320 -0
  236. package/dist/server/services/plan/progress-log.js +101 -0
  237. package/dist/server/services/plan/prompt-builder.js +142 -0
  238. package/dist/server/services/plan/quality-delta.js +274 -0
  239. package/dist/server/services/plan/readiness-planner.js +34 -0
  240. package/dist/server/services/plan/review-gate.js +409 -0
  241. package/dist/server/services/plan/review-report.js +134 -0
  242. package/dist/server/services/plan/review-verdict-stream.js +161 -0
  243. package/dist/server/services/plan/state-reconciler.js +238 -0
  244. package/dist/server/services/plan/template-diff.js +175 -0
  245. package/dist/server/services/plan/template-instantiator.js +516 -0
  246. package/dist/server/services/plan/template-manifest.js +56 -0
  247. package/dist/server/services/plan/template-types.js +3 -0
  248. package/dist/server/services/plan/trigger-evaluator.js +211 -0
  249. package/dist/server/services/plan/types.js +3 -0
  250. package/dist/server/services/plan/watcher.js +73 -0
  251. package/dist/server/services/platform-credentials.js +56 -0
  252. package/dist/server/services/platform-e2ee.js +421 -0
  253. package/dist/server/services/platform-envelope.js +102 -0
  254. package/dist/server/services/platform-heartbeat.js +88 -0
  255. package/dist/server/services/platform-reconnect.js +60 -0
  256. package/dist/server/services/platform-token-lifecycle.js +95 -0
  257. package/dist/server/services/platform-url.js +17 -0
  258. package/dist/server/services/platform.js +656 -0
  259. package/dist/server/services/runtime-info.js +9 -0
  260. package/dist/server/services/schedule/agent-schedule-store.js +232 -0
  261. package/dist/server/services/schedule/agent-scheduler.js +323 -0
  262. package/dist/server/services/schedule/daily-timing.js +105 -0
  263. package/dist/server/services/schedule/quality-schedule-launch.js +11 -0
  264. package/dist/server/services/schedule/quality-schedule-store.js +211 -0
  265. package/dist/server/services/schedule/quality-scheduler.js +215 -0
  266. package/dist/server/services/schedule/run-bootstrap.js +329 -0
  267. package/dist/server/services/schedule/schedule-store.js +238 -0
  268. package/dist/server/services/schedule/schedule-wire.js +77 -0
  269. package/dist/server/services/schedule/scheduler.js +310 -0
  270. package/dist/server/services/schedule/template-source.js +97 -0
  271. package/dist/server/services/sdk/agent-sessions.js +264 -0
  272. package/dist/server/services/sdk/agents.js +557 -0
  273. package/dist/server/services/sdk/app-endpoints.js +48 -0
  274. package/dist/server/services/sdk/app-tools.js +303 -0
  275. package/dist/server/services/sdk/audit.js +48 -0
  276. package/dist/server/services/sdk/browser-transport.js +188 -0
  277. package/dist/server/services/sdk/browser.js +605 -0
  278. package/dist/server/services/sdk/composer.js +129 -0
  279. package/dist/server/services/sdk/connect.js +273 -0
  280. package/dist/server/services/sdk/consent-cooldown.js +31 -0
  281. package/dist/server/services/sdk/endpoint-url.js +141 -0
  282. package/dist/server/services/sdk/endpoints.js +314 -0
  283. package/dist/server/services/sdk/file-change-notify.js +36 -0
  284. package/dist/server/services/sdk/files-app.js +163 -0
  285. package/dist/server/services/sdk/files-search.js +131 -0
  286. package/dist/server/services/sdk/files-transfer.js +279 -0
  287. package/dist/server/services/sdk/files-workspace.js +233 -0
  288. package/dist/server/services/sdk/git.js +520 -0
  289. package/dist/server/services/sdk/grant-store.js +318 -0
  290. package/dist/server/services/sdk/host-ops.js +84 -0
  291. package/dist/server/services/sdk/inference-claude.js +214 -0
  292. package/dist/server/services/sdk/inference.js +247 -0
  293. package/dist/server/services/sdk/live-registry.js +42 -0
  294. package/dist/server/services/sdk/local-app-bindings.js +71 -0
  295. package/dist/server/services/sdk/pipeline.js +190 -0
  296. package/dist/server/services/sdk/pm-execute.js +228 -0
  297. package/dist/server/services/sdk/pm-planning-tap.js +29 -0
  298. package/dist/server/services/sdk/pm-schedule.js +165 -0
  299. package/dist/server/services/sdk/pm.js +634 -0
  300. package/dist/server/services/sdk/quality-change-notify.js +20 -0
  301. package/dist/server/services/sdk/quality-schedule.js +165 -0
  302. package/dist/server/services/sdk/quality.js +602 -0
  303. package/dist/server/services/sdk/rate-limits.js +198 -0
  304. package/dist/server/services/sdk/registry.js +597 -0
  305. package/dist/server/services/sdk/revoke.js +86 -0
  306. package/dist/server/services/sdk/run-profile.js +41 -0
  307. package/dist/server/services/sdk/sandbox.js +89 -0
  308. package/dist/server/services/sdk/storage.js +110 -0
  309. package/dist/server/services/sdk/streams.js +147 -0
  310. package/dist/server/services/sdk/terminal.js +201 -0
  311. package/dist/server/services/sdk/test-utils/workshop-browser-app.js +466 -0
  312. package/dist/server/services/sentry.js +99 -0
  313. package/dist/server/services/settings.js +374 -0
  314. package/dist/server/services/snippets/expander.js +87 -0
  315. package/dist/server/services/snippets/index.js +6 -0
  316. package/dist/server/services/snippets/loader.js +119 -0
  317. package/dist/server/services/snippets/parser.js +52 -0
  318. package/dist/server/services/snippets/types.js +3 -0
  319. package/dist/server/services/snippets/watcher.js +60 -0
  320. package/dist/server/services/space-layout.js +80 -0
  321. package/dist/server/services/system-prompts.js +219 -0
  322. package/dist/server/services/terminal/pty-manager.js +206 -0
  323. package/dist/server/services/terminal/pty-utils.js +150 -0
  324. package/dist/server/services/timeline/assembler.js +890 -0
  325. package/dist/server/services/timeline/contract.js +196 -0
  326. package/dist/server/services/timeline/index.js +4 -0
  327. package/dist/server/services/websocket/agent-schedule-handlers.js +217 -0
  328. package/dist/server/services/websocket/apps-handlers.js +399 -0
  329. package/dist/server/services/websocket/ask-user-question-bridge.js +112 -0
  330. package/dist/server/services/websocket/autocomplete-index.js +415 -0
  331. package/dist/server/services/websocket/autocomplete-match.js +243 -0
  332. package/dist/server/services/websocket/autocomplete.js +407 -0
  333. package/dist/server/services/websocket/browser-agent-runtime.js +208 -0
  334. package/dist/server/services/websocket/browser-device-profile.js +46 -0
  335. package/dist/server/services/websocket/browser-handlers.js +377 -0
  336. package/dist/server/services/websocket/browser-host-broadcast.js +179 -0
  337. package/dist/server/services/websocket/browser-ice-config.js +121 -0
  338. package/dist/server/services/websocket/browser-payloads.js +222 -0
  339. package/dist/server/services/websocket/browser-viewer-lifecycle.js +84 -0
  340. package/dist/server/services/websocket/browser-viewer-registry.js +118 -0
  341. package/dist/server/services/websocket/collab-broadcast.js +11 -0
  342. package/dist/server/services/websocket/collab-handlers.js +116 -0
  343. package/dist/server/services/websocket/commit-suggestion-service.js +132 -0
  344. package/dist/server/services/websocket/debug-log.js +38 -0
  345. package/dist/server/services/websocket/dispatch-util.js +10 -0
  346. package/dist/server/services/websocket/engine-auth-handlers.js +323 -0
  347. package/dist/server/services/websocket/engine-credential-watcher.js +83 -0
  348. package/dist/server/services/websocket/engine-profile-handlers.js +92 -0
  349. package/dist/server/services/websocket/file-definition-handlers.js +180 -0
  350. package/dist/server/services/websocket/file-download-handler.js +291 -0
  351. package/dist/server/services/websocket/file-explorer-handlers.js +291 -0
  352. package/dist/server/services/websocket/file-search-handlers.js +267 -0
  353. package/dist/server/services/websocket/file-share-handlers.js +328 -0
  354. package/dist/server/services/websocket/file-transfer-http.js +361 -0
  355. package/dist/server/services/websocket/file-upload-handler.js +267 -0
  356. package/dist/server/services/websocket/file-utils.js +346 -0
  357. package/dist/server/services/websocket/git-branch-handlers.js +90 -0
  358. package/dist/server/services/websocket/git-diff-handlers.js +59 -0
  359. package/dist/server/services/websocket/git-handlers.js +212 -0
  360. package/dist/server/services/websocket/git-head-watcher.js +85 -0
  361. package/dist/server/services/websocket/git-log-handlers.js +104 -0
  362. package/dist/server/services/websocket/git-pr-handlers.js +56 -0
  363. package/dist/server/services/websocket/git-tag-handlers.js +53 -0
  364. package/dist/server/services/websocket/git-utils.js +11 -0
  365. package/dist/server/services/websocket/git-worktree-handlers.js +521 -0
  366. package/dist/server/services/websocket/handler-context.js +113 -0
  367. package/dist/server/services/websocket/handler.js +1197 -0
  368. package/dist/server/services/websocket/index.js +5 -0
  369. package/dist/server/services/websocket/movement-written-files.js +26 -0
  370. package/dist/server/services/websocket/msg-id-tracker.js +48 -0
  371. package/dist/server/services/websocket/plan-board-handlers.js +410 -0
  372. package/dist/server/services/websocket/plan-execution-handlers.js +244 -0
  373. package/dist/server/services/websocket/plan-handlers.js +67 -0
  374. package/dist/server/services/websocket/plan-helpers.js +161 -0
  375. package/dist/server/services/websocket/plan-issue-handlers.js +168 -0
  376. package/dist/server/services/websocket/plan-sprint-handlers.js +190 -0
  377. package/dist/server/services/websocket/plan-transfer-handlers.js +310 -0
  378. package/dist/server/services/websocket/pm-timeline-broadcast.js +28 -0
  379. package/dist/server/services/websocket/post-session-scan.js +132 -0
  380. package/dist/server/services/websocket/prompt-queue-handlers.js +96 -0
  381. package/dist/server/services/websocket/quality-agent-cost.js +229 -0
  382. package/dist/server/services/websocket/quality-agent-notes.js +113 -0
  383. package/dist/server/services/websocket/quality-change-activity.js +187 -0
  384. package/dist/server/services/websocket/quality-complexity.js +345 -0
  385. package/dist/server/services/websocket/quality-dependencies.js +445 -0
  386. package/dist/server/services/websocket/quality-duplication.js +162 -0
  387. package/dist/server/services/websocket/quality-eta.js +65 -0
  388. package/dist/server/services/websocket/quality-fingerprint.js +104 -0
  389. package/dist/server/services/websocket/quality-grading.js +575 -0
  390. package/dist/server/services/websocket/quality-handlers.js +48 -0
  391. package/dist/server/services/websocket/quality-linting.js +270 -0
  392. package/dist/server/services/websocket/quality-operations.js +54 -0
  393. package/dist/server/services/websocket/quality-persistence.js +410 -0
  394. package/dist/server/services/websocket/quality-review-agent.js +526 -0
  395. package/dist/server/services/websocket/quality-run.js +220 -0
  396. package/dist/server/services/websocket/quality-secrets.js +0 -0
  397. package/dist/server/services/websocket/quality-service.js +775 -0
  398. package/dist/server/services/websocket/quality-tools.js +383 -0
  399. package/dist/server/services/websocket/quality-triage-agent.js +101 -0
  400. package/dist/server/services/websocket/quality-types.js +149 -0
  401. package/dist/server/services/websocket/recommended-answers.js +28 -0
  402. package/dist/server/services/websocket/schedule-handlers.js +197 -0
  403. package/dist/server/services/websocket/sdk-agent-host.js +569 -0
  404. package/dist/server/services/websocket/sdk-browser-host.js +459 -0
  405. package/dist/server/services/websocket/sdk-composer-host.js +10 -0
  406. package/dist/server/services/websocket/sdk-git-host.js +115 -0
  407. package/dist/server/services/websocket/sdk-handlers.js +242 -0
  408. package/dist/server/services/websocket/sdk-pm-execution-host.js +63 -0
  409. package/dist/server/services/websocket/sdk-pm-schedule-host.js +21 -0
  410. package/dist/server/services/websocket/sdk-quality-schedule-host.js +34 -0
  411. package/dist/server/services/websocket/sdk-terminal-host.js +140 -0
  412. package/dist/server/services/websocket/session-handlers.js +565 -0
  413. package/dist/server/services/websocket/session-history.js +335 -0
  414. package/dist/server/services/websocket/session-initialization.js +315 -0
  415. package/dist/server/services/websocket/session-registry.js +340 -0
  416. package/dist/server/services/websocket/session-role-directory.js +39 -0
  417. package/dist/server/services/websocket/settings-handlers.js +276 -0
  418. package/dist/server/services/websocket/skill-handlers.js +324 -0
  419. package/dist/server/services/websocket/skill-watcher.js +83 -0
  420. package/dist/server/services/websocket/snippet-handlers.js +261 -0
  421. package/dist/server/services/websocket/tab-broadcast.js +35 -0
  422. package/dist/server/services/websocket/tab-event-buffer.js +110 -0
  423. package/dist/server/services/websocket/tab-event-replay.js +47 -0
  424. package/dist/server/services/websocket/tab-handlers.js +211 -0
  425. package/dist/server/services/websocket/terminal-handlers.js +268 -0
  426. package/dist/server/services/websocket/types.js +96 -0
  427. package/dist/server/types/company-messages.js +102 -0
  428. package/dist/server/utils/agent-manager.js +202 -0
  429. package/dist/server/utils/console-timestamps.js +24 -0
  430. package/dist/server/utils/file-store.js +112 -0
  431. package/dist/server/utils/paths.js +28 -0
  432. package/dist/server/utils/port-manager.js +43 -0
  433. package/dist/server/utils/port.js +204 -0
  434. package/electron-host/dist/agent-lease.js +141 -0
  435. package/electron-host/dist/capture-recovery.js +77 -0
  436. package/electron-host/dist/codec-policy.js +37 -0
  437. package/electron-host/dist/controller-channel.js +60 -0
  438. package/electron-host/dist/controller-handlers.js +153 -0
  439. package/electron-host/dist/device-emulator.js +106 -0
  440. package/electron-host/dist/dom-driver.js +642 -0
  441. package/electron-host/dist/domain-registry.js +159 -0
  442. package/electron-host/dist/domain-validators.js +308 -0
  443. package/electron-host/dist/editable-focus-reporter.js +143 -0
  444. package/electron-host/dist/fake-peer.test-util.js +105 -0
  445. package/electron-host/dist/host-log.js +31 -0
  446. package/electron-host/dist/host-wire.js +11 -0
  447. package/electron-host/dist/input-router.js +421 -0
  448. package/electron-host/dist/ipc-protocol.js +89 -0
  449. package/electron-host/dist/ipc-server.js +193 -0
  450. package/electron-host/dist/js-driver.js +216 -0
  451. package/electron-host/dist/main-guards.js +70 -0
  452. package/electron-host/dist/main.js +716 -0
  453. package/electron-host/dist/roster-sync.js +87 -0
  454. package/electron-host/dist/rpc-context.js +1 -0
  455. package/electron-host/dist/rpc-tab-methods.js +138 -0
  456. package/electron-host/dist/rpc-webrtc-methods.js +121 -0
  457. package/electron-host/dist/screen-capture-cache.js +147 -0
  458. package/electron-host/dist/sdk-injector.js +166 -0
  459. package/electron-host/dist/sdp.js +102 -0
  460. package/electron-host/dist/session-surface.js +78 -0
  461. package/electron-host/dist/surface-sizing.js +59 -0
  462. package/electron-host/dist/view-manager.js +254 -0
  463. package/electron-host/dist/viewer-registry.js +46 -0
  464. package/electron-host/dist/viewer-session.js +288 -0
  465. package/electron-host/dist/webrtc-channels.js +14 -0
  466. package/electron-host/dist/webrtc-controller.js +57 -0
  467. package/electron-host/dist/webrtc-ice-filter.js +13 -0
  468. package/electron-host/dist/webrtc-peer.js +45 -0
  469. package/electron-host/package.json +12 -0
  470. package/electron-host/tsconfig.json +23 -0
  471. package/electron-host/webrtc-controller-preload.cjs +3 -0
  472. package/electron-host/webrtc-controller.html +14 -0
  473. package/package.json +85 -0
  474. package/vendor/e2ee/LICENSE +14 -0
  475. package/vendor/e2ee/package.json +14 -0
  476. package/vendor/e2ee/src/index.js +310 -0
@@ -0,0 +1,445 @@
1
+ // Copyright (c) 2025-present Minionry, Inc.
2
+ // SPDX-License-Identifier: LicenseRef-Minionry-Software
3
+ import { existsSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { computeFingerprint, dependencyParts, normalizeFindingPath } from './quality-fingerprint.js';
6
+ import { runCommand } from './quality-tools.js';
7
+ import { ANALYZER_FINDING_BOUND } from './quality-types.js';
8
+ export const DEPENDENCIES_CATEGORY = 'dependencies';
9
+ export const NPM_AUDIT_ARGS = ['audit', '--json', '--package-lock-only'];
10
+ export const NPM_LOCKFILES = ['package-lock.json', 'npm-shrinkwrap.json'];
11
+ export const OTHER_NODE_LOCKFILES = ['yarn.lock', 'pnpm-lock.yaml', 'bun.lock', 'bun.lockb'];
12
+ export const PIP_AUDIT_INSTALL_COMMAND = 'pip install pip-audit';
13
+ export const PIP_AUDIT_REQUIREMENTS_FILE = 'requirements.txt';
14
+ export const PIP_AUDIT_PROJECT_FILE = 'pyproject.toml';
15
+ const PIP_AUDIT_COMMON_ARGS = ['-f', 'json', '--progress-spinner', 'off'];
16
+ export const PIP_AUDIT_REQUIREMENTS_ARGS = [...PIP_AUDIT_COMMON_ARGS, '-r', PIP_AUDIT_REQUIREMENTS_FILE];
17
+ export const PIP_AUDIT_PROJECT_ARGS = [...PIP_AUDIT_COMMON_ARGS, '.'];
18
+ export const CARGO_AUDIT_INSTALL_COMMAND = 'cargo install cargo-audit';
19
+ export const CARGO_LOCKFILE = 'Cargo.lock';
20
+ export const CARGO_AUDIT_ARGS = ['audit', '--json'];
21
+ export const AUDIT_TIMEOUT_MS = 300_000;
22
+ export const DESCRIPTION_MAX_CHARS = 400;
23
+ function notRun(reason) {
24
+ return { score: 0, available: false, issueCount: 0, findings: [], reason };
25
+ }
26
+ const NPM_SEVERITY_TABLE = { critical: 'critical', high: 'high', moderate: 'medium', low: 'low', info: 'low' };
27
+ export function npmSeverity(raw) {
28
+ const word = typeof raw === 'string' ? raw.toLowerCase() : '';
29
+ const severity = NPM_SEVERITY_TABLE[word];
30
+ if (severity)
31
+ return { severity, note: `npm audit severity "${word}" → ${severity}` };
32
+ return {
33
+ severity: 'medium',
34
+ note: word ? `npm audit severity "${word}" is not in the severity table → medium` : 'npm audit reports no severity for this advisory → medium',
35
+ };
36
+ }
37
+ export const PIP_AUDIT_SEVERITY = { severity: 'medium', note: 'pip-audit reports no advisory severity → medium' };
38
+ export function cvssRating(score) {
39
+ if (score >= 9)
40
+ return 'critical';
41
+ if (score >= 7)
42
+ return 'high';
43
+ if (score >= 4)
44
+ return 'medium';
45
+ return 'low';
46
+ }
47
+ const CVSS_AV = { N: 0.85, A: 0.62, L: 0.55, P: 0.2 };
48
+ const CVSS_AC = { L: 0.77, H: 0.44 };
49
+ const CVSS_UI = { N: 0.85, R: 0.62 };
50
+ const CVSS_CIA = { H: 0.56, L: 0.22, N: 0 };
51
+ const CVSS_PR_UNCHANGED = { N: 0.85, L: 0.62, H: 0.27 };
52
+ const CVSS_PR_CHANGED = { N: 0.85, L: 0.68, H: 0.5 };
53
+ function cvssRoundup(value) {
54
+ const scaled = Math.round(value * 100_000);
55
+ if (scaled % 10_000 === 0)
56
+ return scaled / 100_000;
57
+ return (Math.floor(scaled / 10_000) + 1) / 10;
58
+ }
59
+ function cvssMetrics(vector) {
60
+ const [prefix, ...parts] = vector.trim().split('/');
61
+ if (!/^CVSS:3\.[01]$/.test(prefix))
62
+ return null;
63
+ const metrics = new Map();
64
+ for (const part of parts) {
65
+ const [key, value] = part.split(':');
66
+ if (key && value && !metrics.has(key))
67
+ metrics.set(key, value);
68
+ }
69
+ return metrics;
70
+ }
71
+ function cvssBaseMetrics(metrics) {
72
+ const scope = metrics.get('S');
73
+ if (scope !== 'U' && scope !== 'C')
74
+ return null;
75
+ const changed = scope === 'C';
76
+ const values = [
77
+ CVSS_AV[metrics.get('AV') ?? ''],
78
+ CVSS_AC[metrics.get('AC') ?? ''],
79
+ (changed ? CVSS_PR_CHANGED : CVSS_PR_UNCHANGED)[metrics.get('PR') ?? ''],
80
+ CVSS_UI[metrics.get('UI') ?? ''],
81
+ CVSS_CIA[metrics.get('C') ?? ''],
82
+ CVSS_CIA[metrics.get('I') ?? ''],
83
+ CVSS_CIA[metrics.get('A') ?? ''],
84
+ ];
85
+ if (values.some((v) => v === undefined))
86
+ return null;
87
+ const [av, ac, pr, ui, c, i, a] = values;
88
+ return { changed, av, ac, pr, ui, c, i, a };
89
+ }
90
+ export function cvssBaseScore(vector) {
91
+ const metrics = cvssMetrics(vector);
92
+ const base = metrics && cvssBaseMetrics(metrics);
93
+ if (!base)
94
+ return null;
95
+ const { changed, av, ac, pr, ui, c, i, a } = base;
96
+ const iss = 1 - (1 - c) * (1 - i) * (1 - a);
97
+ const impact = changed ? 7.52 * (iss - 0.029) - 3.25 * (iss - 0.02) ** 15 : 6.42 * iss;
98
+ const exploitability = 8.22 * av * ac * pr * ui;
99
+ if (impact <= 0)
100
+ return 0;
101
+ return cvssRoundup(Math.min(changed ? 1.08 * (impact + exploitability) : impact + exploitability, 10));
102
+ }
103
+ export function cargoVulnerabilitySeverity(cvss) {
104
+ if (cvss === null)
105
+ return { severity: 'medium', note: 'the advisory carries no CVSS vector → medium' };
106
+ const score = cvssBaseScore(cvss);
107
+ if (score === null)
108
+ return { severity: 'medium', note: `the advisory's CVSS vector "${cvss}" is not a v3 base vector and could not be scored → medium` };
109
+ const severity = cvssRating(score);
110
+ return { severity, note: `CVSS base score ${score.toFixed(1)} for ${cvss} → ${severity}` };
111
+ }
112
+ export function cargoWarningSeverity(kind) {
113
+ return { severity: 'low', note: `cargo audit "${kind}" warning → low` };
114
+ }
115
+ const GHSA_ID = /GHSA-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}/i;
116
+ function advisoryTitle(title, advisoryId, packageName, affected) {
117
+ return title ? `${title} (${advisoryId})` : `${advisoryId} in ${packageName}${affected ? ` ${affected}` : ''}`;
118
+ }
119
+ function asRecord(value) {
120
+ return typeof value === 'object' && value !== null && !Array.isArray(value) ? value : null;
121
+ }
122
+ function asString(value) {
123
+ return typeof value === 'string' ? value : '';
124
+ }
125
+ function asStringList(value) {
126
+ return Array.isArray(value) ? value.filter((v) => typeof v === 'string') : [];
127
+ }
128
+ function parseJson(text) {
129
+ try {
130
+ return JSON.parse(text);
131
+ }
132
+ catch {
133
+ return undefined;
134
+ }
135
+ }
136
+ function npmFix(fixAvailable) {
137
+ const fix = asRecord(fixAvailable);
138
+ if (fix) {
139
+ const major = fix.isSemVerMajor === true ? ' (a semver-major change)' : '';
140
+ return `Upgrade ${asString(fix.name)} to ${asString(fix.version)}${major}.`;
141
+ }
142
+ return fixAvailable === true ? 'A fix is available — `npm audit fix`.' : 'No fix is available yet — check the advisory for mitigations.';
143
+ }
144
+ function npmAdvisoryId(via, url) {
145
+ const ghsa = GHSA_ID.exec(url)?.[0];
146
+ if (ghsa)
147
+ return ghsa;
148
+ return via.source === undefined ? url : String(via.source);
149
+ }
150
+ function npmAdvisory(pkg, via) {
151
+ const url = asString(via.url);
152
+ const advisoryId = npmAdvisoryId(via, url);
153
+ if (!advisoryId && !via.title)
154
+ return null;
155
+ const name = asString(via.name) || asString(pkg.name);
156
+ const affected = asString(via.range) || asString(pkg.range);
157
+ return {
158
+ packageName: name,
159
+ affected,
160
+ advisoryId,
161
+ title: advisoryTitle(asString(via.title), advisoryId, name, affected),
162
+ description: '',
163
+ aliases: [],
164
+ severity: npmSeverity(via.severity ?? pkg.severity),
165
+ fix: npmFix(pkg.fixAvailable),
166
+ ...(url ? { url } : {}),
167
+ };
168
+ }
169
+ function npmPackageAdvisories(pkg) {
170
+ const advisories = [];
171
+ for (const entry of Array.isArray(pkg.via) ? pkg.via : []) {
172
+ const via = asRecord(entry);
173
+ const advisory = via && npmAdvisory(pkg, via);
174
+ if (advisory)
175
+ advisories.push(advisory);
176
+ }
177
+ return advisories;
178
+ }
179
+ export function parseNpmAuditReport(text) {
180
+ const raw = asRecord(parseJson(text));
181
+ if (!raw)
182
+ return null;
183
+ const vulnerabilities = asRecord(raw.vulnerabilities);
184
+ if (vulnerabilities) {
185
+ const advisories = [];
186
+ for (const [name, value] of Object.entries(vulnerabilities)) {
187
+ const pkg = asRecord(value);
188
+ if (pkg)
189
+ advisories.push(...npmPackageAdvisories({ name, ...pkg }));
190
+ }
191
+ return { advisories };
192
+ }
193
+ const error = asRecord(raw.error);
194
+ if (error || typeof raw.message === 'string') {
195
+ return { error: asString(error?.summary) || asString(raw.message) || asString(error?.code) || 'npm audit reported an error without a message' };
196
+ }
197
+ return null;
198
+ }
199
+ export function parsePipAuditReport(text) {
200
+ const raw = asRecord(parseJson(text));
201
+ if (!raw || !Array.isArray(raw.dependencies))
202
+ return null;
203
+ const advisories = [];
204
+ for (const entry of raw.dependencies) {
205
+ const dep = asRecord(entry);
206
+ if (!dep || !Array.isArray(dep.vulns))
207
+ continue;
208
+ const packageName = asString(dep.name);
209
+ const version = asString(dep.version);
210
+ for (const value of dep.vulns) {
211
+ const vuln = asRecord(value);
212
+ if (!vuln || typeof vuln.id !== 'string')
213
+ continue;
214
+ const fixVersions = asStringList(vuln.fix_versions);
215
+ advisories.push({
216
+ packageName,
217
+ affected: version,
218
+ advisoryId: vuln.id,
219
+ title: advisoryTitle('', vuln.id, packageName, version),
220
+ description: asString(vuln.description),
221
+ aliases: asStringList(vuln.aliases),
222
+ severity: PIP_AUDIT_SEVERITY,
223
+ fix: fixVersions.length > 0 ? `Upgrade ${packageName} to ${fixVersions.join(' or ')}.` : 'No fixed version is published yet — check the advisory for mitigations.',
224
+ });
225
+ }
226
+ }
227
+ return advisories;
228
+ }
229
+ function cargoVersions(versions) {
230
+ const patched = asStringList(asRecord(versions)?.patched);
231
+ return patched.length > 0 ? `Upgrade to a patched version: ${patched.join(', ')}.` : 'No patched version is listed — check the advisory for mitigations.';
232
+ }
233
+ function cargoVulnerability(value) {
234
+ const entry = asRecord(value);
235
+ const advisory = asRecord(entry?.advisory);
236
+ const pkg = asRecord(entry?.package);
237
+ if (!entry || !advisory || typeof advisory.id !== 'string')
238
+ return null;
239
+ const url = asString(advisory.url);
240
+ const packageName = asString(pkg?.name) || asString(advisory.package);
241
+ const version = asString(pkg?.version);
242
+ return {
243
+ packageName,
244
+ affected: version,
245
+ advisoryId: advisory.id,
246
+ title: advisoryTitle(asString(advisory.title), advisory.id, packageName, version),
247
+ description: asString(advisory.description),
248
+ aliases: asStringList(advisory.aliases),
249
+ severity: cargoVulnerabilitySeverity(typeof advisory.cvss === 'string' ? advisory.cvss : null),
250
+ fix: cargoVersions(entry.versions),
251
+ ...(url ? { url } : {}),
252
+ };
253
+ }
254
+ function cargoWarning(kind, value) {
255
+ const entry = asRecord(value);
256
+ const pkg = asRecord(entry?.package);
257
+ if (!entry || !pkg || typeof pkg.name !== 'string')
258
+ return null;
259
+ const advisory = asRecord(entry.advisory);
260
+ const url = asString(advisory?.url);
261
+ const version = asString(pkg.version);
262
+ const fix = kind === 'yanked'
263
+ ? 'The published version was yanked from crates.io — update to a version that is still published.'
264
+ : 'Replace the crate or pin a maintained alternative — see the advisory.';
265
+ return {
266
+ packageName: pkg.name,
267
+ affected: version,
268
+ advisoryId: asString(advisory?.id) || kind,
269
+ title: advisory ? advisoryTitle(asString(advisory.title), asString(advisory.id) || kind, pkg.name, version) : `${pkg.name}${version ? ` ${version}` : ''} is ${kind}`,
270
+ description: asString(advisory?.description),
271
+ aliases: asStringList(advisory?.aliases),
272
+ severity: cargoWarningSeverity(kind),
273
+ fix,
274
+ ...(url ? { url } : {}),
275
+ };
276
+ }
277
+ export function parseCargoAuditReport(text) {
278
+ const raw = asRecord(parseJson(text));
279
+ const vulnerabilities = asRecord(raw?.vulnerabilities);
280
+ if (!raw || !vulnerabilities || !Array.isArray(vulnerabilities.list))
281
+ return null;
282
+ const advisories = vulnerabilities.list.map(cargoVulnerability).filter((a) => a !== null);
283
+ for (const [kind, list] of Object.entries(asRecord(raw.warnings) ?? {})) {
284
+ if (!Array.isArray(list))
285
+ continue;
286
+ for (const value of list) {
287
+ const warning = cargoWarning(kind, value);
288
+ if (warning)
289
+ advisories.push(warning);
290
+ }
291
+ }
292
+ return advisories;
293
+ }
294
+ export function summarizeAdvisory(text) {
295
+ const paragraph = text
296
+ .split(/\n\s*\n/)
297
+ .map((p) => p.replace(/^\s*#+\s.*$/gm, '').replace(/\s+/g, ' ').trim())
298
+ .find(Boolean) ?? '';
299
+ return paragraph.length > DESCRIPTION_MAX_CHARS ? `${paragraph.slice(0, DESCRIPTION_MAX_CHARS - 1)}…` : paragraph;
300
+ }
301
+ function compareStrings(a, b) {
302
+ if (a === b)
303
+ return 0;
304
+ return a < b ? -1 : 1;
305
+ }
306
+ function compareAdvisories(a, b) {
307
+ return compareStrings(a.packageName, b.packageName) || compareStrings(a.advisoryId, b.advisoryId);
308
+ }
309
+ export function advisoriesToFindings(file, advisories) {
310
+ const findings = new Map();
311
+ for (const advisory of [...advisories].sort(compareAdvisories)) {
312
+ const fingerprint = computeFingerprint(dependencyParts(DEPENDENCIES_CATEGORY, file, advisory.packageName, advisory.advisoryId));
313
+ if (findings.has(fingerprint))
314
+ continue;
315
+ const summary = summarizeAdvisory(advisory.description);
316
+ const affected = advisory.affected ? ` ${advisory.affected}` : '';
317
+ const aliases = advisory.aliases.length > 0 ? ` Also known as ${advisory.aliases.join(', ')}.` : '';
318
+ findings.set(fingerprint, {
319
+ severity: advisory.severity.severity,
320
+ category: DEPENDENCIES_CATEGORY,
321
+ file,
322
+ line: null,
323
+ title: advisory.title,
324
+ description: `${advisory.packageName}${affected} — ${advisory.severity.note}.${summary ? ` ${summary}` : ''}${aliases}${advisory.url ? ` ${advisory.url}` : ''}`,
325
+ suggestion: advisory.fix,
326
+ fingerprint,
327
+ });
328
+ }
329
+ return [...findings.values()];
330
+ }
331
+ function skipped(reason) {
332
+ return { ran: false, findings: [], reason };
333
+ }
334
+ const NOT_ON_PATH = /\bENOENT\b/;
335
+ const NO_CARGO_SUBCOMMAND = /no such command/i;
336
+ function stderrLines(stderr) {
337
+ return stderr
338
+ .split('\n')
339
+ .map((l) => l.trim())
340
+ .filter(Boolean);
341
+ }
342
+ function firstLine(result) {
343
+ return stderrLines(result.stderr)[0] ?? stderrLines(result.stdout).pop() ?? '';
344
+ }
345
+ function lastLine(result) {
346
+ return stderrLines(result.stderr).pop() ?? stderrLines(result.stdout).pop() ?? '';
347
+ }
348
+ function timedOut(tool) {
349
+ return `${tool} timed out after ${AUDIT_TIMEOUT_MS / 1000} s`;
350
+ }
351
+ function didNotComplete(tool, exitCode, detail) {
352
+ return `${tool} did not complete (exit ${exitCode})${detail ? `: ${detail}` : ''}`;
353
+ }
354
+ async function auditNode(dirPath) {
355
+ const lockfile = NPM_LOCKFILES.find((name) => existsSync(join(dirPath, name)));
356
+ if (!lockfile) {
357
+ const other = OTHER_NODE_LOCKFILES.find((name) => existsSync(join(dirPath, name)));
358
+ return skipped(other
359
+ ? `npm audit needs ${NPM_LOCKFILES.join(' or ')} and this directory has ${other} instead — that package manager's audit is not run`
360
+ : `npm audit needs a lockfile (${NPM_LOCKFILES.join(' or ')}) and this directory has none — \`npm install --package-lock-only\` creates one`);
361
+ }
362
+ const result = await runCommand('npm', [...NPM_AUDIT_ARGS], dirPath, undefined, AUDIT_TIMEOUT_MS);
363
+ if (result.timedOut)
364
+ return skipped(timedOut('npm audit'));
365
+ const report = parseNpmAuditReport(result.stdout);
366
+ if (report === null) {
367
+ if (NOT_ON_PATH.test(result.stderr))
368
+ return skipped('npm is not on PATH — npm audit ships with npm');
369
+ return skipped(didNotComplete('npm audit', result.exitCode, lastLine(result).replace(/^npm (error|warn) /, '')));
370
+ }
371
+ if ('error' in report)
372
+ return skipped(`npm audit did not complete: ${report.error}`);
373
+ return { ran: true, findings: advisoriesToFindings(lockfile, report.advisories) };
374
+ }
375
+ async function auditPython(dirPath, installed) {
376
+ if (installed && !installed.has('pip-audit'))
377
+ return skipped(`pip-audit is not installed — ${PIP_AUDIT_INSTALL_COMMAND}`);
378
+ const target = existsSync(join(dirPath, PIP_AUDIT_REQUIREMENTS_FILE))
379
+ ? { file: PIP_AUDIT_REQUIREMENTS_FILE, args: PIP_AUDIT_REQUIREMENTS_ARGS }
380
+ : existsSync(join(dirPath, PIP_AUDIT_PROJECT_FILE))
381
+ ? { file: PIP_AUDIT_PROJECT_FILE, args: PIP_AUDIT_PROJECT_ARGS }
382
+ : null;
383
+ if (!target) {
384
+ return skipped(`pip-audit audits ${PIP_AUDIT_REQUIREMENTS_FILE} or ${PIP_AUDIT_PROJECT_FILE} and this directory has neither (a setup.py alone cannot be audited)`);
385
+ }
386
+ const result = await runCommand('pip-audit', [...target.args], dirPath, undefined, AUDIT_TIMEOUT_MS);
387
+ if (result.timedOut)
388
+ return skipped(timedOut('pip-audit'));
389
+ const report = parsePipAuditReport(result.stdout);
390
+ if (report === null) {
391
+ if (NOT_ON_PATH.test(result.stderr))
392
+ return skipped(`pip-audit is not installed — ${PIP_AUDIT_INSTALL_COMMAND}`);
393
+ return skipped(didNotComplete('pip-audit', result.exitCode, lastLine(result)));
394
+ }
395
+ return { ran: true, findings: advisoriesToFindings(target.file, report) };
396
+ }
397
+ async function auditRust(dirPath, installed, cargoRoot) {
398
+ if (installed && !installed.has('cargo-audit'))
399
+ return skipped(`cargo-audit is not installed — ${CARGO_AUDIT_INSTALL_COMMAND}`);
400
+ const lockfilePath = join(cargoRoot, CARGO_LOCKFILE);
401
+ const file = normalizeFindingPath(lockfilePath, dirPath);
402
+ if (!existsSync(lockfilePath)) {
403
+ return skipped(`cargo audit needs ${file} and the workspace has none — \`cargo generate-lockfile\` creates one (cargo audit would otherwise write one into the repository itself)`);
404
+ }
405
+ const result = await runCommand('cargo', [...CARGO_AUDIT_ARGS], cargoRoot, undefined, AUDIT_TIMEOUT_MS);
406
+ if (result.timedOut)
407
+ return skipped(timedOut('cargo audit'));
408
+ const report = parseCargoAuditReport(result.stdout);
409
+ if (report === null) {
410
+ const detail = firstLine(result);
411
+ if (NOT_ON_PATH.test(result.stderr))
412
+ return skipped('cargo is not on PATH');
413
+ if (NO_CARGO_SUBCOMMAND.test(detail))
414
+ return skipped(`cargo-audit is not installed — ${CARGO_AUDIT_INSTALL_COMMAND}`);
415
+ return skipped(didNotComplete('cargo audit', result.exitCode, detail));
416
+ }
417
+ return { ran: true, findings: advisoriesToFindings(file, report) };
418
+ }
419
+ export async function analyzeDependencies(dirPath, ecosystems, installed, cargoRoot = dirPath, scoped = false) {
420
+ if (scoped) {
421
+ return notRun('a scoped scan audits no dependencies — the audit is project-wide and asks the registry, so it runs on full scans only');
422
+ }
423
+ const outcomes = [];
424
+ if (ecosystems.includes('node'))
425
+ outcomes.push(await auditNode(dirPath));
426
+ if (ecosystems.includes('python'))
427
+ outcomes.push(await auditPython(dirPath, installed));
428
+ if (ecosystems.includes('rust'))
429
+ outcomes.push(await auditRust(dirPath, installed, cargoRoot));
430
+ if (outcomes.length === 0) {
431
+ return notRun('no package.json, requirements.txt / pyproject.toml or Cargo.toml in this directory — nothing to audit');
432
+ }
433
+ const reasons = outcomes.flatMap((o) => (o.reason === undefined ? [] : [o.reason]));
434
+ const ran = outcomes.filter((o) => o.ran);
435
+ if (ran.length === 0)
436
+ return notRun(reasons.join('; '));
437
+ const findings = ran.flatMap((o) => o.findings);
438
+ return {
439
+ score: findings.length === 0 ? 100 : 0,
440
+ available: true,
441
+ issueCount: findings.length,
442
+ findings: findings.slice(0, ANALYZER_FINDING_BOUND),
443
+ ...(reasons.length === 0 ? {} : { reason: reasons.join('; ') }),
444
+ };
445
+ }
@@ -0,0 +1,162 @@
1
+ // Copyright (c) 2025-present Minionry, Inc.
2
+ // SPDX-License-Identifier: LicenseRef-Minionry-Software
3
+ import { existsSync, readFileSync, rmSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { collectedContentReader, computeFingerprint, duplicationParts, lineTextOf, normalizeFindingPath } from './quality-fingerprint.js';
6
+ import { runCommand } from './quality-tools.js';
7
+ import { ANALYZER_FINDING_BOUND, FUNCTION_LENGTH_THRESHOLD } from './quality-types.js';
8
+ export const DUPLICATION_CATEGORY = 'duplication';
9
+ export const JSCPD_MIN_LINES = 5;
10
+ export const JSCPD_MIN_TOKENS = 50;
11
+ export const JSCPD_THRESHOLD_SOURCE = 'https://github.com/kucherenko/jscpd';
12
+ export const JSCPD_THRESHOLD_CITATION = `jscpd defaults: min ${JSCPD_MIN_LINES} lines / min ${JSCPD_MIN_TOKENS} tokens (${JSCPD_THRESHOLD_SOURCE})`;
13
+ export const JSCPD_INSTALL_COMMAND = 'npm install -D jscpd';
14
+ export const JSCPD_REPORT_FILE = 'jscpd-report.json';
15
+ export const JSCPD_ARGS = [
16
+ '--no-install',
17
+ 'jscpd',
18
+ '--reporters',
19
+ 'json',
20
+ '--silent',
21
+ '--absolute',
22
+ '--min-lines',
23
+ String(JSCPD_MIN_LINES),
24
+ '--min-tokens',
25
+ String(JSCPD_MIN_TOKENS),
26
+ ];
27
+ export const JSCPD_TIMEOUT_MS = 300_000;
28
+ export const JSCPD_MAX_FILE_LIST_BYTES = 512 * 1024;
29
+ function notRun(reason) {
30
+ return { score: 0, available: false, issueCount: 0, findings: [], reason };
31
+ }
32
+ function asLocation(value) {
33
+ if (typeof value !== 'object' || value === null)
34
+ return null;
35
+ const raw = value;
36
+ const startLoc = raw.startLoc;
37
+ const endLoc = raw.endLoc;
38
+ const start = typeof raw.start === 'number' ? raw.start : startLoc?.line;
39
+ const end = typeof raw.end === 'number' ? raw.end : endLoc?.line;
40
+ if (typeof raw.name !== 'string' || typeof start !== 'number' || typeof end !== 'number')
41
+ return null;
42
+ return { name: raw.name, start, end };
43
+ }
44
+ function asDuplicate(value) {
45
+ if (typeof value !== 'object' || value === null)
46
+ return null;
47
+ const raw = value;
48
+ const firstFile = asLocation(raw.firstFile);
49
+ const secondFile = asLocation(raw.secondFile);
50
+ if (!firstFile || !secondFile)
51
+ return null;
52
+ return {
53
+ firstFile,
54
+ secondFile,
55
+ lines: typeof raw.lines === 'number' ? raw.lines : firstFile.end - firstFile.start + 1,
56
+ tokens: typeof raw.tokens === 'number' ? raw.tokens : 0,
57
+ ...(typeof raw.kind === 'string' ? { kind: raw.kind } : {}),
58
+ };
59
+ }
60
+ export function parseJscpdReport(text) {
61
+ let parsed;
62
+ try {
63
+ parsed = JSON.parse(text);
64
+ }
65
+ catch {
66
+ return null;
67
+ }
68
+ if (typeof parsed !== 'object' || parsed === null)
69
+ return null;
70
+ const raw = parsed;
71
+ if (!Array.isArray(raw.duplicates))
72
+ return null;
73
+ const total = raw.statistics?.total;
74
+ return {
75
+ duplicates: raw.duplicates.map(asDuplicate).filter((d) => d !== null),
76
+ percentage: typeof total?.percentage === 'number' ? total.percentage : 0,
77
+ };
78
+ }
79
+ function orderPair(a, b) {
80
+ if (a.file < b.file)
81
+ return [a, b];
82
+ if (a.file > b.file)
83
+ return [b, a];
84
+ return a.start <= b.start ? [a, b] : [b, a];
85
+ }
86
+ function cloneSeverity(lines) {
87
+ if (lines >= FUNCTION_LENGTH_THRESHOLD)
88
+ return 'high';
89
+ if (lines >= JSCPD_MIN_LINES * 3)
90
+ return 'medium';
91
+ return 'low';
92
+ }
93
+ function describeLocation(loc) {
94
+ return `${loc.file}:${loc.start}–${loc.end}`;
95
+ }
96
+ export function jscpdReportToFindings(report, dirPath, files) {
97
+ const contentOf = collectedContentReader(files);
98
+ const findings = [];
99
+ for (const clone of report.duplicates) {
100
+ const toScan = (loc) => ({ file: normalizeFindingPath(loc.name, dirPath), start: loc.start, end: loc.end });
101
+ const [first, second] = orderPair(toScan(clone.firstFile), toScan(clone.secondFile));
102
+ const toClone = (loc) => ({ file: loc.file, lineText: lineTextOf(contentOf(loc.file), loc.start) });
103
+ const nearMiss = clone.kind === 'similar' ? ' (near-miss clone)' : '';
104
+ findings.push({
105
+ severity: cloneSeverity(clone.lines),
106
+ category: DUPLICATION_CATEGORY,
107
+ file: first.file,
108
+ line: first.start,
109
+ title: `Duplicated block (${clone.lines} lines, ${clone.tokens} tokens)`,
110
+ description: `${describeLocation(first)} duplicates ${describeLocation(second)}${nearMiss}. Threshold: ${JSCPD_THRESHOLD_CITATION}.`,
111
+ fingerprint: computeFingerprint(duplicationParts(DUPLICATION_CATEGORY, toClone(first), toClone(second))),
112
+ });
113
+ }
114
+ return findings;
115
+ }
116
+ function failureDetail(result) {
117
+ const stderr = result.stderr.split('\n').map((l) => l.trim()).find(Boolean);
118
+ if (stderr)
119
+ return stderr;
120
+ return result.stdout.split('\n').map((l) => l.trim()).filter(Boolean).pop() ?? '';
121
+ }
122
+ const NPX_MISSING_TOOL = /missing packages|could not determine executable|not found/i;
123
+ export async function analyzeDuplication(dirPath, ecosystems, files, installed, toolOutputDir) {
124
+ if (!ecosystems.includes('node')) {
125
+ return notRun('jscpd runs through npx and is rostered for node directories only — this directory has no package.json');
126
+ }
127
+ if (installed && !installed.has('jscpd')) {
128
+ return notRun(`jscpd is not installed — ${JSCPD_INSTALL_COMMAND}`);
129
+ }
130
+ if (toolOutputDir === undefined) {
131
+ return notRun('jscpd needs a report directory under .minionry/quality/ and this scan has none (a scoped or post-session scan never writes the store)');
132
+ }
133
+ if (files.length === 0) {
134
+ return notRun('no source files were collected for jscpd to scan');
135
+ }
136
+ const targets = files.map((f) => f.relativePath);
137
+ const argvBytes = targets.reduce((sum, path) => sum + Buffer.byteLength(path) + 1, 0);
138
+ if (argvBytes > JSCPD_MAX_FILE_LIST_BYTES) {
139
+ return notRun(`${targets.length} files (${Math.round(argvBytes / 1024)} KB of arguments) exceed what one jscpd invocation can be handed, and jscpd must see every file at once to find cross-file clones`);
140
+ }
141
+ const reportPath = join(toolOutputDir, JSCPD_REPORT_FILE);
142
+ rmSync(reportPath, { force: true });
143
+ const result = await runCommand('npx', [...JSCPD_ARGS, '--output', toolOutputDir, ...targets], dirPath, undefined, JSCPD_TIMEOUT_MS);
144
+ if (!existsSync(reportPath)) {
145
+ if (result.timedOut)
146
+ return notRun(`jscpd timed out after ${JSCPD_TIMEOUT_MS / 1000} s`);
147
+ const detail = failureDetail(result);
148
+ if (NPX_MISSING_TOOL.test(detail))
149
+ return notRun(`jscpd is not installed — ${JSCPD_INSTALL_COMMAND}`);
150
+ return notRun(`jscpd produced no report (exit ${result.exitCode})${detail ? `: ${detail}` : ''}`);
151
+ }
152
+ const report = parseJscpdReport(readFileSync(reportPath, 'utf8'));
153
+ if (!report)
154
+ return notRun(`jscpd's report at ${reportPath} could not be parsed`);
155
+ const findings = jscpdReportToFindings(report, dirPath, files);
156
+ return {
157
+ score: Math.max(0, Math.min(100, Math.round(100 - report.percentage))),
158
+ available: true,
159
+ issueCount: findings.length,
160
+ findings: findings.slice(0, ANALYZER_FINDING_BOUND),
161
+ };
162
+ }
@@ -0,0 +1,65 @@
1
+ // Copyright (c) 2025-present Minionry, Inc.
2
+ // SPDX-License-Identifier: LicenseRef-Minionry-Software
3
+ const SCAN_PER_FILE_MS = 250;
4
+ const SCAN_PER_KLOC_MS = 800;
5
+ const SCAN_FIXED_MS = 8_000;
6
+ const SCAN_MIN_MS = 5_000;
7
+ const REVIEW_PER_FILE_MS = 1_200;
8
+ const REVIEW_PER_KLOC_MS = 2_500;
9
+ const REVIEW_FIXED_MS = 25_000;
10
+ const REVIEW_MIN_MS = 30_000;
11
+ const MAX_HISTORY_SAMPLES = 5;
12
+ function recentDurations(history, dirPath, field) {
13
+ const values = [];
14
+ for (let i = history.length - 1; i >= 0 && values.length < MAX_HISTORY_SAMPLES; i--) {
15
+ const entry = history[i];
16
+ if (!entry.directories.some((d) => d.path === dirPath))
17
+ continue;
18
+ const v = entry[field];
19
+ if (typeof v === 'number' && v > 0)
20
+ values.push(v);
21
+ }
22
+ return values;
23
+ }
24
+ function weightedRecentAverage(values) {
25
+ if (values.length === 0)
26
+ return null;
27
+ let weightSum = 0;
28
+ let valueSum = 0;
29
+ for (let i = 0; i < values.length; i++) {
30
+ const w = values.length - i;
31
+ valueSum += values[i] * w;
32
+ weightSum += w;
33
+ }
34
+ return weightSum > 0 ? valueSum / weightSum : null;
35
+ }
36
+ export function heuristicScanMs({ files, lines }) {
37
+ const kloc = Math.max(0, lines) / 1_000;
38
+ const raw = SCAN_FIXED_MS + files * SCAN_PER_FILE_MS + kloc * SCAN_PER_KLOC_MS;
39
+ return Math.max(SCAN_MIN_MS, Math.round(raw));
40
+ }
41
+ export function heuristicReviewMs({ files, lines }) {
42
+ const kloc = Math.max(0, lines) / 1_000;
43
+ const raw = REVIEW_FIXED_MS + files * REVIEW_PER_FILE_MS + kloc * REVIEW_PER_KLOC_MS;
44
+ return Math.max(REVIEW_MIN_MS, Math.round(raw));
45
+ }
46
+ export function estimateScanMs(size, history, dirPath) {
47
+ const recent = recentDurations(history, dirPath, 'scanDurationMs');
48
+ const fromHistory = weightedRecentAverage(recent);
49
+ if (fromHistory !== null)
50
+ return Math.max(SCAN_MIN_MS, Math.round(fromHistory));
51
+ return heuristicScanMs(size);
52
+ }
53
+ export function estimateReviewMs(size, history, dirPath) {
54
+ const recent = recentDurations(history, dirPath, 'reviewDurationMs');
55
+ const fromHistory = weightedRecentAverage(recent);
56
+ if (fromHistory !== null)
57
+ return Math.max(REVIEW_MIN_MS, Math.round(fromHistory));
58
+ return heuristicReviewMs(size);
59
+ }
60
+ export async function estimateCodebaseSize(dirPath) {
61
+ const { collectSourceFiles } = await import('./quality-tools.js');
62
+ const files = await collectSourceFiles(dirPath, dirPath);
63
+ const lines = files.reduce((sum, f) => sum + f.lines, 0);
64
+ return { files: files.length, lines };
65
+ }