@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,105 @@
1
+ // Copyright (c) 2025-present Minionry, Inc.
2
+ // SPDX-License-Identifier: LicenseRef-Minionry-Software
3
+ import { resolve } from 'node:path';
4
+ import { executeGitCommand, gitErrorText } from './command.js';
5
+ import { parseAheadBehind, parseGitStatus } from './parsers.js';
6
+ import { gitFail, gitOk } from './types.js';
7
+ export async function readStatus({ cwd }) {
8
+ const statusResult = await executeGitCommand(['status', '--porcelain=v1'], cwd);
9
+ if (statusResult.exitCode !== 0) {
10
+ return gitFail(gitErrorText(statusResult, 'Failed to get git status'));
11
+ }
12
+ const branchResult = await executeGitCommand(['rev-parse', '--abbrev-ref', 'HEAD'], cwd);
13
+ const branch = branchResult.stdout.trim() || 'HEAD';
14
+ let ahead = 0;
15
+ let behind = 0;
16
+ let hasUpstream = false;
17
+ const trackingResult = await executeGitCommand(['rev-list', '--left-right', '--count', `${branch}...@{u}`], cwd);
18
+ if (trackingResult.exitCode === 0) {
19
+ hasUpstream = true;
20
+ ({ ahead, behind } = parseAheadBehind(trackingResult.stdout));
21
+ }
22
+ else {
23
+ const localResult = await executeGitCommand(['rev-list', '--count', 'HEAD'], cwd);
24
+ if (localResult.exitCode === 0) {
25
+ ahead = parseInt(localResult.stdout.trim(), 10) || 0;
26
+ }
27
+ }
28
+ const { staged, unstaged, untracked } = parseGitStatus(statusResult.stdout);
29
+ return gitOk({
30
+ status: {
31
+ branch,
32
+ isDirty: staged.length > 0 || unstaged.length > 0 || untracked.length > 0,
33
+ staged,
34
+ unstaged,
35
+ untracked,
36
+ ahead,
37
+ behind,
38
+ hasUpstream,
39
+ },
40
+ porcelain: statusResult.stdout,
41
+ });
42
+ }
43
+ export async function readHeadSha({ cwd }) {
44
+ const result = await executeGitCommand(['rev-parse', 'HEAD'], cwd);
45
+ return result.exitCode === 0 ? result.stdout.trim() : null;
46
+ }
47
+ export async function stagePaths({ cwd, stageAll, paths }) {
48
+ if (!stageAll && (!paths || paths.length === 0)) {
49
+ return gitFail('No paths specified for staging');
50
+ }
51
+ if (!stageAll && paths) {
52
+ const resolvedRoot = resolve(cwd);
53
+ for (const p of paths) {
54
+ if (!resolve(cwd, p).startsWith(resolvedRoot)) {
55
+ return gitFail(`Path traversal not allowed: ${p}`);
56
+ }
57
+ }
58
+ }
59
+ const args = stageAll ? ['add', '-A'] : ['add', '--', ...paths];
60
+ const result = await executeGitCommand(args, cwd);
61
+ if (result.exitCode !== 0) {
62
+ return gitFail(gitErrorText(result, 'Failed to stage files'));
63
+ }
64
+ return gitOk({ paths: paths || [] });
65
+ }
66
+ export async function unstagePaths({ cwd, paths }) {
67
+ const result = await executeGitCommand(['reset', 'HEAD', '--', ...paths], cwd);
68
+ if (result.exitCode !== 0) {
69
+ return gitFail(gitErrorText(result, 'Failed to unstage files'));
70
+ }
71
+ return gitOk({ paths });
72
+ }
73
+ export const MAX_COMMIT_MESSAGE_LENGTH = 10_000;
74
+ export async function commit({ cwd, message }) {
75
+ const result = await executeGitCommand(['commit', '-m', message], cwd);
76
+ if (result.exitCode !== 0) {
77
+ const error = gitErrorText(result, 'Failed to commit');
78
+ return {
79
+ ok: false,
80
+ error,
81
+ nothingToCommit: error.includes('nothing to commit') || error.includes('no changes added'),
82
+ };
83
+ }
84
+ const hashResult = await executeGitCommand(['rev-parse', '--short', 'HEAD'], cwd);
85
+ return { ok: true, hash: hashResult.stdout.trim() };
86
+ }
87
+ export async function push({ cwd }) {
88
+ const branchResult = await executeGitCommand(['rev-parse', '--abbrev-ref', 'HEAD'], cwd);
89
+ const branch = branchResult.stdout.trim();
90
+ const upstreamCheck = await executeGitCommand(['rev-parse', '--abbrev-ref', `${branch}@{u}`], cwd);
91
+ const hasUpstream = upstreamCheck.exitCode === 0;
92
+ const pushArgs = hasUpstream ? ['push'] : ['push', '-u', 'origin', branch];
93
+ const result = await executeGitCommand(pushArgs, cwd);
94
+ if (result.exitCode !== 0) {
95
+ return gitFail(gitErrorText(result, 'Failed to push'));
96
+ }
97
+ return gitOk({ output: result.stdout || result.stderr });
98
+ }
99
+ export async function pull({ cwd }) {
100
+ const result = await executeGitCommand(['pull'], cwd);
101
+ if (result.exitCode !== 0) {
102
+ return gitFail(gitErrorText(result, 'Failed to pull'));
103
+ }
104
+ return gitOk({ output: result.stdout || result.stderr });
105
+ }
@@ -0,0 +1,80 @@
1
+ // Copyright (c) 2025-present Minionry, Inc.
2
+ // SPDX-License-Identifier: LicenseRef-Minionry-Software
3
+ import { createHash } from 'node:crypto';
4
+ import { executeGitCommand } from './command.js';
5
+ import { buildCommitMessagePrompt, COMMIT_MESSAGE_SYSTEM_PROMPT, classifyCommitMessageFailure, runHaikuForCommitMessage, } from './commit-message.js';
6
+ import { logCommitMessageEvent, truncateDiff } from './haiku.js';
7
+ import { discoverRepos } from './log-ops.js';
8
+ import { parseGitStatus } from './parsers.js';
9
+ const GIT_TIMEOUT_MS = 15_000;
10
+ const MAX_GIT_OUTPUT_BYTES = 5_000_000;
11
+ export const GIT_VIEW_TAB_ID = 'gitview';
12
+ export function computeStatusKey(headSha, porcelainStatus) {
13
+ return createHash('sha1').update(headSha).update('\0').update(porcelainStatus).digest('hex');
14
+ }
15
+ export async function resolveSuggestionRepoRoots(dir) {
16
+ const guard = { timeoutMs: GIT_TIMEOUT_MS, maxOutputBytes: MAX_GIT_OUTPUT_BYTES };
17
+ const toplevel = await executeGitCommand(['rev-parse', '--show-toplevel'], dir, guard);
18
+ if (toplevel.exitCode === 0) {
19
+ const root = toplevel.stdout.trim();
20
+ return root ? [root] : [];
21
+ }
22
+ const { repos } = await discoverRepos({ cwd: dir });
23
+ return repos.map((repo) => repo.path);
24
+ }
25
+ export async function snapshotWorktree(dir) {
26
+ const guard = { timeoutMs: GIT_TIMEOUT_MS, maxOutputBytes: MAX_GIT_OUTPUT_BYTES };
27
+ const status = await executeGitCommand(['status', '--porcelain=v1'], dir, guard);
28
+ if (status.exitCode !== 0)
29
+ return null;
30
+ const head = await executeGitCommand(['rev-parse', 'HEAD'], dir, guard);
31
+ if (head.exitCode !== 0)
32
+ return null;
33
+ const headSha = head.stdout.trim();
34
+ const statusKey = computeStatusKey(headSha, status.stdout);
35
+ const { staged, unstaged, untracked } = parseGitStatus(status.stdout);
36
+ if (staged.length === 0 && unstaged.length === 0 && untracked.length === 0) {
37
+ return { statusKey, branch: '', isDirty: false, diff: '', filesList: '', recentCommits: '' };
38
+ }
39
+ const branchRes = await executeGitCommand(['rev-parse', '--abbrev-ref', 'HEAD'], dir, guard);
40
+ const diffRes = await executeGitCommand(['diff', 'HEAD'], dir, guard);
41
+ if (diffRes.exitCode !== 0)
42
+ return null;
43
+ const logRes = await executeGitCommand(['log', '--oneline', '-5'], dir, guard);
44
+ const seen = new Set();
45
+ const fileLines = [];
46
+ for (const file of [...staged, ...unstaged, ...untracked]) {
47
+ if (seen.has(file.path))
48
+ continue;
49
+ seen.add(file.path);
50
+ fileLines.push(`${file.status} ${file.path}`);
51
+ }
52
+ const untrackedAppendix = untracked.length > 0
53
+ ? `\n\nUNTRACKED FILES (new files that would be added):\n${untracked.map((f) => f.path).join('\n')}`
54
+ : '';
55
+ return {
56
+ statusKey,
57
+ branch: branchRes.stdout.trim() || 'HEAD',
58
+ isDirty: true,
59
+ diff: diffRes.stdout + untrackedAppendix,
60
+ filesList: fileLines.join('\n'),
61
+ recentCommits: logRes.stdout.trim() || 'No recent commits',
62
+ };
63
+ }
64
+ export async function generateSuggestionMessage(dir, snapshot) {
65
+ const startedAt = Date.now();
66
+ const diffBytes = snapshot.diff.length;
67
+ const prompt = buildCommitMessagePrompt(dir, {
68
+ recentCommits: snapshot.recentCommits,
69
+ stagedFiles: snapshot.filesList,
70
+ diff: truncateDiff(snapshot.diff),
71
+ });
72
+ const { result, attempts, message } = await runHaikuForCommitMessage(prompt, COMMIT_MESSAGE_SYSTEM_PROMPT, dir, { tabId: GIT_VIEW_TAB_ID, startedAt, diffBytes });
73
+ logCommitMessageEvent(dir, {
74
+ tabId: GIT_VIEW_TAB_ID, source: 'auto-suggest', attempt: attempts,
75
+ success: message.length > 0,
76
+ latencyMs: Date.now() - startedAt, diffBytes, messageLength: message.length,
77
+ ...(message ? {} : { reason: classifyCommitMessageFailure(result).reason, exitCode: result.exitCode, timedOut: result.timedOut }),
78
+ });
79
+ return message;
80
+ }
@@ -0,0 +1,36 @@
1
+ // Copyright (c) 2025-present Minionry, Inc.
2
+ // SPDX-License-Identifier: LicenseRef-Minionry-Software
3
+ import { executeGitCommand } from './command.js';
4
+ import { parseTagList, TAG_LIST_FORMAT } from './parsers.js';
5
+ import { gitFail, gitOk } from './types.js';
6
+ export async function listTags({ cwd }) {
7
+ const result = await executeGitCommand(['tag', '-l', '--sort=-creatordate', `--format=${TAG_LIST_FORMAT}`], cwd);
8
+ if (result.exitCode !== 0) {
9
+ return gitFail(result.stderr || 'Failed to list tags');
10
+ }
11
+ return gitOk(parseTagList(result.stdout));
12
+ }
13
+ export async function createTag({ cwd, name, message, commit }) {
14
+ if (/\s/.test(name) || name.includes('..')) {
15
+ return gitFail('Invalid tag name: no spaces or ".." allowed');
16
+ }
17
+ const args = message
18
+ ? ['tag', '-a', name, '-m', message, ...(commit ? [commit] : [])]
19
+ : ['tag', name, ...(commit ? [commit] : [])];
20
+ const result = await executeGitCommand(args, cwd);
21
+ if (result.exitCode !== 0) {
22
+ return gitFail(result.stderr || 'Failed to create tag');
23
+ }
24
+ const hashResult = await executeGitCommand(['rev-parse', '--short', name], cwd);
25
+ return gitOk({ name, hash: hashResult.stdout.trim() });
26
+ }
27
+ export async function pushTag({ cwd, name, all }) {
28
+ const args = all
29
+ ? ['push', 'origin', '--tags']
30
+ : ['push', 'origin', name];
31
+ const result = await executeGitCommand(args, cwd);
32
+ if (result.exitCode !== 0) {
33
+ return gitFail(result.stderr || 'Failed to push tag');
34
+ }
35
+ return gitOk({ output: result.stderr || result.stdout });
36
+ }
@@ -0,0 +1,4 @@
1
+ // Copyright (c) 2025-present Minionry, Inc.
2
+ // SPDX-License-Identifier: LicenseRef-Minionry-Software
3
+ export const gitOk = (value) => ({ ok: true, value });
4
+ export const gitFail = (error) => ({ ok: false, error });
@@ -0,0 +1,114 @@
1
+ // Copyright (c) 2025-present Minionry, Inc.
2
+ // SPDX-License-Identifier: LicenseRef-Minionry-Software
3
+ import { executeGitCommand, spawnWithOutput } from './command.js';
4
+ import { deriveWorktreePath, firstWorktreePath, parseWorktreePorcelain, parseWorktreeStatusSummary } from './parsers.js';
5
+ import { gitFail, gitOk } from './types.js';
6
+ export async function readCurrentBranch(dir) {
7
+ const result = await executeGitCommand(['rev-parse', '--abbrev-ref', 'HEAD'], dir);
8
+ return result.stdout.trim();
9
+ }
10
+ export async function isInsideGitRepo(dir) {
11
+ const result = await executeGitCommand(['rev-parse', '--show-toplevel'], dir);
12
+ return result.exitCode === 0;
13
+ }
14
+ export async function resolveMainWorktreeRoot(dir) {
15
+ const result = await executeGitCommand(['worktree', 'list', '--porcelain'], dir);
16
+ if (result.exitCode !== 0)
17
+ return dir;
18
+ return parseWorktreePorcelain(result.stdout).find((wt) => wt.isMain)?.path || dir;
19
+ }
20
+ export async function resolveMainWorktreePath(dir) {
21
+ const wtListResult = await executeGitCommand(['worktree', 'list', '--porcelain'], dir);
22
+ return firstWorktreePath(wtListResult.stdout) ?? dir;
23
+ }
24
+ async function attachWorktreeStatuses(worktrees) {
25
+ await Promise.all(worktrees.map(async (wt) => {
26
+ if (wt.isBare)
27
+ return;
28
+ const result = await executeGitCommand(['--no-optional-locks', 'status', '--porcelain=v2', '--branch'], wt.path, { timeoutMs: 8000, maxOutputBytes: 2_000_000 });
29
+ if (result.exitCode !== 0)
30
+ return;
31
+ wt.status = parseWorktreeStatusSummary(result.stdout);
32
+ }));
33
+ }
34
+ export async function listWorktrees({ cwd, includeStatus }) {
35
+ const result = await executeGitCommand(['worktree', 'list', '--porcelain'], cwd);
36
+ if (result.exitCode !== 0) {
37
+ return gitFail(result.stderr || 'Failed to list worktrees');
38
+ }
39
+ const worktrees = parseWorktreePorcelain(result.stdout);
40
+ if (includeStatus) {
41
+ await attachWorktreeStatuses(worktrees);
42
+ }
43
+ return gitOk(worktrees);
44
+ }
45
+ export async function createWorktree({ cwd, branchName, baseBranch, path }) {
46
+ const baseDir = await resolveMainWorktreeRoot(cwd);
47
+ const wtPath = path || deriveWorktreePath(baseDir, branchName);
48
+ const args = ['worktree', 'add', wtPath, '-b', branchName, ...(baseBranch ? [baseBranch] : [])];
49
+ const result = await executeGitCommand(args, baseDir);
50
+ if (result.exitCode !== 0) {
51
+ return gitFail(result.stderr || 'Failed to create worktree');
52
+ }
53
+ const headResult = await executeGitCommand(['rev-parse', '--short', 'HEAD'], wtPath);
54
+ return gitOk({ path: wtPath, branch: branchName, head: headResult.stdout.trim() });
55
+ }
56
+ export async function removeWorktree({ cwd, path, force, deleteBranch }) {
57
+ let branchToDelete;
58
+ if (deleteBranch) {
59
+ branchToDelete = await readCurrentBranch(path);
60
+ }
61
+ const args = force ? ['worktree', 'remove', '--force', path] : ['worktree', 'remove', path];
62
+ const result = await executeGitCommand(args, cwd);
63
+ if (result.exitCode !== 0) {
64
+ return gitFail(result.stderr || 'Failed to remove worktree');
65
+ }
66
+ if (branchToDelete && deleteBranch) {
67
+ await executeGitCommand(['branch', '-d', branchToDelete], cwd);
68
+ }
69
+ await executeGitCommand(['worktree', 'prune'], cwd);
70
+ return gitOk({ path });
71
+ }
72
+ async function pushWithUpstreamRetry(worktreePath, pushRemote, pushBranch, setUpstream) {
73
+ const args = setUpstream
74
+ ? ['push', '--set-upstream', pushRemote, pushBranch]
75
+ : ['push', pushRemote, pushBranch];
76
+ const result = await executeGitCommand(args, worktreePath);
77
+ if (result.exitCode === 0)
78
+ return { exitCode: 0, output: result.stderr || result.stdout, error: '' };
79
+ const needsUpstream = result.stderr.includes('no upstream') || result.stderr.includes('has no upstream');
80
+ if (!needsUpstream)
81
+ return { exitCode: result.exitCode, output: '', error: result.stderr || 'Failed to push' };
82
+ const retry = await executeGitCommand(['push', '--set-upstream', pushRemote, pushBranch], worktreePath);
83
+ if (retry.exitCode !== 0)
84
+ return { exitCode: retry.exitCode, output: '', error: retry.stderr || 'Failed to push' };
85
+ return { exitCode: 0, output: retry.stderr || retry.stdout, error: '' };
86
+ }
87
+ export async function pushWorktree({ cwd, remote, branch, setUpstream }) {
88
+ const pushRemote = remote || 'origin';
89
+ const currentBranch = await readCurrentBranch(cwd);
90
+ const pushBranch = branch || currentBranch;
91
+ const pushResult = await pushWithUpstreamRetry(cwd, pushRemote, pushBranch, !!setUpstream);
92
+ if (pushResult.exitCode !== 0) {
93
+ return gitFail(pushResult.error);
94
+ }
95
+ return gitOk({ output: pushResult.output, upstream: `${pushRemote}/${pushBranch}` });
96
+ }
97
+ export async function createWorktreePr({ cwd, title, body, baseBranch, draft }) {
98
+ const branchName = await readCurrentBranch(cwd);
99
+ const prTitle = title || branchName.replace(/[-_/]/g, ' ').replace(/^\w/, c => c.toUpperCase());
100
+ const args = ['pr', 'create', '--title', prTitle];
101
+ if (body)
102
+ args.push('--body', body);
103
+ if (baseBranch)
104
+ args.push('--base', baseBranch);
105
+ if (draft)
106
+ args.push('--draft');
107
+ const result = await spawnWithOutput('gh', args, cwd);
108
+ if (result.exitCode !== 0) {
109
+ return gitFail(result.stderr || 'Failed to create PR');
110
+ }
111
+ const prUrl = result.stdout.trim();
112
+ const prNumberMatch = prUrl.match(/\/(\d+)$/);
113
+ return gitOk({ prUrl, prNumber: prNumberMatch ? parseInt(prNumberMatch[1], 10) : 0 });
114
+ }
@@ -0,0 +1,337 @@
1
+ // Copyright (c) 2025-present Minionry, Inc.
2
+ // SPDX-License-Identifier: LicenseRef-Minionry-Software
3
+ import { spawn } from 'node:child_process';
4
+ import { createHash } from 'node:crypto';
5
+ import { closeSync, existsSync, mkdirSync, openSync, readFileSync, realpathSync, rmSync, statSync } from 'node:fs';
6
+ import { homedir } from 'node:os';
7
+ import { delimiter, join, resolve } from 'node:path';
8
+ import { ensureMachineDir, machineFile, machineLockPath, machinePath } from '../../bin/machine-layout.js';
9
+ import { withFileLock, writeFileAtomic } from '../utils/file-store.js';
10
+ import { MINIONRY_ROOT } from '../utils/paths.js';
11
+ import { resolveCliEnvironment } from './platform-url.js';
12
+ const instancesLockPath = () => machineLockPath('instances');
13
+ function normalizeDirIdentity(dir) {
14
+ try {
15
+ return realpathSync(resolve(dir));
16
+ }
17
+ catch {
18
+ return resolve(dir);
19
+ }
20
+ }
21
+ function defaultIsPidAlive(pid) {
22
+ if (!Number.isInteger(pid) || pid <= 0)
23
+ return false;
24
+ try {
25
+ process.kill(pid, 0);
26
+ return true;
27
+ }
28
+ catch (err) {
29
+ return err.code === 'EPERM';
30
+ }
31
+ }
32
+ export function pickLiveDuplicate(instances, workingDirectory, environment, options = {}) {
33
+ const { selfPid = process.pid, isPidAlive = defaultIsPidAlive, ignorePids = [] } = options;
34
+ const targetDir = normalizeDirIdentity(workingDirectory);
35
+ const isRival = (instance) => instance.pid !== selfPid &&
36
+ !ignorePids.includes(instance.pid) &&
37
+ (instance.environment ?? 'production') === environment &&
38
+ normalizeDirIdentity(instance.workingDirectory) === targetDir &&
39
+ isPidAlive(instance.pid);
40
+ const registeredEarlier = (a, b) => a.startedAt < b.startedAt || (a.startedAt === b.startedAt && a.pid < b.pid);
41
+ let duplicate = null;
42
+ for (const instance of instances) {
43
+ if (!isRival(instance))
44
+ continue;
45
+ if (duplicate === null || registeredEarlier(instance, duplicate))
46
+ duplicate = instance;
47
+ }
48
+ return duplicate;
49
+ }
50
+ export async function probeInstanceIdentity(port, timeoutMs = 1500) {
51
+ const controller = new AbortController();
52
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
53
+ try {
54
+ const res = await fetch(`http://127.0.0.1:${port}/health`, { signal: controller.signal });
55
+ if (!res.ok)
56
+ return { alive: false };
57
+ const body = (await res.json());
58
+ if (body.status !== 'ok')
59
+ return { alive: false };
60
+ return {
61
+ alive: true,
62
+ workingDirectory: typeof body.workingDirectory === 'string' ? body.workingDirectory : undefined,
63
+ environment: typeof body.environment === 'string' ? body.environment : undefined,
64
+ pid: typeof body.pid === 'number' ? body.pid : undefined,
65
+ };
66
+ }
67
+ catch {
68
+ return { alive: false };
69
+ }
70
+ finally {
71
+ clearTimeout(timer);
72
+ }
73
+ }
74
+ export async function probeInstanceHealth(port, timeoutMs = 1500) {
75
+ return (await probeInstanceIdentity(port, timeoutMs)).alive;
76
+ }
77
+ export function identityMatchesSpace(identity, workingDirectory, environment) {
78
+ if (!identity.alive)
79
+ return false;
80
+ if (!identity.workingDirectory || !identity.environment)
81
+ return false;
82
+ return (normalizeDirIdentity(identity.workingDirectory) === normalizeDirIdentity(workingDirectory) &&
83
+ identity.environment === environment);
84
+ }
85
+ export function parseSupersededPids(raw) {
86
+ if (!raw)
87
+ return [];
88
+ return raw
89
+ .split(',')
90
+ .map((s) => Number.parseInt(s.trim(), 10))
91
+ .filter((pid) => Number.isInteger(pid) && pid > 0);
92
+ }
93
+ export class InstanceRegistry {
94
+ registryPath;
95
+ currentInstance = null;
96
+ heartbeatInterval = null;
97
+ constructor() {
98
+ ensureMachineDir('state');
99
+ this.registryPath = machineFile('instances');
100
+ }
101
+ register(port, workingDirectory) {
102
+ const instance = {
103
+ id: `minionry-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
104
+ port,
105
+ url: `http://localhost:${port}`,
106
+ workingDirectory,
107
+ startedAt: Date.now(),
108
+ lastHeartbeat: Date.now(),
109
+ pid: process.pid,
110
+ environment: resolveCliEnvironment()
111
+ };
112
+ this.currentInstance = instance;
113
+ this.saveInstance(instance);
114
+ this.startHeartbeat();
115
+ return instance;
116
+ }
117
+ unregister() {
118
+ if (this.currentInstance) {
119
+ this.removeInstance(this.currentInstance.id);
120
+ this.currentInstance = null;
121
+ }
122
+ this.stopHeartbeat();
123
+ }
124
+ getCurrentInstance() {
125
+ return this.currentInstance;
126
+ }
127
+ static getAllInstances() {
128
+ const registryPath = machineFile('instances');
129
+ if (!existsSync(registryPath)) {
130
+ return [];
131
+ }
132
+ return withFileLock(instancesLockPath(), () => {
133
+ try {
134
+ const data = readFileSync(registryPath, 'utf-8');
135
+ if (!data?.trim()) {
136
+ return [];
137
+ }
138
+ const parsed = JSON.parse(data);
139
+ const instances = Array.isArray(parsed) ? parsed : [];
140
+ const now = Date.now();
141
+ const maxAge = 2 * 60 * 1000;
142
+ return instances.filter(instance => now - instance.lastHeartbeat < maxAge);
143
+ }
144
+ catch (error) {
145
+ if (error instanceof SyntaxError) {
146
+ console.warn('[InstanceRegistry] Registry file corrupted, will be recreated on next write');
147
+ }
148
+ else {
149
+ console.error('[InstanceRegistry] Failed to read instances registry:', error);
150
+ }
151
+ return [];
152
+ }
153
+ }, { timeoutMs: 3000 });
154
+ }
155
+ static getInstance(id) {
156
+ const instances = InstanceRegistry.getAllInstances();
157
+ return instances.find(i => i.id === id) || null;
158
+ }
159
+ static findLiveDuplicate(workingDirectory, environment, options = {}) {
160
+ return pickLiveDuplicate(InstanceRegistry.getAllInstances(), workingDirectory, environment, options);
161
+ }
162
+ saveInstance(instance) {
163
+ withFileLock(instancesLockPath(), () => {
164
+ let instances = [];
165
+ if (existsSync(this.registryPath)) {
166
+ try {
167
+ const data = readFileSync(this.registryPath, 'utf-8');
168
+ if (data?.trim()) {
169
+ const parsed = JSON.parse(data);
170
+ instances = Array.isArray(parsed) ? parsed : [];
171
+ }
172
+ }
173
+ catch {
174
+ instances = [];
175
+ }
176
+ }
177
+ const now = Date.now();
178
+ const maxAge = 2 * 60 * 1000;
179
+ instances = instances.filter(i => i.id !== instance.id && now - i.lastHeartbeat < maxAge);
180
+ instances.push(instance);
181
+ writeFileAtomic(this.registryPath, JSON.stringify(instances, null, 2), { mode: 0o644 });
182
+ });
183
+ }
184
+ removeInstance(id) {
185
+ withFileLock(instancesLockPath(), () => {
186
+ let instances = [];
187
+ if (existsSync(this.registryPath)) {
188
+ try {
189
+ const data = readFileSync(this.registryPath, 'utf-8');
190
+ if (data?.trim()) {
191
+ const parsed = JSON.parse(data);
192
+ instances = Array.isArray(parsed) ? parsed : [];
193
+ }
194
+ }
195
+ catch {
196
+ instances = [];
197
+ }
198
+ }
199
+ instances = instances.filter(i => i.id !== id);
200
+ writeFileAtomic(this.registryPath, JSON.stringify(instances, null, 2), { mode: 0o644 });
201
+ });
202
+ }
203
+ startHeartbeat() {
204
+ this.heartbeatInterval = setInterval(() => {
205
+ if (this.currentInstance) {
206
+ this.currentInstance.lastHeartbeat = Date.now();
207
+ this.saveInstance(this.currentInstance);
208
+ }
209
+ }, 30000);
210
+ }
211
+ stopHeartbeat() {
212
+ if (this.heartbeatInterval) {
213
+ clearInterval(this.heartbeatInterval);
214
+ this.heartbeatInterval = null;
215
+ }
216
+ }
217
+ }
218
+ export function deriveSpaceBrowserAgentId(clientId, workingDirectory) {
219
+ const canonical = `${clientId}\u0000${resolve(workingDirectory)}`;
220
+ const digest = createHash('sha256').update(canonical).digest('hex');
221
+ return `space-${digest.slice(0, 32)}`;
222
+ }
223
+ export function buildRespawnEnv(source, workingDirectory, isWin = process.platform === 'win32') {
224
+ const env = {};
225
+ for (const [key, value] of Object.entries(source)) {
226
+ if (value === undefined)
227
+ continue;
228
+ if (/^npm_/i.test(key))
229
+ continue;
230
+ if (/^PORT$/i.test(key))
231
+ continue;
232
+ if (key === 'MINIONRY_ALLOW_DUPLICATE')
233
+ continue;
234
+ if (key === 'MINIONRY_SUPERSEDES_PID')
235
+ continue;
236
+ env[key] = value;
237
+ }
238
+ collapsePath(env, isWin);
239
+ env.MINIONRY_WORKING_DIR = workingDirectory;
240
+ return env;
241
+ }
242
+ function collapsePath(env, isWin) {
243
+ if (!isWin) {
244
+ if (env.PATH)
245
+ env.PATH = dedupePathValue(env.PATH, false);
246
+ return;
247
+ }
248
+ const keys = Object.keys(env).filter((k) => /^path$/i.test(k));
249
+ if (keys.length === 0)
250
+ return;
251
+ const segments = [];
252
+ for (const k of keys) {
253
+ const value = env[k];
254
+ if (value)
255
+ segments.push(value);
256
+ delete env[k];
257
+ }
258
+ env.Path = dedupePathValue(segments.join(delimiter), true);
259
+ }
260
+ function dedupePathValue(raw, caseInsensitive) {
261
+ const seen = new Set();
262
+ const out = [];
263
+ for (const seg of raw.split(delimiter)) {
264
+ if (!seg)
265
+ continue;
266
+ const norm = caseInsensitive ? seg.toLowerCase() : seg;
267
+ if (seen.has(norm))
268
+ continue;
269
+ seen.add(norm);
270
+ out.push(seg);
271
+ }
272
+ return out.join(delimiter);
273
+ }
274
+ const updateLockDir = () => machineLockPath('update');
275
+ const UPDATE_LOCK_STALE_MS = 10 * 60 * 1000;
276
+ export function clearStaleUpdateLock(now = Date.now()) {
277
+ try {
278
+ const lockDir = updateLockDir();
279
+ if (!existsSync(lockDir))
280
+ return;
281
+ if (now - statSync(lockDir).mtimeMs < UPDATE_LOCK_STALE_MS)
282
+ return;
283
+ rmSync(lockDir, { recursive: true, force: true });
284
+ }
285
+ catch {
286
+ }
287
+ }
288
+ export function buildRespawnPlan(clientRoot, { update, isWin = process.platform === 'win32', lockDir = updateLockDir(), execPath = process.execPath, stableDir = homedir(), }) {
289
+ const npmCmd = isWin ? 'npm.cmd' : 'npm';
290
+ const relaunch = `${npmCmd} run --silent start`;
291
+ if (!update) {
292
+ return { command: npmCmd, args: ['run', '--silent', 'start'], cwd: clientRoot };
293
+ }
294
+ const quote = (s) => (isWin ? `"${s}"` : `'${s}'`);
295
+ const minionUpdate = `${quote(execPath)} ${quote(join(clientRoot, 'bin', 'minion.js'))} update`;
296
+ if (isWin) {
297
+ const chain = `md "${lockDir}" 2>nul && (set MINIONRY_SKIP_UPDATE_CHECK=1& ${minionUpdate} & rd /s /q "${lockDir}") ` +
298
+ `|| (for /l %i in (1,1,60) do @(if not exist "${lockDir}" goto :go) & timeout /t 5 /nobreak >nul) ` +
299
+ `& :go & cd /d "${clientRoot}" && ${relaunch}`;
300
+ return { command: 'cmd.exe', args: ['/c', chain], cwd: stableDir };
301
+ }
302
+ const chain = `if mkdir '${lockDir}' 2>/dev/null; then MINIONRY_SKIP_UPDATE_CHECK=1 ${minionUpdate}; rmdir '${lockDir}' 2>/dev/null; ` +
303
+ `else n=0; while [ -d '${lockDir}' ] && [ $n -lt 120 ]; do sleep 5; n=$((n+1)); done; fi; ` +
304
+ `cd '${clientRoot}' && ${relaunch}`;
305
+ return { command: 'sh', args: ['-c', chain], cwd: stableDir };
306
+ }
307
+ export function respawnInstance(workingDirectory, options = {}) {
308
+ const logDir = machinePath('logs');
309
+ if (!existsSync(logDir))
310
+ mkdirSync(logDir, { recursive: true });
311
+ const stamp = new Date().toISOString().replace(/[:.]/g, '-');
312
+ const logFd = openSync(join(logDir, `minionry-restart-${stamp}.log`), 'a');
313
+ const clientRoot = MINIONRY_ROOT;
314
+ const update = options.update === true;
315
+ if (update)
316
+ clearStaleUpdateLock();
317
+ const plan = buildRespawnPlan(clientRoot, { update });
318
+ const env = buildRespawnEnv(process.env, workingDirectory);
319
+ env.MINIONRY_SUPERSEDES_PID = String(process.pid);
320
+ const child = spawn(plan.command, plan.args, {
321
+ cwd: plan.cwd,
322
+ stdio: ['ignore', logFd, logFd],
323
+ env,
324
+ detached: true,
325
+ ...(process.platform === 'win32'
326
+ ? {
327
+ windowsHide: true,
328
+ windowsVerbatimArguments: plan.command === 'cmd.exe',
329
+ }
330
+ : {}),
331
+ });
332
+ child.on('error', (err) => {
333
+ console.error('[respawnInstance] Failed to relaunch minion server:', err);
334
+ });
335
+ child.unref();
336
+ closeSync(logFd);
337
+ }