@aiden-ade/sandbox-agent 0.1.60 → 0.1.62

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 (342) hide show
  1. package/dist/index.cjs +465 -342
  2. package/package.json +1 -1
  3. package/dist/__tests__/binding-switch-git.test.d.ts +0 -2
  4. package/dist/__tests__/binding-switch-git.test.d.ts.map +0 -1
  5. package/dist/__tests__/binding-switch-git.test.js +0 -610
  6. package/dist/__tests__/binding-switch-git.test.js.map +0 -1
  7. package/dist/__tests__/cli-ensure-install.test.d.ts +0 -2
  8. package/dist/__tests__/cli-ensure-install.test.d.ts.map +0 -1
  9. package/dist/__tests__/cli-ensure-install.test.js +0 -109
  10. package/dist/__tests__/cli-ensure-install.test.js.map +0 -1
  11. package/dist/__tests__/cli-executable.test.d.ts +0 -2
  12. package/dist/__tests__/cli-executable.test.d.ts.map +0 -1
  13. package/dist/__tests__/cli-executable.test.js +0 -129
  14. package/dist/__tests__/cli-executable.test.js.map +0 -1
  15. package/dist/__tests__/cli-help.test.d.ts +0 -2
  16. package/dist/__tests__/cli-help.test.d.ts.map +0 -1
  17. package/dist/__tests__/cli-help.test.js +0 -34
  18. package/dist/__tests__/cli-help.test.js.map +0 -1
  19. package/dist/__tests__/computer-readiness.test.d.ts +0 -2
  20. package/dist/__tests__/computer-readiness.test.d.ts.map +0 -1
  21. package/dist/__tests__/computer-readiness.test.js +0 -63
  22. package/dist/__tests__/computer-readiness.test.js.map +0 -1
  23. package/dist/__tests__/core-agent-workflow-context.test.d.ts +0 -2
  24. package/dist/__tests__/core-agent-workflow-context.test.d.ts.map +0 -1
  25. package/dist/__tests__/core-agent-workflow-context.test.js +0 -139
  26. package/dist/__tests__/core-agent-workflow-context.test.js.map +0 -1
  27. package/dist/__tests__/daemon-reconcile-ask.test.d.ts +0 -2
  28. package/dist/__tests__/daemon-reconcile-ask.test.d.ts.map +0 -1
  29. package/dist/__tests__/daemon-reconcile-ask.test.js +0 -86
  30. package/dist/__tests__/daemon-reconcile-ask.test.js.map +0 -1
  31. package/dist/__tests__/daemon-socket.integration.test.d.ts +0 -2
  32. package/dist/__tests__/daemon-socket.integration.test.d.ts.map +0 -1
  33. package/dist/__tests__/daemon-socket.integration.test.js +0 -450
  34. package/dist/__tests__/daemon-socket.integration.test.js.map +0 -1
  35. package/dist/__tests__/daemon.test.d.ts +0 -2
  36. package/dist/__tests__/daemon.test.d.ts.map +0 -1
  37. package/dist/__tests__/daemon.test.js +0 -3975
  38. package/dist/__tests__/daemon.test.js.map +0 -1
  39. package/dist/__tests__/ensure-shared-checkout-branch.test.d.ts +0 -2
  40. package/dist/__tests__/ensure-shared-checkout-branch.test.d.ts.map +0 -1
  41. package/dist/__tests__/ensure-shared-checkout-branch.test.js +0 -129
  42. package/dist/__tests__/ensure-shared-checkout-branch.test.js.map +0 -1
  43. package/dist/__tests__/event-outbox-persist-failure.test.d.ts +0 -2
  44. package/dist/__tests__/event-outbox-persist-failure.test.d.ts.map +0 -1
  45. package/dist/__tests__/event-outbox-persist-failure.test.js +0 -84
  46. package/dist/__tests__/event-outbox-persist-failure.test.js.map +0 -1
  47. package/dist/__tests__/event-outbox.test.d.ts +0 -2
  48. package/dist/__tests__/event-outbox.test.d.ts.map +0 -1
  49. package/dist/__tests__/event-outbox.test.js +0 -256
  50. package/dist/__tests__/event-outbox.test.js.map +0 -1
  51. package/dist/__tests__/fake-provider-cli.test.d.ts +0 -2
  52. package/dist/__tests__/fake-provider-cli.test.d.ts.map +0 -1
  53. package/dist/__tests__/fake-provider-cli.test.js +0 -46
  54. package/dist/__tests__/fake-provider-cli.test.js.map +0 -1
  55. package/dist/__tests__/generated-images.test.d.ts +0 -2
  56. package/dist/__tests__/generated-images.test.d.ts.map +0 -1
  57. package/dist/__tests__/generated-images.test.js +0 -132
  58. package/dist/__tests__/generated-images.test.js.map +0 -1
  59. package/dist/__tests__/lifecycle-logger.test.d.ts +0 -2
  60. package/dist/__tests__/lifecycle-logger.test.d.ts.map +0 -1
  61. package/dist/__tests__/lifecycle-logger.test.js +0 -49
  62. package/dist/__tests__/lifecycle-logger.test.js.map +0 -1
  63. package/dist/__tests__/local-skill-scan.test.d.ts +0 -2
  64. package/dist/__tests__/local-skill-scan.test.d.ts.map +0 -1
  65. package/dist/__tests__/local-skill-scan.test.js +0 -91
  66. package/dist/__tests__/local-skill-scan.test.js.map +0 -1
  67. package/dist/__tests__/local-workspace-migration.test.d.ts +0 -2
  68. package/dist/__tests__/local-workspace-migration.test.d.ts.map +0 -1
  69. package/dist/__tests__/local-workspace-migration.test.js +0 -195
  70. package/dist/__tests__/local-workspace-migration.test.js.map +0 -1
  71. package/dist/__tests__/mcp-inspect-timeout.test.d.ts +0 -2
  72. package/dist/__tests__/mcp-inspect-timeout.test.d.ts.map +0 -1
  73. package/dist/__tests__/mcp-inspect-timeout.test.js +0 -97
  74. package/dist/__tests__/mcp-inspect-timeout.test.js.map +0 -1
  75. package/dist/__tests__/mcp-readiness.test.d.ts +0 -2
  76. package/dist/__tests__/mcp-readiness.test.d.ts.map +0 -1
  77. package/dist/__tests__/mcp-readiness.test.js +0 -267
  78. package/dist/__tests__/mcp-readiness.test.js.map +0 -1
  79. package/dist/__tests__/mcp-registration-cli.test.d.ts +0 -2
  80. package/dist/__tests__/mcp-registration-cli.test.d.ts.map +0 -1
  81. package/dist/__tests__/mcp-registration-cli.test.js +0 -71
  82. package/dist/__tests__/mcp-registration-cli.test.js.map +0 -1
  83. package/dist/__tests__/mcp-registration.test.d.ts +0 -2
  84. package/dist/__tests__/mcp-registration.test.d.ts.map +0 -1
  85. package/dist/__tests__/mcp-registration.test.js +0 -426
  86. package/dist/__tests__/mcp-registration.test.js.map +0 -1
  87. package/dist/__tests__/model-scan-runner.test.d.ts +0 -2
  88. package/dist/__tests__/model-scan-runner.test.d.ts.map +0 -1
  89. package/dist/__tests__/model-scan-runner.test.js +0 -67
  90. package/dist/__tests__/model-scan-runner.test.js.map +0 -1
  91. package/dist/__tests__/network-diagnostics.test.d.ts +0 -2
  92. package/dist/__tests__/network-diagnostics.test.d.ts.map +0 -1
  93. package/dist/__tests__/network-diagnostics.test.js +0 -26
  94. package/dist/__tests__/network-diagnostics.test.js.map +0 -1
  95. package/dist/__tests__/observable-map.test.d.ts +0 -2
  96. package/dist/__tests__/observable-map.test.d.ts.map +0 -1
  97. package/dist/__tests__/observable-map.test.js +0 -54
  98. package/dist/__tests__/observable-map.test.js.map +0 -1
  99. package/dist/__tests__/provider-auth-preflight.test.d.ts +0 -2
  100. package/dist/__tests__/provider-auth-preflight.test.d.ts.map +0 -1
  101. package/dist/__tests__/provider-auth-preflight.test.js +0 -123
  102. package/dist/__tests__/provider-auth-preflight.test.js.map +0 -1
  103. package/dist/__tests__/provider-model-discovery.test.d.ts +0 -2
  104. package/dist/__tests__/provider-model-discovery.test.d.ts.map +0 -1
  105. package/dist/__tests__/provider-model-discovery.test.js +0 -472
  106. package/dist/__tests__/provider-model-discovery.test.js.map +0 -1
  107. package/dist/__tests__/remove-worktrees-if-clean.test.d.ts +0 -2
  108. package/dist/__tests__/remove-worktrees-if-clean.test.d.ts.map +0 -1
  109. package/dist/__tests__/remove-worktrees-if-clean.test.js +0 -221
  110. package/dist/__tests__/remove-worktrees-if-clean.test.js.map +0 -1
  111. package/dist/__tests__/resume-fallback-env.test.d.ts +0 -2
  112. package/dist/__tests__/resume-fallback-env.test.d.ts.map +0 -1
  113. package/dist/__tests__/resume-fallback-env.test.js +0 -28
  114. package/dist/__tests__/resume-fallback-env.test.js.map +0 -1
  115. package/dist/__tests__/run-journal.test.d.ts +0 -2
  116. package/dist/__tests__/run-journal.test.d.ts.map +0 -1
  117. package/dist/__tests__/run-journal.test.js +0 -121
  118. package/dist/__tests__/run-journal.test.js.map +0 -1
  119. package/dist/__tests__/run-start-gate.test.d.ts +0 -2
  120. package/dist/__tests__/run-start-gate.test.d.ts.map +0 -1
  121. package/dist/__tests__/run-start-gate.test.js +0 -29
  122. package/dist/__tests__/run-start-gate.test.js.map +0 -1
  123. package/dist/__tests__/runtime-capabilities-sync.test.d.ts +0 -2
  124. package/dist/__tests__/runtime-capabilities-sync.test.d.ts.map +0 -1
  125. package/dist/__tests__/runtime-capabilities-sync.test.js +0 -66
  126. package/dist/__tests__/runtime-capabilities-sync.test.js.map +0 -1
  127. package/dist/__tests__/runtime-config.test.d.ts +0 -2
  128. package/dist/__tests__/runtime-config.test.d.ts.map +0 -1
  129. package/dist/__tests__/runtime-config.test.js +0 -102
  130. package/dist/__tests__/runtime-config.test.js.map +0 -1
  131. package/dist/__tests__/runtime-presenter-heartbeat.test.d.ts +0 -2
  132. package/dist/__tests__/runtime-presenter-heartbeat.test.d.ts.map +0 -1
  133. package/dist/__tests__/runtime-presenter-heartbeat.test.js +0 -159
  134. package/dist/__tests__/runtime-presenter-heartbeat.test.js.map +0 -1
  135. package/dist/__tests__/sandbox-outbox.test.d.ts +0 -2
  136. package/dist/__tests__/sandbox-outbox.test.d.ts.map +0 -1
  137. package/dist/__tests__/sandbox-outbox.test.js +0 -214
  138. package/dist/__tests__/sandbox-outbox.test.js.map +0 -1
  139. package/dist/__tests__/sandbox-workflow-context.test.d.ts +0 -2
  140. package/dist/__tests__/sandbox-workflow-context.test.d.ts.map +0 -1
  141. package/dist/__tests__/sandbox-workflow-context.test.js +0 -396
  142. package/dist/__tests__/sandbox-workflow-context.test.js.map +0 -1
  143. package/dist/__tests__/service-manager.test.d.ts +0 -2
  144. package/dist/__tests__/service-manager.test.d.ts.map +0 -1
  145. package/dist/__tests__/service-manager.test.js +0 -50
  146. package/dist/__tests__/service-manager.test.js.map +0 -1
  147. package/dist/__tests__/sleep-inhibitor.test.d.ts +0 -2
  148. package/dist/__tests__/sleep-inhibitor.test.d.ts.map +0 -1
  149. package/dist/__tests__/sleep-inhibitor.test.js +0 -236
  150. package/dist/__tests__/sleep-inhibitor.test.js.map +0 -1
  151. package/dist/__tests__/status-local-workspace.test.d.ts +0 -2
  152. package/dist/__tests__/status-local-workspace.test.d.ts.map +0 -1
  153. package/dist/__tests__/status-local-workspace.test.js +0 -121
  154. package/dist/__tests__/status-local-workspace.test.js.map +0 -1
  155. package/dist/__tests__/suspension-detector.test.d.ts +0 -2
  156. package/dist/__tests__/suspension-detector.test.d.ts.map +0 -1
  157. package/dist/__tests__/suspension-detector.test.js +0 -112
  158. package/dist/__tests__/suspension-detector.test.js.map +0 -1
  159. package/dist/__tests__/web-presenter-parent-events.test.d.ts +0 -2
  160. package/dist/__tests__/web-presenter-parent-events.test.d.ts.map +0 -1
  161. package/dist/__tests__/web-presenter-parent-events.test.js +0 -130
  162. package/dist/__tests__/web-presenter-parent-events.test.js.map +0 -1
  163. package/dist/__tests__/workspace-relocation.test.d.ts +0 -2
  164. package/dist/__tests__/workspace-relocation.test.d.ts.map +0 -1
  165. package/dist/__tests__/workspace-relocation.test.js +0 -66
  166. package/dist/__tests__/workspace-relocation.test.js.map +0 -1
  167. package/dist/__tests__/workspace-run-lease.test.d.ts +0 -2
  168. package/dist/__tests__/workspace-run-lease.test.d.ts.map +0 -1
  169. package/dist/__tests__/workspace-run-lease.test.js +0 -230
  170. package/dist/__tests__/workspace-run-lease.test.js.map +0 -1
  171. package/dist/__tests__/ws-client-heartbeat-volatile.test.d.ts +0 -2
  172. package/dist/__tests__/ws-client-heartbeat-volatile.test.d.ts.map +0 -1
  173. package/dist/__tests__/ws-client-heartbeat-volatile.test.js +0 -61
  174. package/dist/__tests__/ws-client-heartbeat-volatile.test.js.map +0 -1
  175. package/dist/__tests__/ws-client-heartbeat.integration.test.d.ts +0 -2
  176. package/dist/__tests__/ws-client-heartbeat.integration.test.d.ts.map +0 -1
  177. package/dist/__tests__/ws-client-heartbeat.integration.test.js +0 -109
  178. package/dist/__tests__/ws-client-heartbeat.integration.test.js.map +0 -1
  179. package/dist/__tests__/ws-client-user-message-ack.integration.test.d.ts +0 -2
  180. package/dist/__tests__/ws-client-user-message-ack.integration.test.d.ts.map +0 -1
  181. package/dist/__tests__/ws-client-user-message-ack.integration.test.js +0 -167
  182. package/dist/__tests__/ws-client-user-message-ack.integration.test.js.map +0 -1
  183. package/dist/aiden-system-prompt.d.ts +0 -6
  184. package/dist/aiden-system-prompt.d.ts.map +0 -1
  185. package/dist/aiden-system-prompt.js +0 -6
  186. package/dist/aiden-system-prompt.js.map +0 -1
  187. package/dist/binding-switch-git.d.ts +0 -26
  188. package/dist/binding-switch-git.d.ts.map +0 -1
  189. package/dist/binding-switch-git.js +0 -973
  190. package/dist/binding-switch-git.js.map +0 -1
  191. package/dist/cli-ensure-install.d.ts +0 -39
  192. package/dist/cli-ensure-install.d.ts.map +0 -1
  193. package/dist/cli-ensure-install.js +0 -133
  194. package/dist/cli-ensure-install.js.map +0 -1
  195. package/dist/cli-executable.d.ts +0 -97
  196. package/dist/cli-executable.d.ts.map +0 -1
  197. package/dist/cli-executable.js +0 -432
  198. package/dist/cli-executable.js.map +0 -1
  199. package/dist/cli-help.d.ts +0 -7
  200. package/dist/cli-help.d.ts.map +0 -1
  201. package/dist/cli-help.js +0 -49
  202. package/dist/cli-help.js.map +0 -1
  203. package/dist/computer-readiness.d.ts +0 -42
  204. package/dist/computer-readiness.d.ts.map +0 -1
  205. package/dist/computer-readiness.js +0 -191
  206. package/dist/computer-readiness.js.map +0 -1
  207. package/dist/core-agent.d.ts +0 -47
  208. package/dist/core-agent.d.ts.map +0 -1
  209. package/dist/core-agent.js +0 -364
  210. package/dist/core-agent.js.map +0 -1
  211. package/dist/daemon.d.ts +0 -392
  212. package/dist/daemon.d.ts.map +0 -1
  213. package/dist/daemon.js +0 -4711
  214. package/dist/daemon.js.map +0 -1
  215. package/dist/event-outbox.d.ts +0 -83
  216. package/dist/event-outbox.d.ts.map +0 -1
  217. package/dist/event-outbox.js +0 -440
  218. package/dist/event-outbox.js.map +0 -1
  219. package/dist/fake-provider-cli.d.ts +0 -13
  220. package/dist/fake-provider-cli.d.ts.map +0 -1
  221. package/dist/fake-provider-cli.js +0 -53
  222. package/dist/fake-provider-cli.js.map +0 -1
  223. package/dist/generated-images.d.ts +0 -2
  224. package/dist/generated-images.d.ts.map +0 -1
  225. package/dist/generated-images.js +0 -2
  226. package/dist/generated-images.js.map +0 -1
  227. package/dist/index.d.ts +0 -6
  228. package/dist/index.d.ts.map +0 -1
  229. package/dist/index.js +0 -209
  230. package/dist/index.js.map +0 -1
  231. package/dist/lifecycle-logger.d.ts +0 -25
  232. package/dist/lifecycle-logger.d.ts.map +0 -1
  233. package/dist/lifecycle-logger.js +0 -73
  234. package/dist/lifecycle-logger.js.map +0 -1
  235. package/dist/local-skill-scan.d.ts +0 -21
  236. package/dist/local-skill-scan.d.ts.map +0 -1
  237. package/dist/local-skill-scan.js +0 -109
  238. package/dist/local-skill-scan.js.map +0 -1
  239. package/dist/local-workspace-migration.d.ts +0 -68
  240. package/dist/local-workspace-migration.d.ts.map +0 -1
  241. package/dist/local-workspace-migration.js +0 -157
  242. package/dist/local-workspace-migration.js.map +0 -1
  243. package/dist/mcp-readiness.d.ts +0 -59
  244. package/dist/mcp-readiness.d.ts.map +0 -1
  245. package/dist/mcp-readiness.js +0 -202
  246. package/dist/mcp-readiness.js.map +0 -1
  247. package/dist/mcp-registration.d.ts +0 -75
  248. package/dist/mcp-registration.d.ts.map +0 -1
  249. package/dist/mcp-registration.js +0 -563
  250. package/dist/mcp-registration.js.map +0 -1
  251. package/dist/model-scan-runner.d.ts +0 -15
  252. package/dist/model-scan-runner.d.ts.map +0 -1
  253. package/dist/model-scan-runner.js +0 -51
  254. package/dist/model-scan-runner.js.map +0 -1
  255. package/dist/network-diagnostics.d.ts +0 -8
  256. package/dist/network-diagnostics.d.ts.map +0 -1
  257. package/dist/network-diagnostics.js +0 -56
  258. package/dist/network-diagnostics.js.map +0 -1
  259. package/dist/observable-map.d.ts +0 -25
  260. package/dist/observable-map.d.ts.map +0 -1
  261. package/dist/observable-map.js +0 -42
  262. package/dist/observable-map.js.map +0 -1
  263. package/dist/provider-auth-preflight.d.ts +0 -22
  264. package/dist/provider-auth-preflight.d.ts.map +0 -1
  265. package/dist/provider-auth-preflight.js +0 -110
  266. package/dist/provider-auth-preflight.js.map +0 -1
  267. package/dist/provider-model-discovery.d.ts +0 -38
  268. package/dist/provider-model-discovery.d.ts.map +0 -1
  269. package/dist/provider-model-discovery.js +0 -519
  270. package/dist/provider-model-discovery.js.map +0 -1
  271. package/dist/resume-fallback-env.d.ts +0 -20
  272. package/dist/resume-fallback-env.d.ts.map +0 -1
  273. package/dist/resume-fallback-env.js +0 -30
  274. package/dist/resume-fallback-env.js.map +0 -1
  275. package/dist/run-journal.d.ts +0 -53
  276. package/dist/run-journal.d.ts.map +0 -1
  277. package/dist/run-journal.js +0 -193
  278. package/dist/run-journal.js.map +0 -1
  279. package/dist/run-start-gate.d.ts +0 -9
  280. package/dist/run-start-gate.d.ts.map +0 -1
  281. package/dist/run-start-gate.js +0 -24
  282. package/dist/run-start-gate.js.map +0 -1
  283. package/dist/runtime-backends.d.ts +0 -2
  284. package/dist/runtime-backends.d.ts.map +0 -1
  285. package/dist/runtime-backends.js +0 -2
  286. package/dist/runtime-backends.js.map +0 -1
  287. package/dist/runtime-capabilities-sync.d.ts +0 -33
  288. package/dist/runtime-capabilities-sync.d.ts.map +0 -1
  289. package/dist/runtime-capabilities-sync.js +0 -56
  290. package/dist/runtime-capabilities-sync.js.map +0 -1
  291. package/dist/runtime-config.d.ts +0 -24
  292. package/dist/runtime-config.d.ts.map +0 -1
  293. package/dist/runtime-config.js +0 -124
  294. package/dist/runtime-config.js.map +0 -1
  295. package/dist/sandbox-outbox.d.ts +0 -122
  296. package/dist/sandbox-outbox.d.ts.map +0 -1
  297. package/dist/sandbox-outbox.js +0 -180
  298. package/dist/sandbox-outbox.js.map +0 -1
  299. package/dist/sandbox.d.ts +0 -44
  300. package/dist/sandbox.d.ts.map +0 -1
  301. package/dist/sandbox.js +0 -438
  302. package/dist/sandbox.js.map +0 -1
  303. package/dist/service-manager.d.ts +0 -28
  304. package/dist/service-manager.d.ts.map +0 -1
  305. package/dist/service-manager.js +0 -259
  306. package/dist/service-manager.js.map +0 -1
  307. package/dist/sleep-inhibitor.d.ts +0 -111
  308. package/dist/sleep-inhibitor.d.ts.map +0 -1
  309. package/dist/sleep-inhibitor.js +0 -412
  310. package/dist/sleep-inhibitor.js.map +0 -1
  311. package/dist/suspension-detector.d.ts +0 -48
  312. package/dist/suspension-detector.d.ts.map +0 -1
  313. package/dist/suspension-detector.js +0 -89
  314. package/dist/suspension-detector.js.map +0 -1
  315. package/dist/types.d.ts +0 -6
  316. package/dist/types.d.ts.map +0 -1
  317. package/dist/types.js +0 -6
  318. package/dist/types.js.map +0 -1
  319. package/dist/updater.d.ts +0 -15
  320. package/dist/updater.d.ts.map +0 -1
  321. package/dist/updater.js +0 -50
  322. package/dist/updater.js.map +0 -1
  323. package/dist/version.d.ts +0 -2
  324. package/dist/version.d.ts.map +0 -1
  325. package/dist/version.js +0 -2
  326. package/dist/version.js.map +0 -1
  327. package/dist/web-presenter.d.ts +0 -55
  328. package/dist/web-presenter.d.ts.map +0 -1
  329. package/dist/web-presenter.js +0 -265
  330. package/dist/web-presenter.js.map +0 -1
  331. package/dist/workspace-relocation.d.ts +0 -22
  332. package/dist/workspace-relocation.d.ts.map +0 -1
  333. package/dist/workspace-relocation.js +0 -90
  334. package/dist/workspace-relocation.js.map +0 -1
  335. package/dist/workspace-run-lease.d.ts +0 -97
  336. package/dist/workspace-run-lease.d.ts.map +0 -1
  337. package/dist/workspace-run-lease.js +0 -241
  338. package/dist/workspace-run-lease.js.map +0 -1
  339. package/dist/ws-client.d.ts +0 -194
  340. package/dist/ws-client.d.ts.map +0 -1
  341. package/dist/ws-client.js +0 -282
  342. package/dist/ws-client.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiden-ade/sandbox-agent",
3
- "version": "0.1.60",
3
+ "version": "0.1.62",
4
4
  "type": "module",
5
5
  "description": "Alan agent runtime — cloud sandbox and local daemon (alan-agent CLI)",
6
6
  "bin": {
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=binding-switch-git.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"binding-switch-git.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/binding-switch-git.test.ts"],"names":[],"mappings":""}
@@ -1,610 +0,0 @@
1
- import { execFileSync } from "node:child_process";
2
- import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync, } from "node:fs";
3
- import { tmpdir } from "node:os";
4
- import { basename, dirname, join } from "node:path";
5
- import { afterEach, describe, expect, it, vi } from "vitest";
6
- import { executeBindingSwitchGitRpc } from "../binding-switch-git.js";
7
- const tempDirs = [];
8
- vi.setConfig({ testTimeout: 20_000 });
9
- function git(cwd, ...args) {
10
- return execFileSync("git", args, { cwd, encoding: "utf8" }).trim();
11
- }
12
- function makeTempDir() {
13
- const path = mkdtempSync(join(tmpdir(), "alan-binding-switch-git-"));
14
- tempDirs.push(path);
15
- return path;
16
- }
17
- function configureIdentity(repoPath) {
18
- git(repoPath, "config", "user.name", "Alan Test");
19
- git(repoPath, "config", "user.email", "alan-test@example.com");
20
- }
21
- function createRepository(root, name) {
22
- const repoPath = join(root, name);
23
- mkdirSync(repoPath);
24
- git(repoPath, "init", "-b", "main");
25
- configureIdentity(repoPath);
26
- writeFileSync(join(repoPath, "tracked.txt"), "initial\n");
27
- git(repoPath, "add", "tracked.txt");
28
- git(repoPath, "commit", "-m", "Initial commit");
29
- return repoPath;
30
- }
31
- function createRemoteFixture(root) {
32
- const remotePath = join(root, "remote.git");
33
- mkdirSync(remotePath);
34
- git(remotePath, "init", "--bare");
35
- git(remotePath, "symbolic-ref", "HEAD", "refs/heads/main");
36
- const seedPath = createRepository(root, "seed");
37
- git(seedPath, "remote", "add", "origin", remotePath);
38
- git(seedPath, "push", "-u", "origin", "main");
39
- const mainSha = git(seedPath, "rev-parse", "HEAD");
40
- const sourcePath = join(root, "source");
41
- const targetPath = join(root, "target");
42
- git(root, "clone", remotePath, sourcePath);
43
- git(root, "clone", remotePath, targetPath);
44
- configureIdentity(sourcePath);
45
- configureIdentity(targetPath);
46
- return { remotePath, sourcePath, targetPath, mainSha };
47
- }
48
- function common(repoPath, suffix = "one") {
49
- let expectedRemoteUrl;
50
- try {
51
- expectedRemoteUrl = git(repoPath, "config", "--get", "remote.origin.url");
52
- }
53
- catch {
54
- expectedRemoteUrl = undefined;
55
- }
56
- return {
57
- switchId: `switch-${suffix}`,
58
- repoKey: `repo-${suffix}`,
59
- repoPath,
60
- projectPath: repoPath,
61
- expectedRepoRoot: repoPath,
62
- expectedRemoteUrl,
63
- expectedBranch: git(repoPath, "branch", "--show-current"),
64
- };
65
- }
66
- async function waitForPath(path, timeoutMs = 5_000) {
67
- const deadline = Date.now() + timeoutMs;
68
- while (!existsSync(path)) {
69
- if (Date.now() >= deadline)
70
- throw new Error(`Timed out waiting for ${path}`);
71
- await new Promise((resolvePromise) => setTimeout(resolvePromise, 20));
72
- }
73
- }
74
- afterEach(() => {
75
- for (const path of tempDirs.splice(0))
76
- rmSync(path, { recursive: true, force: true });
77
- });
78
- describe("binding-switch git RPC", () => {
79
- it("reports and verifies the repository fetch remote during status inspection", async () => {
80
- const root = makeTempDir();
81
- const { remotePath, sourcePath } = createRemoteFixture(root);
82
- await expect(executeBindingSwitchGitRpc("git.status", common(sourcePath))).resolves.toMatchObject({
83
- repoRoot: sourcePath,
84
- remoteUrl: remotePath,
85
- branch: "main",
86
- dirty: false,
87
- });
88
- });
89
- it("rejects a mismatched expected remote during status inspection", async () => {
90
- const root = makeTempDir();
91
- const { sourcePath } = createRemoteFixture(root);
92
- const otherRemote = join(root, "other.git");
93
- mkdirSync(otherRemote);
94
- git(otherRemote, "init", "--bare");
95
- await expect(executeBindingSwitchGitRpc("git.status", {
96
- ...common(sourcePath, "status-remote-mismatch"),
97
- expectedRemoteUrl: otherRemote,
98
- })).rejects.toMatchObject({ code: "remote_url_mismatch" });
99
- });
100
- it("reports a missing remote without weakening repository path authorization", async () => {
101
- const root = makeTempDir();
102
- const repoPath = createRepository(root, "repo-without-remote");
103
- const { expectedRemoteUrl: _expectedRemoteUrl, ...request } = common(repoPath, "status-no-remote");
104
- await expect(executeBindingSwitchGitRpc("git.status", request)).resolves.toMatchObject({
105
- repoRoot: repoPath,
106
- remoteUrl: null,
107
- });
108
- });
109
- it("rejects credential-bearing remotes during status inspection", async () => {
110
- const root = makeTempDir();
111
- const repoPath = createRepository(root, "repo-with-credential-remote");
112
- git(repoPath, "remote", "add", "origin", "https://user:secret@example.invalid/owner/repo.git");
113
- const { expectedRemoteUrl: _expectedRemoteUrl, ...request } = common(repoPath, "status-credential-remote");
114
- await expect(executeBindingSwitchGitRpc("git.status", request)).rejects.toMatchObject({
115
- code: "remote_url_credentials_rejected",
116
- });
117
- });
118
- it("creates one guarded checkpoint and replays it by switch/repository trailers", async () => {
119
- const root = makeTempDir();
120
- const repoPath = createRepository(root, "repo");
121
- const originalHead = git(repoPath, "rev-parse", "HEAD");
122
- writeFileSync(join(repoPath, "tracked.txt"), "checkpointed\n");
123
- writeFileSync(join(repoPath, "untracked.txt"), "new\n");
124
- const status = (await executeBindingSwitchGitRpc("git.status", common(repoPath)));
125
- expect(status).toMatchObject({
126
- headSha: originalHead,
127
- dirty: true,
128
- filesChanged: 2,
129
- });
130
- expect(status.statusFingerprint).toMatch(/^[0-9a-f]{64}$/);
131
- const request = {
132
- ...common(repoPath),
133
- expectedHeadSha: originalHead,
134
- expectedStatusFingerprint: status.statusFingerprint,
135
- expectedProposedTreeSha: status.proposedTreeSha,
136
- commitMessage: "chore: checkpoint work before environment switch\n\nCo-Authored-By: Alan <agent@tryalan.ai>",
137
- authorName: "Siddharth Talesara",
138
- authorEmail: "siddharth@example.com",
139
- };
140
- const first = (await executeBindingSwitchGitRpc("git.checkpoint", request));
141
- expect(first.replayed).toBe(false);
142
- expect(first.sha).not.toBe(originalHead);
143
- expect(git(repoPath, "show", "-s", "--format=%B", "HEAD")).toContain("Alan-Switch-Id: switch-one");
144
- expect(git(repoPath, "show", "-s", "--format=%B", "HEAD")).toContain("Alan-Repository-Id: repo-one");
145
- expect(git(repoPath, "show", "-s", "--format=%B", "HEAD")).toContain("Co-Authored-By: Alan <agent@tryalan.ai>");
146
- await expect(executeBindingSwitchGitRpc("git.checkpoint", request)).resolves.toMatchObject({
147
- sha: first.sha,
148
- replayed: true,
149
- });
150
- writeFileSync(join(repoPath, "tracked.txt"), "changed after checkpoint\n");
151
- await expect(executeBindingSwitchGitRpc("git.checkpoint", request)).resolves.toMatchObject({
152
- sha: first.sha,
153
- replayed: true,
154
- });
155
- expect(git(repoPath, "status", "--porcelain")).toContain("tracked.txt");
156
- });
157
- it("invalidates approval when bytes change at the same tracked or untracked path", async () => {
158
- const root = makeTempDir();
159
- const repoPath = createRepository(root, "repo");
160
- writeFileSync(join(repoPath, "tracked.txt"), "first tracked bytes\n");
161
- writeFileSync(join(repoPath, "untracked.txt"), "first untracked bytes\n");
162
- const first = (await executeBindingSwitchGitRpc("git.status", common(repoPath)));
163
- writeFileSync(join(repoPath, "tracked.txt"), "second tracked bytes\n");
164
- writeFileSync(join(repoPath, "untracked.txt"), "second untracked bytes\n");
165
- const second = (await executeBindingSwitchGitRpc("git.status", common(repoPath)));
166
- expect(second.statusFingerprint).not.toBe(first.statusFingerprint);
167
- await expect(executeBindingSwitchGitRpc("git.checkpoint", {
168
- ...common(repoPath),
169
- expectedHeadSha: first.headSha,
170
- expectedStatusFingerprint: first.statusFingerprint,
171
- expectedProposedTreeSha: first.proposedTreeSha,
172
- commitMessage: "checkpoint",
173
- authorName: "Alan",
174
- authorEmail: "agent@tryalan.ai",
175
- })).rejects.toMatchObject({ code: "source_changed_since_approval" });
176
- });
177
- it("commits only the approved proposed tree when bytes change during preparation", async () => {
178
- const root = makeTempDir();
179
- const repoPath = createRepository(root, "checkpoint-race");
180
- writeFileSync(join(repoPath, "tracked.txt"), "approved checkpoint bytes\n");
181
- const inspected = (await executeBindingSwitchGitRpc("git.status", common(repoPath, "checkpoint-race")));
182
- const barrierDir = join(root, "checkpoint-barrier");
183
- const previousBarrier = process.env.ALAN_TEST_GIT_CHECKPOINT_BARRIER_DIR;
184
- process.env.ALAN_TEST_GIT_CHECKPOINT_BARRIER_DIR = barrierDir;
185
- try {
186
- const checkpointPromise = executeBindingSwitchGitRpc("git.checkpoint", {
187
- ...common(repoPath, "checkpoint-race"),
188
- expectedHeadSha: inspected.headSha,
189
- expectedStatusFingerprint: inspected.statusFingerprint,
190
- expectedProposedTreeSha: inspected.proposedTreeSha,
191
- commitMessage: "approved checkpoint",
192
- authorName: "Alan",
193
- authorEmail: "agent@tryalan.ai",
194
- });
195
- await waitForPath(join(barrierDir, "ready"));
196
- writeFileSync(join(repoPath, "tracked.txt"), "post-approval bytes\n");
197
- writeFileSync(join(barrierDir, "release"), "release\n");
198
- const result = (await checkpointPromise);
199
- expect(result.replayed).toBe(false);
200
- expect(git(repoPath, "rev-parse", `${result.sha}^{tree}`)).toBe(inspected.proposedTreeSha);
201
- expect(git(repoPath, "show", `${result.sha}:tracked.txt`)).toBe("approved checkpoint bytes");
202
- expect(git(repoPath, "status", "--porcelain")).toContain("tracked.txt");
203
- }
204
- finally {
205
- mkdirSync(barrierDir, { recursive: true });
206
- writeFileSync(join(barrierDir, "release"), "release\n");
207
- if (previousBarrier === undefined) {
208
- delete process.env.ALAN_TEST_GIT_CHECKPOINT_BARRIER_DIR;
209
- }
210
- else {
211
- process.env.ALAN_TEST_GIT_CHECKPOINT_BARRIER_DIR = previousBarrier;
212
- }
213
- }
214
- });
215
- it("leaves both branches and the new branch index intact when HEAD changes during finalization", async () => {
216
- const root = makeTempDir();
217
- const repoPath = createRepository(root, "checkpoint-branch-race");
218
- const originalHead = git(repoPath, "rev-parse", "HEAD");
219
- git(repoPath, "branch", "other-checkpoint", originalHead);
220
- writeFileSync(join(repoPath, "tracked.txt"), "approved checkpoint bytes\n");
221
- const inspected = (await executeBindingSwitchGitRpc("git.status", common(repoPath, "checkpoint-branch-race")));
222
- const barrierDir = join(root, "checkpoint-finalize-barrier");
223
- const previousBarrier = process.env.ALAN_TEST_GIT_CHECKPOINT_FINALIZE_BARRIER_DIR;
224
- process.env.ALAN_TEST_GIT_CHECKPOINT_FINALIZE_BARRIER_DIR = barrierDir;
225
- try {
226
- const checkpointPromise = executeBindingSwitchGitRpc("git.checkpoint", {
227
- ...common(repoPath, "checkpoint-branch-race"),
228
- expectedHeadSha: inspected.headSha,
229
- expectedStatusFingerprint: inspected.statusFingerprint,
230
- expectedProposedTreeSha: inspected.proposedTreeSha,
231
- commitMessage: "approved checkpoint",
232
- authorName: "Alan",
233
- authorEmail: "agent@tryalan.ai",
234
- });
235
- await waitForPath(join(barrierDir, "ready"));
236
- git(repoPath, "switch", "other-checkpoint");
237
- writeFileSync(join(barrierDir, "release"), "release\n");
238
- await expect(checkpointPromise).rejects.toMatchObject({
239
- code: "source_changed_since_approval",
240
- });
241
- expect(git(repoPath, "rev-parse", "refs/heads/main")).toBe(originalHead);
242
- expect(git(repoPath, "rev-parse", "refs/heads/other-checkpoint")).toBe(originalHead);
243
- expect(git(repoPath, "branch", "--show-current")).toBe("other-checkpoint");
244
- expect(git(repoPath, "rev-parse", "HEAD")).toBe(originalHead);
245
- expect(git(repoPath, "diff", "--cached", "--name-only")).toBe("");
246
- expect(git(repoPath, "status", "--porcelain")).toBe("M tracked.txt");
247
- expect(readFileSync(join(repoPath, "tracked.txt"), "utf8")).toBe("approved checkpoint bytes\n");
248
- }
249
- finally {
250
- mkdirSync(barrierDir, { recursive: true });
251
- writeFileSync(join(barrierDir, "release"), "release\n");
252
- if (previousBarrier === undefined) {
253
- delete process.env.ALAN_TEST_GIT_CHECKPOINT_FINALIZE_BARRIER_DIR;
254
- }
255
- else {
256
- process.env.ALAN_TEST_GIT_CHECKPOINT_FINALIZE_BARRIER_DIR = previousBarrier;
257
- }
258
- }
259
- });
260
- it("rejects checkpoint and push when the branch changes at the same HEAD", async () => {
261
- const root = makeTempDir();
262
- const checkpointRepo = createRepository(root, "checkpoint");
263
- writeFileSync(join(checkpointRepo, "tracked.txt"), "dirty on main\n");
264
- const inspected = (await executeBindingSwitchGitRpc("git.status", common(checkpointRepo, "same-sha-checkpoint")));
265
- git(checkpointRepo, "switch", "-c", "other-checkpoint");
266
- expect(git(checkpointRepo, "rev-parse", "HEAD")).toBe(inspected.headSha);
267
- await expect(executeBindingSwitchGitRpc("git.checkpoint", {
268
- ...common(checkpointRepo, "same-sha-checkpoint"),
269
- expectedBranch: "main",
270
- expectedHeadSha: inspected.headSha,
271
- expectedStatusFingerprint: inspected.statusFingerprint,
272
- expectedProposedTreeSha: inspected.proposedTreeSha,
273
- commitMessage: "must not commit another branch",
274
- authorName: "Alan",
275
- authorEmail: "agent@tryalan.ai",
276
- })).rejects.toMatchObject({ code: "source_changed_since_approval" });
277
- const { sourcePath } = createRemoteFixture(root);
278
- const approvedHead = git(sourcePath, "rev-parse", "HEAD");
279
- git(sourcePath, "switch", "-c", "other-push");
280
- expect(git(sourcePath, "rev-parse", "HEAD")).toBe(approvedHead);
281
- await expect(executeBindingSwitchGitRpc("git.push", {
282
- ...common(sourcePath, "same-sha-push"),
283
- expectedBranch: "main",
284
- expectedHeadSha: approvedHead,
285
- branch: "main",
286
- })).rejects.toMatchObject({ code: "source_changed_since_approval" });
287
- });
288
- it("publishes the source branch and lands a different bootstrap checkout at the exact SHA", async () => {
289
- const root = makeTempDir();
290
- const { remotePath, sourcePath, targetPath } = createRemoteFixture(root);
291
- git(sourcePath, "switch", "-c", "task/migrate");
292
- writeFileSync(join(sourcePath, "tracked.txt"), "source task work\n");
293
- git(sourcePath, "commit", "-am", "Task work");
294
- const pushedSha = git(sourcePath, "rev-parse", "HEAD");
295
- expect(await executeBindingSwitchGitRpc("git.push", {
296
- ...common(sourcePath),
297
- expectedHeadSha: pushedSha,
298
- branch: "task/migrate",
299
- expectedRemoteUrl: `file://${remotePath}`,
300
- })).toMatchObject({
301
- pushedSha,
302
- branch: "task/migrate",
303
- replayed: false,
304
- });
305
- expect(git(targetPath, "branch", "--show-current")).toBe("main");
306
- const prepared = await executeBindingSwitchGitRpc("git.prepare_checkout", {
307
- ...common(targetPath),
308
- branch: "task/migrate",
309
- targetSha: pushedSha,
310
- });
311
- expect(prepared).toMatchObject({
312
- path: targetPath,
313
- branch: "task/migrate",
314
- headSha: pushedSha,
315
- replayed: false,
316
- });
317
- expect(git(targetPath, "branch", "--show-current")).toBe("task/migrate");
318
- expect(git(targetPath, "rev-parse", "HEAD")).toBe(pushedSha);
319
- expect(await executeBindingSwitchGitRpc("git.prepare_checkout", {
320
- ...common(targetPath),
321
- branch: "task/migrate",
322
- targetSha: pushedSha,
323
- })).toMatchObject({ headSha: pushedSha, replayed: true });
324
- expect(await executeBindingSwitchGitRpc("git.verify_head", {
325
- ...common(targetPath),
326
- expectedSha: pushedSha,
327
- })).toMatchObject({ headSha: pushedSha, verified: true });
328
- });
329
- it("rejects verification of a dirty target even when HEAD is exact", async () => {
330
- const root = makeTempDir();
331
- const repoPath = createRepository(root, "dirty-verify");
332
- const expectedSha = git(repoPath, "rev-parse", "HEAD");
333
- writeFileSync(join(repoPath, "late-target-edit.txt"), "must block verification\n");
334
- await expect(executeBindingSwitchGitRpc("git.verify_head", {
335
- ...common(repoPath, "dirty-verify"),
336
- expectedSha,
337
- })).rejects.toMatchObject({ code: "target_checkout_dirty" });
338
- }, 60_000);
339
- it("fetches and creates an Alan-managed worktree at the published SHA", async () => {
340
- const root = makeTempDir();
341
- const { sourcePath, targetPath } = createRemoteFixture(root);
342
- git(sourcePath, "switch", "-c", "task/worktree");
343
- writeFileSync(join(sourcePath, "tracked.txt"), "worktree work\n");
344
- git(sourcePath, "commit", "-am", "Worktree task");
345
- const pushedSha = git(sourcePath, "rev-parse", "HEAD");
346
- await executeBindingSwitchGitRpc("git.push", {
347
- ...common(sourcePath, "worktree"),
348
- expectedHeadSha: pushedSha,
349
- branch: "task/worktree",
350
- });
351
- const managedRoot = join(dirname(targetPath), `${basename(targetPath)}-alan-worktrees`);
352
- const worktreePath = join(managedRoot, "switch-worktree", "repo");
353
- expect(await executeBindingSwitchGitRpc("git.prepare_worktree", {
354
- ...common(targetPath, "worktree"),
355
- worktreePath,
356
- branch: "task/worktree",
357
- targetSha: pushedSha,
358
- })).toMatchObject({
359
- path: worktreePath,
360
- branch: "task/worktree",
361
- headSha: pushedSha,
362
- replayed: false,
363
- });
364
- expect(git(worktreePath, "rev-parse", "HEAD")).toBe(pushedSha);
365
- expect(await executeBindingSwitchGitRpc("git.prepare_worktree", {
366
- ...common(targetPath, "worktree"),
367
- worktreePath,
368
- branch: "task/worktree",
369
- targetSha: pushedSha,
370
- })).toMatchObject({ headSha: pushedSha, replayed: true });
371
- });
372
- it("rejects an arbitrary repository root even when its path is otherwise valid", async () => {
373
- const root = makeTempDir();
374
- const trustedProject = createRepository(root, "trusted");
375
- const arbitraryRepo = createRepository(root, "arbitrary");
376
- writeFileSync(join(arbitraryRepo, "tracked.txt"), "arbitrary dirt\n");
377
- const status = (await executeBindingSwitchGitRpc("git.status", common(arbitraryRepo, "arbitrary")));
378
- await expect(executeBindingSwitchGitRpc("git.checkpoint", {
379
- ...common(arbitraryRepo, "arbitrary"),
380
- projectPath: trustedProject,
381
- expectedRepoRoot: arbitraryRepo,
382
- expectedHeadSha: status.headSha,
383
- expectedStatusFingerprint: status.statusFingerprint,
384
- expectedProposedTreeSha: status.proposedTreeSha,
385
- commitMessage: "must not commit",
386
- authorName: "Alan",
387
- authorEmail: "agent@tryalan.ai",
388
- })).rejects.toMatchObject({ code: "repository_path_not_authorized" });
389
- expect(git(arbitraryRepo, "status", "--porcelain")).not.toBe("");
390
- });
391
- it("rejects a mismatched remote before contacting either repository", async () => {
392
- const root = makeTempDir();
393
- const { sourcePath, remotePath } = createRemoteFixture(root);
394
- const otherRemote = join(root, "other.git");
395
- mkdirSync(otherRemote);
396
- git(otherRemote, "init", "--bare");
397
- git(sourcePath, "switch", "-c", "task/remote-guard");
398
- writeFileSync(join(sourcePath, "tracked.txt"), "guarded work\n");
399
- git(sourcePath, "commit", "-am", "Guarded work");
400
- const expectedHeadSha = git(sourcePath, "rev-parse", "HEAD");
401
- await expect(executeBindingSwitchGitRpc("git.push", {
402
- ...common(sourcePath, "remote-guard"),
403
- expectedHeadSha,
404
- branch: "task/remote-guard",
405
- expectedRemoteUrl: otherRemote,
406
- })).rejects.toMatchObject({ code: "remote_url_mismatch" });
407
- expect(git(remotePath, "for-each-ref", "--format=%(refname)", "refs/heads/task/remote-guard")).toBe("");
408
- expect(git(otherRemote, "for-each-ref", "--format=%(refname)", "refs/heads/task/remote-guard")).toBe("");
409
- });
410
- it("rejects an Alan worktree path whose symlinked ancestor escapes the managed root", async () => {
411
- const root = makeTempDir();
412
- const { sourcePath, targetPath } = createRemoteFixture(root);
413
- git(sourcePath, "switch", "-c", "task/symlink");
414
- writeFileSync(join(sourcePath, "tracked.txt"), "symlink guarded\n");
415
- git(sourcePath, "commit", "-am", "Symlink work");
416
- const pushedSha = git(sourcePath, "rev-parse", "HEAD");
417
- await executeBindingSwitchGitRpc("git.push", {
418
- ...common(sourcePath, "symlink"),
419
- expectedHeadSha: pushedSha,
420
- branch: "task/symlink",
421
- });
422
- const managedRoot = join(dirname(targetPath), `${basename(targetPath)}-alan-worktrees`);
423
- const outsideRoot = join(root, "outside");
424
- mkdirSync(managedRoot);
425
- mkdirSync(outsideRoot);
426
- symlinkSync(outsideRoot, join(managedRoot, "escape"));
427
- await expect(executeBindingSwitchGitRpc("git.prepare_worktree", {
428
- ...common(targetPath, "symlink"),
429
- worktreePath: join(managedRoot, "escape", "task"),
430
- branch: "task/symlink",
431
- targetSha: pushedSha,
432
- })).rejects.toMatchObject({ code: "worktree_path_not_authorized" });
433
- expect(existsSync(join(outsideRoot, ".git"))).toBe(false);
434
- rmSync(managedRoot, { recursive: true });
435
- symlinkSync(outsideRoot, managedRoot);
436
- await expect(executeBindingSwitchGitRpc("git.prepare_worktree", {
437
- ...common(targetPath, "symlink"),
438
- worktreePath: join(managedRoot, "task"),
439
- branch: "task/symlink",
440
- targetSha: pushedSha,
441
- })).rejects.toMatchObject({ code: "worktree_path_not_authorized" });
442
- expect(existsSync(join(outsideRoot, ".git"))).toBe(false);
443
- });
444
- it("cancels a hung network operation without letting its child finish later", async () => {
445
- const root = makeTempDir();
446
- const repoPath = createRepository(root, "repo");
447
- git(repoPath, "switch", "-c", "task/timeout");
448
- const markerPath = join(root, "network-finished");
449
- const fakeBin = join(root, "fake-bin");
450
- mkdirSync(fakeBin);
451
- const sshScript = join(fakeBin, "ssh");
452
- writeFileSync(sshScript, `#!/bin/sh\nsleep 2\nprintf finished > '${markerPath}'\nexit 1\n`);
453
- chmodSync(sshScript, 0o755);
454
- const remoteUrl = "ssh://git@example.invalid/owner/repo.git";
455
- git(repoPath, "remote", "add", "origin", remoteUrl);
456
- const previousSshCommand = process.env.ALAN_TEST_GIT_SSH_COMMAND;
457
- const previousTimeout = process.env.ALAN_TEST_GIT_NETWORK_TIMEOUT_MS;
458
- process.env.ALAN_TEST_GIT_SSH_COMMAND = sshScript;
459
- process.env.ALAN_TEST_GIT_NETWORK_TIMEOUT_MS = "100";
460
- try {
461
- await expect(executeBindingSwitchGitRpc("git.push", {
462
- ...common(repoPath, "timeout"),
463
- expectedHeadSha: git(repoPath, "rev-parse", "HEAD"),
464
- branch: "task/timeout",
465
- expectedRemoteUrl: "https://example.invalid/owner/repo.git",
466
- })).rejects.toMatchObject({ code: "git_network_timeout" });
467
- await new Promise((resolvePromise) => setTimeout(resolvePromise, 500));
468
- expect(existsSync(markerPath)).toBe(false);
469
- }
470
- finally {
471
- if (previousSshCommand === undefined)
472
- delete process.env.ALAN_TEST_GIT_SSH_COMMAND;
473
- else
474
- process.env.ALAN_TEST_GIT_SSH_COMMAND = previousSshCommand;
475
- if (previousTimeout === undefined)
476
- delete process.env.ALAN_TEST_GIT_NETWORK_TIMEOUT_MS;
477
- else
478
- process.env.ALAN_TEST_GIT_NETWORK_TIMEOUT_MS = previousTimeout;
479
- }
480
- });
481
- it("preserves published SHA evidence when the source changes during push", async () => {
482
- const root = makeTempDir();
483
- const { remotePath, sourcePath } = createRemoteFixture(root);
484
- git(sourcePath, "switch", "-c", "task/push-race");
485
- writeFileSync(join(sourcePath, "tracked.txt"), "approved work\n");
486
- git(sourcePath, "commit", "-am", "Approved work");
487
- const approvedSha = git(sourcePath, "rev-parse", "HEAD");
488
- const readyPath = join(root, "push-ready");
489
- const releasePath = join(root, "push-release");
490
- const sshScript = join(root, "blocking-ssh.sh");
491
- writeFileSync(sshScript, [
492
- "#!/bin/sh",
493
- 'if [ "$1" = "-G" ]; then exit 0; fi',
494
- 'command=""',
495
- 'for argument in "$@"; do command="$argument"; done',
496
- `case "$command" in *git-receive-pack*) touch '${readyPath}'; while [ ! -f '${releasePath}' ]; do sleep 0.02; done ;; esac`,
497
- 'exec /bin/sh -c "$command"',
498
- "",
499
- ].join("\n"));
500
- chmodSync(sshScript, 0o755);
501
- const remoteUrl = `ssh://git@localhost${remotePath}`;
502
- git(sourcePath, "remote", "set-url", "origin", remoteUrl);
503
- const previousSshCommand = process.env.ALAN_TEST_GIT_SSH_COMMAND;
504
- process.env.ALAN_TEST_GIT_SSH_COMMAND = sshScript;
505
- try {
506
- const pushPromise = executeBindingSwitchGitRpc("git.push", {
507
- ...common(sourcePath, "push-race"),
508
- expectedHeadSha: approvedSha,
509
- expectedBranch: "task/push-race",
510
- branch: "task/push-race",
511
- expectedRemoteUrl: remoteUrl,
512
- });
513
- await waitForPath(readyPath);
514
- writeFileSync(join(sourcePath, "tracked.txt"), "committed work changed during push\n");
515
- git(sourcePath, "commit", "-am", "Late local commit");
516
- const lateSha = git(sourcePath, "rev-parse", "HEAD");
517
- expect(lateSha).not.toBe(approvedSha);
518
- writeFileSync(releasePath, "release\n");
519
- await expect(pushPromise).rejects.toMatchObject({
520
- code: "source_changed_since_approval",
521
- details: {
522
- published: {
523
- pushedSha: approvedSha,
524
- branch: "task/push-race",
525
- remote: "origin",
526
- replayed: false,
527
- },
528
- },
529
- });
530
- expect(git(remotePath, "rev-parse", "refs/heads/task/push-race")).toBe(approvedSha);
531
- expect(git(sourcePath, "rev-parse", "HEAD")).toBe(lateSha);
532
- expect(git(sourcePath, "status", "--porcelain")).toBe("");
533
- }
534
- finally {
535
- writeFileSync(releasePath, "release\n");
536
- if (previousSshCommand === undefined)
537
- delete process.env.ALAN_TEST_GIT_SSH_COMMAND;
538
- else
539
- process.env.ALAN_TEST_GIT_SSH_COMMAND = previousSshCommand;
540
- }
541
- });
542
- it("rejects stale status approval and a fetched branch at the wrong SHA", async () => {
543
- const root = makeTempDir();
544
- const { sourcePath, targetPath, mainSha } = createRemoteFixture(root);
545
- git(sourcePath, "switch", "-c", "task/stale");
546
- writeFileSync(join(sourcePath, "tracked.txt"), "dirty\n");
547
- const status = (await executeBindingSwitchGitRpc("git.status", common(sourcePath, "stale")));
548
- writeFileSync(join(sourcePath, "another.txt"), "raced\n");
549
- await expect(executeBindingSwitchGitRpc("git.checkpoint", {
550
- ...common(sourcePath, "stale"),
551
- expectedHeadSha: status.headSha,
552
- expectedStatusFingerprint: status.statusFingerprint,
553
- expectedProposedTreeSha: status.proposedTreeSha,
554
- commitMessage: "checkpoint",
555
- authorName: "Alan",
556
- authorEmail: "agent@tryalan.ai",
557
- })).rejects.toThrowError(expect.objectContaining({
558
- code: "source_changed_since_approval",
559
- }));
560
- git(sourcePath, "add", "--all");
561
- git(sourcePath, "commit", "-m", "Published task work");
562
- const taskSha = git(sourcePath, "rev-parse", "HEAD");
563
- await executeBindingSwitchGitRpc("git.push", {
564
- ...common(sourcePath, "stale"),
565
- expectedHeadSha: taskSha,
566
- branch: "task/stale",
567
- });
568
- await expect(executeBindingSwitchGitRpc("git.fetch", {
569
- ...common(targetPath, "stale"),
570
- branch: "task/stale",
571
- expectedSha: mainSha,
572
- })).rejects.toThrowError(expect.objectContaining({
573
- code: "target_sha_mismatch",
574
- }));
575
- });
576
- it("never fast-forwards a dirty or divergent shared checkout", async () => {
577
- const root = makeTempDir();
578
- const { sourcePath, targetPath } = createRemoteFixture(root);
579
- git(sourcePath, "switch", "-c", "task/safe");
580
- writeFileSync(join(sourcePath, "tracked.txt"), "remote task work\n");
581
- git(sourcePath, "commit", "-am", "Remote task work");
582
- const pushedSha = git(sourcePath, "rev-parse", "HEAD");
583
- await executeBindingSwitchGitRpc("git.push", {
584
- ...common(sourcePath, "safe"),
585
- expectedHeadSha: pushedSha,
586
- branch: "task/safe",
587
- });
588
- writeFileSync(join(targetPath, "dirty.txt"), "local dirt\n");
589
- await expect(executeBindingSwitchGitRpc("git.prepare_checkout", {
590
- ...common(targetPath, "safe"),
591
- branch: "task/safe",
592
- targetSha: pushedSha,
593
- })).rejects.toThrowError(expect.objectContaining({
594
- code: "target_checkout_dirty",
595
- }));
596
- rmSync(join(targetPath, "dirty.txt"));
597
- git(targetPath, "switch", "-c", "task/safe");
598
- writeFileSync(join(targetPath, "local.txt"), "divergent\n");
599
- git(targetPath, "add", "local.txt");
600
- git(targetPath, "commit", "-m", "Local divergent work");
601
- await expect(executeBindingSwitchGitRpc("git.prepare_checkout", {
602
- ...common(targetPath, "safe"),
603
- branch: "task/safe",
604
- targetSha: pushedSha,
605
- })).rejects.toThrowError(expect.objectContaining({
606
- code: "target_branch_diverged",
607
- }));
608
- });
609
- });
610
- //# sourceMappingURL=binding-switch-git.test.js.map