runwork 0.15.1 → 0.15.2

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 (403) hide show
  1. package/dist/index.js +19272 -116
  2. package/package.json +4 -4
  3. package/dist/__tests__/curl-parser.test.d.ts +0 -1
  4. package/dist/__tests__/curl-parser.test.js +0 -250
  5. package/dist/__tests__/install-scripts.test.d.ts +0 -1
  6. package/dist/__tests__/install-scripts.test.js +0 -280
  7. package/dist/__tests__/types-manager.test.d.ts +0 -1
  8. package/dist/__tests__/types-manager.test.js +0 -89
  9. package/dist/agents/__tests__/chatgpt-registry.test.d.ts +0 -1
  10. package/dist/agents/__tests__/chatgpt-registry.test.js +0 -61
  11. package/dist/agents/__tests__/claude-code-managed-block.test.d.ts +0 -1
  12. package/dist/agents/__tests__/claude-code-managed-block.test.js +0 -105
  13. package/dist/agents/__tests__/claude-code-plugin.test.d.ts +0 -1
  14. package/dist/agents/__tests__/claude-code-plugin.test.js +0 -47
  15. package/dist/agents/__tests__/claude-code-stats.test.d.ts +0 -1
  16. package/dist/agents/__tests__/claude-code-stats.test.js +0 -390
  17. package/dist/agents/__tests__/claude-desktop-memory.test.d.ts +0 -1
  18. package/dist/agents/__tests__/claude-desktop-memory.test.js +0 -154
  19. package/dist/agents/__tests__/claude-desktop-plugin-tree.test.d.ts +0 -1
  20. package/dist/agents/__tests__/claude-desktop-plugin-tree.test.js +0 -161
  21. package/dist/agents/__tests__/claude-desktop-plugin.test.d.ts +0 -1
  22. package/dist/agents/__tests__/claude-desktop-plugin.test.js +0 -117
  23. package/dist/agents/__tests__/claude-desktop-rpm.test.d.ts +0 -1
  24. package/dist/agents/__tests__/claude-desktop-rpm.test.js +0 -126
  25. package/dist/agents/__tests__/claude-desktop-stats.test.d.ts +0 -1
  26. package/dist/agents/__tests__/claude-desktop-stats.test.js +0 -280
  27. package/dist/agents/__tests__/codex-instructions.test.d.ts +0 -1
  28. package/dist/agents/__tests__/codex-instructions.test.js +0 -57
  29. package/dist/agents/__tests__/codex-minimum-permissions.test.d.ts +0 -1
  30. package/dist/agents/__tests__/codex-minimum-permissions.test.js +0 -82
  31. package/dist/agents/__tests__/codex-stats.test.d.ts +0 -1
  32. package/dist/agents/__tests__/codex-stats.test.js +0 -370
  33. package/dist/agents/__tests__/cursor-merge.test.d.ts +0 -1
  34. package/dist/agents/__tests__/cursor-merge.test.js +0 -66
  35. package/dist/agents/__tests__/cursor-stats.test.d.ts +0 -1
  36. package/dist/agents/__tests__/cursor-stats.test.js +0 -183
  37. package/dist/agents/__tests__/defaults-merge.test.d.ts +0 -1
  38. package/dist/agents/__tests__/defaults-merge.test.js +0 -268
  39. package/dist/agents/__tests__/detection.test.d.ts +0 -1
  40. package/dist/agents/__tests__/detection.test.js +0 -89
  41. package/dist/agents/__tests__/graceful-degradation.test.d.ts +0 -1
  42. package/dist/agents/__tests__/graceful-degradation.test.js +0 -129
  43. package/dist/agents/__tests__/instruction-hint.test.d.ts +0 -1
  44. package/dist/agents/__tests__/instruction-hint.test.js +0 -69
  45. package/dist/agents/__tests__/intro-skill.test.d.ts +0 -1
  46. package/dist/agents/__tests__/intro-skill.test.js +0 -193
  47. package/dist/agents/__tests__/schema-validation.test.d.ts +0 -1
  48. package/dist/agents/__tests__/schema-validation.test.js +0 -212
  49. package/dist/agents/__tests__/skill-slug.test.d.ts +0 -1
  50. package/dist/agents/__tests__/skill-slug.test.js +0 -77
  51. package/dist/agents/claude-code.d.ts +0 -59
  52. package/dist/agents/claude-code.js +0 -753
  53. package/dist/agents/claude-desktop-plugin-tree.d.ts +0 -59
  54. package/dist/agents/claude-desktop-plugin-tree.js +0 -143
  55. package/dist/agents/claude-desktop.d.ts +0 -47
  56. package/dist/agents/claude-desktop.js +0 -684
  57. package/dist/agents/cline.d.ts +0 -15
  58. package/dist/agents/cline.js +0 -130
  59. package/dist/agents/codex.d.ts +0 -44
  60. package/dist/agents/codex.js +0 -453
  61. package/dist/agents/conversation-skills.d.ts +0 -18
  62. package/dist/agents/conversation-skills.js +0 -229
  63. package/dist/agents/cursor.d.ts +0 -35
  64. package/dist/agents/cursor.js +0 -294
  65. package/dist/agents/default-config.d.ts +0 -30
  66. package/dist/agents/default-config.js +0 -67
  67. package/dist/agents/defaults-merge.d.ts +0 -87
  68. package/dist/agents/defaults-merge.js +0 -150
  69. package/dist/agents/detect.d.ts +0 -4
  70. package/dist/agents/detect.js +0 -67
  71. package/dist/agents/detection.d.ts +0 -18
  72. package/dist/agents/detection.js +0 -94
  73. package/dist/agents/gemini.d.ts +0 -15
  74. package/dist/agents/gemini.js +0 -122
  75. package/dist/agents/generic-adapter.d.ts +0 -25
  76. package/dist/agents/generic-adapter.js +0 -151
  77. package/dist/agents/intro-skill.d.ts +0 -45
  78. package/dist/agents/intro-skill.js +0 -447
  79. package/dist/agents/registry-data.d.ts +0 -192
  80. package/dist/agents/registry-data.js +0 -669
  81. package/dist/agents/registry.d.ts +0 -13
  82. package/dist/agents/registry.js +0 -36
  83. package/dist/agents/runtime-detection.d.ts +0 -82
  84. package/dist/agents/runtime-detection.js +0 -271
  85. package/dist/agents/session-start-hook.d.ts +0 -37
  86. package/dist/agents/session-start-hook.js +0 -159
  87. package/dist/agents/types.d.ts +0 -171
  88. package/dist/agents/types.js +0 -75
  89. package/dist/agents/utils/instruction-hint.d.ts +0 -21
  90. package/dist/agents/utils/instruction-hint.js +0 -108
  91. package/dist/agents/utils/json-config.d.ts +0 -19
  92. package/dist/agents/utils/json-config.js +0 -69
  93. package/dist/agents/utils/toml-config.d.ts +0 -8
  94. package/dist/agents/utils/toml-config.js +0 -27
  95. package/dist/agents/windsurf.d.ts +0 -14
  96. package/dist/agents/windsurf.js +0 -102
  97. package/dist/api/__tests__/client.test.d.ts +0 -1
  98. package/dist/api/__tests__/client.test.js +0 -272
  99. package/dist/api/client.d.ts +0 -326
  100. package/dist/api/client.js +0 -427
  101. package/dist/auth/__tests__/login-flow.test.d.ts +0 -1
  102. package/dist/auth/__tests__/login-flow.test.js +0 -101
  103. package/dist/auth/__tests__/store.test.d.ts +0 -1
  104. package/dist/auth/__tests__/store.test.js +0 -132
  105. package/dist/auth/login-flow.d.ts +0 -26
  106. package/dist/auth/login-flow.js +0 -137
  107. package/dist/auth/store.d.ts +0 -5
  108. package/dist/auth/store.js +0 -62
  109. package/dist/commands/__tests__/clone-args.test.d.ts +0 -1
  110. package/dist/commands/__tests__/clone-args.test.js +0 -51
  111. package/dist/commands/__tests__/clone-source-result.test.d.ts +0 -1
  112. package/dist/commands/__tests__/clone-source-result.test.js +0 -45
  113. package/dist/commands/__tests__/info-merge.test.d.ts +0 -1
  114. package/dist/commands/__tests__/info-merge.test.js +0 -55
  115. package/dist/commands/__tests__/mcp-entries.test.d.ts +0 -1
  116. package/dist/commands/__tests__/mcp-entries.test.js +0 -48
  117. package/dist/commands/__tests__/setup-persona.test.d.ts +0 -1
  118. package/dist/commands/__tests__/setup-persona.test.js +0 -31
  119. package/dist/commands/__tests__/setup.test.d.ts +0 -1
  120. package/dist/commands/__tests__/setup.test.js +0 -81
  121. package/dist/commands/__tests__/sync-redetect.test.d.ts +0 -1
  122. package/dist/commands/__tests__/sync-redetect.test.js +0 -96
  123. package/dist/commands/__tests__/sync-telemetry.test.d.ts +0 -1
  124. package/dist/commands/__tests__/sync-telemetry.test.js +0 -281
  125. package/dist/commands/__tests__/upgrade.test.d.ts +0 -1
  126. package/dist/commands/__tests__/upgrade.test.js +0 -157
  127. package/dist/commands/agents.d.ts +0 -2
  128. package/dist/commands/agents.js +0 -160
  129. package/dist/commands/api-keys.d.ts +0 -2
  130. package/dist/commands/api-keys.js +0 -95
  131. package/dist/commands/apps.d.ts +0 -2
  132. package/dist/commands/apps.js +0 -82
  133. package/dist/commands/build-plugin.d.ts +0 -2
  134. package/dist/commands/build-plugin.js +0 -135
  135. package/dist/commands/clone.d.ts +0 -60
  136. package/dist/commands/clone.js +0 -271
  137. package/dist/commands/components.d.ts +0 -2
  138. package/dist/commands/components.js +0 -43
  139. package/dist/commands/deploy.d.ts +0 -2
  140. package/dist/commands/deploy.js +0 -279
  141. package/dist/commands/dev.d.ts +0 -11
  142. package/dist/commands/dev.js +0 -1011
  143. package/dist/commands/doctor.d.ts +0 -2
  144. package/dist/commands/doctor.js +0 -230
  145. package/dist/commands/endpoints.d.ts +0 -2
  146. package/dist/commands/endpoints.js +0 -189
  147. package/dist/commands/entities.d.ts +0 -2
  148. package/dist/commands/entities.js +0 -198
  149. package/dist/commands/files.d.ts +0 -2
  150. package/dist/commands/files.js +0 -193
  151. package/dist/commands/inbox.d.ts +0 -11
  152. package/dist/commands/inbox.js +0 -60
  153. package/dist/commands/info.d.ts +0 -178
  154. package/dist/commands/info.js +0 -455
  155. package/dist/commands/init.d.ts +0 -21
  156. package/dist/commands/init.js +0 -185
  157. package/dist/commands/integrations.d.ts +0 -2
  158. package/dist/commands/integrations.js +0 -233
  159. package/dist/commands/login.d.ts +0 -2
  160. package/dist/commands/login.js +0 -44
  161. package/dist/commands/logout.d.ts +0 -2
  162. package/dist/commands/logout.js +0 -13
  163. package/dist/commands/logs.d.ts +0 -2
  164. package/dist/commands/logs.js +0 -232
  165. package/dist/commands/mcp-entries.d.ts +0 -13
  166. package/dist/commands/mcp-entries.js +0 -26
  167. package/dist/commands/mcp.d.ts +0 -2
  168. package/dist/commands/mcp.js +0 -245
  169. package/dist/commands/open.d.ts +0 -2
  170. package/dist/commands/open.js +0 -53
  171. package/dist/commands/resume.d.ts +0 -2
  172. package/dist/commands/resume.js +0 -289
  173. package/dist/commands/save-convo.d.ts +0 -8
  174. package/dist/commands/save-convo.js +0 -20
  175. package/dist/commands/schedules.d.ts +0 -2
  176. package/dist/commands/schedules.js +0 -221
  177. package/dist/commands/setup.d.ts +0 -27
  178. package/dist/commands/setup.js +0 -177
  179. package/dist/commands/share-convo.d.ts +0 -24
  180. package/dist/commands/share-convo.js +0 -167
  181. package/dist/commands/skills.d.ts +0 -2
  182. package/dist/commands/skills.js +0 -352
  183. package/dist/commands/sync-telemetry.d.ts +0 -33
  184. package/dist/commands/sync-telemetry.js +0 -186
  185. package/dist/commands/sync.d.ts +0 -37
  186. package/dist/commands/sync.js +0 -702
  187. package/dist/commands/uninstall.d.ts +0 -2
  188. package/dist/commands/uninstall.js +0 -145
  189. package/dist/commands/upgrade.d.ts +0 -14
  190. package/dist/commands/upgrade.js +0 -217
  191. package/dist/commands/validate.d.ts +0 -2
  192. package/dist/commands/validate.js +0 -89
  193. package/dist/commands/welcome.d.ts +0 -1
  194. package/dist/commands/welcome.js +0 -86
  195. package/dist/commands/workflows.d.ts +0 -2
  196. package/dist/commands/workflows.js +0 -187
  197. package/dist/deploy/__tests__/deploy-state.test.d.ts +0 -1
  198. package/dist/deploy/__tests__/deploy-state.test.js +0 -80
  199. package/dist/deploy/__tests__/deploy-status.test.d.ts +0 -1
  200. package/dist/deploy/__tests__/deploy-status.test.js +0 -41
  201. package/dist/deploy/__tests__/detach-args.test.d.ts +0 -1
  202. package/dist/deploy/__tests__/detach-args.test.js +0 -30
  203. package/dist/deploy/deploy-state.d.ts +0 -36
  204. package/dist/deploy/deploy-state.js +0 -63
  205. package/dist/deploy/deploy-status.d.ts +0 -21
  206. package/dist/deploy/deploy-status.js +0 -36
  207. package/dist/deploy/detach.d.ts +0 -32
  208. package/dist/deploy/detach.js +0 -71
  209. package/dist/dev/__tests__/attach.test.d.ts +0 -1
  210. package/dist/dev/__tests__/attach.test.js +0 -296
  211. package/dist/dev/__tests__/detach.test.d.ts +0 -1
  212. package/dist/dev/__tests__/detach.test.js +0 -404
  213. package/dist/dev/__tests__/preview-url-poller.test.d.ts +0 -1
  214. package/dist/dev/__tests__/preview-url-poller.test.js +0 -149
  215. package/dist/dev/__tests__/session.test.d.ts +0 -1
  216. package/dist/dev/__tests__/session.test.js +0 -446
  217. package/dist/dev/__tests__/startup-sync.test.d.ts +0 -1
  218. package/dist/dev/__tests__/startup-sync.test.js +0 -32
  219. package/dist/dev/__tests__/stop.test.d.ts +0 -1
  220. package/dist/dev/__tests__/stop.test.js +0 -172
  221. package/dist/dev/attach.d.ts +0 -120
  222. package/dist/dev/attach.js +0 -269
  223. package/dist/dev/detach.d.ts +0 -187
  224. package/dist/dev/detach.js +0 -292
  225. package/dist/dev/preview-url-poller.d.ts +0 -35
  226. package/dist/dev/preview-url-poller.js +0 -50
  227. package/dist/dev/session.d.ts +0 -175
  228. package/dist/dev/session.js +0 -258
  229. package/dist/dev/startup-sync.d.ts +0 -23
  230. package/dist/dev/startup-sync.js +0 -14
  231. package/dist/dev/stop.d.ts +0 -52
  232. package/dist/dev/stop.js +0 -101
  233. package/dist/devtools/registry-data.d.ts +0 -46
  234. package/dist/devtools/registry-data.js +0 -40
  235. package/dist/generated/bundled-types.d.ts +0 -1
  236. package/dist/generated/bundled-types.js +0 -46
  237. package/dist/generated/version.d.ts +0 -1
  238. package/dist/generated/version.js +0 -2
  239. package/dist/git/__tests__/auto-commit.test.d.ts +0 -1
  240. package/dist/git/__tests__/auto-commit.test.js +0 -431
  241. package/dist/git/__tests__/classify-sync-error.test.d.ts +0 -1
  242. package/dist/git/__tests__/classify-sync-error.test.js +0 -57
  243. package/dist/git/__tests__/credential-helper-e2e.test.d.ts +0 -21
  244. package/dist/git/__tests__/credential-helper-e2e.test.js +0 -195
  245. package/dist/git/__tests__/credentials.test.d.ts +0 -1
  246. package/dist/git/__tests__/credentials.test.js +0 -141
  247. package/dist/git/__tests__/deploy-guard.test.d.ts +0 -1
  248. package/dist/git/__tests__/deploy-guard.test.js +0 -61
  249. package/dist/git/__tests__/ensure-git-credential-helper.test.d.ts +0 -1
  250. package/dist/git/__tests__/ensure-git-credential-helper.test.js +0 -133
  251. package/dist/git/__tests__/ensure-runwork-remote.test.d.ts +0 -1
  252. package/dist/git/__tests__/ensure-runwork-remote.test.js +0 -109
  253. package/dist/git/__tests__/identity.test.d.ts +0 -1
  254. package/dist/git/__tests__/identity.test.js +0 -146
  255. package/dist/git/__tests__/manifest.test.d.ts +0 -1
  256. package/dist/git/__tests__/manifest.test.js +0 -446
  257. package/dist/git/__tests__/preflight-resolution.test.d.ts +0 -1
  258. package/dist/git/__tests__/preflight-resolution.test.js +0 -366
  259. package/dist/git/__tests__/preflight.test.d.ts +0 -1
  260. package/dist/git/__tests__/preflight.test.js +0 -36
  261. package/dist/git/__tests__/repo-config.test.d.ts +0 -1
  262. package/dist/git/__tests__/repo-config.test.js +0 -26
  263. package/dist/git/__tests__/sync.test.d.ts +0 -1
  264. package/dist/git/__tests__/sync.test.js +0 -405
  265. package/dist/git/auto-commit.d.ts +0 -35
  266. package/dist/git/auto-commit.js +0 -293
  267. package/dist/git/classify-sync-error.d.ts +0 -21
  268. package/dist/git/classify-sync-error.js +0 -111
  269. package/dist/git/credentials.d.ts +0 -75
  270. package/dist/git/credentials.js +0 -164
  271. package/dist/git/critical-files.d.ts +0 -20
  272. package/dist/git/critical-files.js +0 -68
  273. package/dist/git/deploy-guard.d.ts +0 -53
  274. package/dist/git/deploy-guard.js +0 -78
  275. package/dist/git/identity.d.ts +0 -44
  276. package/dist/git/identity.js +0 -133
  277. package/dist/git/preflight.d.ts +0 -57
  278. package/dist/git/preflight.js +0 -276
  279. package/dist/git/remote.d.ts +0 -37
  280. package/dist/git/remote.js +0 -75
  281. package/dist/git/repo-config.d.ts +0 -8
  282. package/dist/git/repo-config.js +0 -29
  283. package/dist/git/sync.d.ts +0 -15
  284. package/dist/git/sync.js +0 -159
  285. package/dist/health/__tests__/checks.test.d.ts +0 -1
  286. package/dist/health/__tests__/checks.test.js +0 -357
  287. package/dist/health/__tests__/cli-distribution-checks.test.d.ts +0 -1
  288. package/dist/health/__tests__/cli-distribution-checks.test.js +0 -196
  289. package/dist/health/__tests__/fix.test.d.ts +0 -1
  290. package/dist/health/__tests__/fix.test.js +0 -72
  291. package/dist/health/__tests__/runner-filter.test.d.ts +0 -1
  292. package/dist/health/__tests__/runner-filter.test.js +0 -61
  293. package/dist/health/checks.d.ts +0 -42
  294. package/dist/health/checks.js +0 -636
  295. package/dist/health/fix.d.ts +0 -22
  296. package/dist/health/fix.js +0 -62
  297. package/dist/health/runner.d.ts +0 -8
  298. package/dist/health/runner.js +0 -47
  299. package/dist/health/types.d.ts +0 -12
  300. package/dist/health/types.js +0 -1
  301. package/dist/index.d.ts +0 -1
  302. package/dist/logs/__tests__/tailer-format.test.d.ts +0 -1
  303. package/dist/logs/__tests__/tailer-format.test.js +0 -43
  304. package/dist/logs/tailer.d.ts +0 -17
  305. package/dist/logs/tailer.js +0 -217
  306. package/dist/sync/__tests__/change-detect.test.d.ts +0 -1
  307. package/dist/sync/__tests__/change-detect.test.js +0 -123
  308. package/dist/sync/__tests__/hash.test.d.ts +0 -1
  309. package/dist/sync/__tests__/hash.test.js +0 -18
  310. package/dist/sync/change-detect.d.ts +0 -19
  311. package/dist/sync/change-detect.js +0 -136
  312. package/dist/sync/conflict-ui.d.ts +0 -5
  313. package/dist/sync/conflict-ui.js +0 -78
  314. package/dist/sync/executor.d.ts +0 -23
  315. package/dist/sync/executor.js +0 -102
  316. package/dist/sync/hash.d.ts +0 -1
  317. package/dist/sync/hash.js +0 -4
  318. package/dist/sync/types.d.ts +0 -24
  319. package/dist/sync/types.js +0 -1
  320. package/dist/template/manifest.d.ts +0 -17
  321. package/dist/template/manifest.js +0 -145
  322. package/dist/tools/types.d.ts +0 -52
  323. package/dist/tools/types.js +0 -8
  324. package/dist/types-manager.d.ts +0 -1
  325. package/dist/types-manager.js +0 -35
  326. package/dist/types.d.ts +0 -349
  327. package/dist/types.js +0 -1
  328. package/dist/ui/__tests__/banner.test.d.ts +0 -1
  329. package/dist/ui/__tests__/banner.test.js +0 -98
  330. package/dist/ui/__tests__/colors.test.d.ts +0 -1
  331. package/dist/ui/__tests__/colors.test.js +0 -22
  332. package/dist/ui/__tests__/keyboard.test.d.ts +0 -1
  333. package/dist/ui/__tests__/keyboard.test.js +0 -34
  334. package/dist/ui/__tests__/status-line.test.d.ts +0 -1
  335. package/dist/ui/__tests__/status-line.test.js +0 -54
  336. package/dist/ui/banner.d.ts +0 -29
  337. package/dist/ui/banner.js +0 -153
  338. package/dist/ui/colors.d.ts +0 -4
  339. package/dist/ui/colors.js +0 -7
  340. package/dist/ui/keyboard.d.ts +0 -12
  341. package/dist/ui/keyboard.js +0 -61
  342. package/dist/ui/status-line.d.ts +0 -6
  343. package/dist/ui/status-line.js +0 -53
  344. package/dist/utils/__tests__/format-error.test.d.ts +0 -1
  345. package/dist/utils/__tests__/format-error.test.js +0 -43
  346. package/dist/utils/__tests__/help-json.test.d.ts +0 -1
  347. package/dist/utils/__tests__/help-json.test.js +0 -39
  348. package/dist/utils/__tests__/http.test.d.ts +0 -1
  349. package/dist/utils/__tests__/http.test.js +0 -381
  350. package/dist/utils/__tests__/ignore-matcher.test.d.ts +0 -1
  351. package/dist/utils/__tests__/ignore-matcher.test.js +0 -205
  352. package/dist/utils/__tests__/output.test.d.ts +0 -1
  353. package/dist/utils/__tests__/output.test.js +0 -38
  354. package/dist/utils/__tests__/prompt.test.d.ts +0 -1
  355. package/dist/utils/__tests__/prompt.test.js +0 -27
  356. package/dist/utils/__tests__/resolve.test.d.ts +0 -1
  357. package/dist/utils/__tests__/resolve.test.js +0 -95
  358. package/dist/utils/__tests__/sqlite.test.d.ts +0 -1
  359. package/dist/utils/__tests__/sqlite.test.js +0 -89
  360. package/dist/utils/agent-guidance.d.ts +0 -47
  361. package/dist/utils/agent-guidance.js +0 -128
  362. package/dist/utils/app-info.d.ts +0 -70
  363. package/dist/utils/app-info.js +0 -167
  364. package/dist/utils/curl-parser.d.ts +0 -12
  365. package/dist/utils/curl-parser.js +0 -162
  366. package/dist/utils/data-input.d.ts +0 -1
  367. package/dist/utils/data-input.js +0 -38
  368. package/dist/utils/format-error.d.ts +0 -10
  369. package/dist/utils/format-error.js +0 -38
  370. package/dist/utils/fs.d.ts +0 -5
  371. package/dist/utils/fs.js +0 -23
  372. package/dist/utils/help-json.d.ts +0 -70
  373. package/dist/utils/help-json.js +0 -88
  374. package/dist/utils/http.d.ts +0 -46
  375. package/dist/utils/http.js +0 -421
  376. package/dist/utils/ignore-matcher.d.ts +0 -47
  377. package/dist/utils/ignore-matcher.js +0 -132
  378. package/dist/utils/output.d.ts +0 -17
  379. package/dist/utils/output.js +0 -27
  380. package/dist/utils/prompt.d.ts +0 -5
  381. package/dist/utils/prompt.js +0 -42
  382. package/dist/utils/resolve.d.ts +0 -12
  383. package/dist/utils/resolve.js +0 -52
  384. package/dist/utils/setup-state.d.ts +0 -3
  385. package/dist/utils/setup-state.js +0 -19
  386. package/dist/utils/sqlite-adapter.d.ts +0 -48
  387. package/dist/utils/sqlite-adapter.js +0 -147
  388. package/dist/utils/sqlite.d.ts +0 -14
  389. package/dist/utils/sqlite.js +0 -14
  390. package/dist/utils/subprocess.d.ts +0 -19
  391. package/dist/utils/subprocess.js +0 -27
  392. package/dist/utils/which.d.ts +0 -14
  393. package/dist/utils/which.js +0 -47
  394. package/dist/utils/zip.d.ts +0 -7
  395. package/dist/utils/zip.js +0 -42
  396. package/dist/validate/__tests__/validate.test.d.ts +0 -1
  397. package/dist/validate/__tests__/validate.test.js +0 -42
  398. package/dist/validate/freshness.d.ts +0 -28
  399. package/dist/validate/freshness.js +0 -48
  400. package/dist/validate/preview.d.ts +0 -21
  401. package/dist/validate/preview.js +0 -39
  402. package/dist/workspace/resolve.d.ts +0 -19
  403. package/dist/workspace/resolve.js +0 -100
@@ -1,446 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import * as fs from 'fs';
3
- import * as os from 'os';
4
- import * as path from 'path';
5
- import { SESSION_FILE_SCHEMA_VERSION, BOOT_TIME_TOLERANCE_MS, buildSessionFile, currentBootTime, getSessionPaths, getSessionState, isBootTimeStale, isPidAlive, readSessionFile, removeSessionFile, removeSessionFileIfOwned, writeSessionFile, } from '../session.js';
6
- function createTmpAppDir() {
7
- return fs.mkdtempSync(path.join(os.tmpdir(), 'runwork-session-test-'));
8
- }
9
- function makeValidSessionFile(overrides = {}) {
10
- return {
11
- version: SESSION_FILE_SCHEMA_VERSION,
12
- pid: process.pid,
13
- sessionId: 'sess_test_abc',
14
- appId: 'test-app',
15
- previewUrl: 'https://test.preview.runwork.dev',
16
- startedAt: 1_700_000_000_000,
17
- bootTime: 1_600_000_000_000,
18
- cliVersion: '0.10.2',
19
- mode: 'foreground',
20
- ...overrides,
21
- };
22
- }
23
- describe('session lifecycle primitives', () => {
24
- let appDir;
25
- beforeEach(() => {
26
- appDir = createTmpAppDir();
27
- });
28
- afterEach(() => {
29
- if (fs.existsSync(appDir)) {
30
- fs.rmSync(appDir, { recursive: true, force: true });
31
- }
32
- });
33
- describe('getSessionPaths()', () => {
34
- it('returns the four expected paths under <appDir>/.runwork', () => {
35
- const paths = getSessionPaths(appDir);
36
- expect(paths.dir).toBe(path.join(appDir, '.runwork'));
37
- expect(paths.file).toBe(path.join(appDir, '.runwork', 'dev-session.json'));
38
- expect(paths.tmpFile).toBe(path.join(appDir, '.runwork', 'dev-session.json.tmp'));
39
- expect(paths.stdoutLog).toBe(path.join(appDir, '.runwork', 'dev-stdout.log'));
40
- expect(paths.stderrLog).toBe(path.join(appDir, '.runwork', 'dev-stderr.log'));
41
- });
42
- });
43
- describe('currentBootTime() / isBootTimeStale()', () => {
44
- it('currentBootTime is finite and within a few seconds of Date.now() - os.uptime()*1000', () => {
45
- const expected = Date.now() - os.uptime() * 1000;
46
- const got = currentBootTime();
47
- expect(Number.isFinite(got)).toBe(true);
48
- expect(Math.abs(got - expected)).toBeLessThan(2_000);
49
- });
50
- it('returns false (not stale) when stored matches current exactly', () => {
51
- expect(isBootTimeStale(currentBootTime())).toBe(false);
52
- });
53
- it('returns false (not stale) for drift just under the tolerance', () => {
54
- const now = 1_000_000_000_000;
55
- const stored = now - (BOOT_TIME_TOLERANCE_MS - 1);
56
- expect(isBootTimeStale(stored, { bootTime: () => now })).toBe(false);
57
- });
58
- it('returns true (stale) for drift just over the tolerance', () => {
59
- const now = 1_000_000_000_000;
60
- const stored = now - (BOOT_TIME_TOLERANCE_MS + 1);
61
- expect(isBootTimeStale(stored, { bootTime: () => now })).toBe(true);
62
- });
63
- it('detects a reboot (large boot-time delta)', () => {
64
- const before = 1_000_000_000_000;
65
- const afterReboot = before + 24 * 60 * 60 * 1000; // 24 hours later
66
- expect(isBootTimeStale(before, { bootTime: () => afterReboot })).toBe(true);
67
- });
68
- });
69
- describe('isPidAlive()', () => {
70
- it('returns true for the currently running process', () => {
71
- expect(isPidAlive(process.pid)).toBe(true);
72
- });
73
- it('returns false for PID 0', () => {
74
- expect(isPidAlive(0)).toBe(false);
75
- });
76
- it('returns false for negative PIDs', () => {
77
- expect(isPidAlive(-1)).toBe(false);
78
- expect(isPidAlive(-99999)).toBe(false);
79
- });
80
- it('returns false for non-integer PIDs', () => {
81
- expect(isPidAlive(1.5)).toBe(false);
82
- expect(isPidAlive(NaN)).toBe(false);
83
- });
84
- it('returns false for an almost-certainly-dead high PID', () => {
85
- // 32-bit max-ish PID on platforms that support it. Vanishingly
86
- // unlikely to be assigned during test runtime.
87
- expect(isPidAlive(2_147_483_640)).toBe(false);
88
- });
89
- });
90
- describe('readSessionFile()', () => {
91
- it('returns null when the file does not exist', () => {
92
- expect(readSessionFile(appDir)).toBeNull();
93
- });
94
- it('returns null when the file is not valid JSON', () => {
95
- const { dir, file } = getSessionPaths(appDir);
96
- fs.mkdirSync(dir, { recursive: true });
97
- fs.writeFileSync(file, 'not-json{{{', 'utf-8');
98
- expect(readSessionFile(appDir)).toBeNull();
99
- });
100
- it('returns null when required fields are missing', () => {
101
- const { dir, file } = getSessionPaths(appDir);
102
- fs.mkdirSync(dir, { recursive: true });
103
- fs.writeFileSync(file, JSON.stringify({ version: 1, pid: 123 }), 'utf-8');
104
- expect(readSessionFile(appDir)).toBeNull();
105
- });
106
- it('returns null when a field has the wrong type', () => {
107
- const { dir, file } = getSessionPaths(appDir);
108
- fs.mkdirSync(dir, { recursive: true });
109
- const bad = { ...makeValidSessionFile(), pid: 'not-a-number' };
110
- fs.writeFileSync(file, JSON.stringify(bad), 'utf-8');
111
- expect(readSessionFile(appDir)).toBeNull();
112
- });
113
- it('returns null when mode is not one of the allowed strings', () => {
114
- const { dir, file } = getSessionPaths(appDir);
115
- fs.mkdirSync(dir, { recursive: true });
116
- const bad = { ...makeValidSessionFile(), mode: 'something-else' };
117
- fs.writeFileSync(file, JSON.stringify(bad), 'utf-8');
118
- expect(readSessionFile(appDir)).toBeNull();
119
- });
120
- it('returns the parsed file when valid', () => {
121
- const valid = makeValidSessionFile();
122
- writeSessionFile(appDir, valid);
123
- expect(readSessionFile(appDir)).toEqual(valid);
124
- });
125
- });
126
- describe('writeSessionFile()', () => {
127
- it('creates the .runwork directory if it does not exist', () => {
128
- const { dir } = getSessionPaths(appDir);
129
- expect(fs.existsSync(dir)).toBe(false);
130
- writeSessionFile(appDir, makeValidSessionFile());
131
- expect(fs.existsSync(dir)).toBe(true);
132
- });
133
- it('produces a file readable by readSessionFile', () => {
134
- const file = makeValidSessionFile({ previewUrl: 'https://abc.example' });
135
- writeSessionFile(appDir, file);
136
- expect(readSessionFile(appDir)).toEqual(file);
137
- });
138
- it('does not leave a .tmp file behind on a successful write', () => {
139
- writeSessionFile(appDir, makeValidSessionFile());
140
- const { tmpFile } = getSessionPaths(appDir);
141
- expect(fs.existsSync(tmpFile)).toBe(false);
142
- });
143
- it('overwrites the previous file on subsequent writes', () => {
144
- writeSessionFile(appDir, makeValidSessionFile({ previewUrl: 'https://first' }));
145
- writeSessionFile(appDir, makeValidSessionFile({ previewUrl: 'https://second' }));
146
- expect(readSessionFile(appDir)?.previewUrl).toBe('https://second');
147
- });
148
- });
149
- describe('removeSessionFile()', () => {
150
- it('removes the session file', () => {
151
- writeSessionFile(appDir, makeValidSessionFile());
152
- const { file } = getSessionPaths(appDir);
153
- expect(fs.existsSync(file)).toBe(true);
154
- removeSessionFile(appDir);
155
- expect(fs.existsSync(file)).toBe(false);
156
- });
157
- it('is idempotent (no error when the file is missing)', () => {
158
- expect(() => removeSessionFile(appDir)).not.toThrow();
159
- expect(() => removeSessionFile(appDir)).not.toThrow();
160
- });
161
- it('also removes a leftover .tmp file from a crashed mid-write', () => {
162
- const { dir, tmpFile } = getSessionPaths(appDir);
163
- fs.mkdirSync(dir, { recursive: true });
164
- fs.writeFileSync(tmpFile, '{}', 'utf-8');
165
- removeSessionFile(appDir);
166
- expect(fs.existsSync(tmpFile)).toBe(false);
167
- });
168
- });
169
- describe('removeSessionFileIfOwned()', () => {
170
- it('removes the file when the pid matches', () => {
171
- writeSessionFile(appDir, makeValidSessionFile({ pid: 12345 }));
172
- expect(removeSessionFileIfOwned(appDir, 12345)).toBe(true);
173
- expect(fs.existsSync(getSessionPaths(appDir).file)).toBe(false);
174
- });
175
- it('does NOT remove the file when the pid mismatches (another process took over)', () => {
176
- writeSessionFile(appDir, makeValidSessionFile({ pid: 99999 }));
177
- expect(removeSessionFileIfOwned(appDir, 12345)).toBe(false);
178
- expect(fs.existsSync(getSessionPaths(appDir).file)).toBe(true);
179
- });
180
- it('returns false (no-op) when the file is missing', () => {
181
- expect(removeSessionFileIfOwned(appDir, 12345)).toBe(false);
182
- });
183
- it('returns false when the file is malformed (treats as not-ours)', () => {
184
- const { dir, file } = getSessionPaths(appDir);
185
- fs.mkdirSync(dir, { recursive: true });
186
- fs.writeFileSync(file, '{ broken', 'utf-8');
187
- expect(removeSessionFileIfOwned(appDir, 12345)).toBe(false);
188
- // We don't touch malformed files -- the next `runwork dev` will
189
- // detect-and-clean via getSessionState, which is the right place
190
- // for that policy.
191
- expect(fs.existsSync(file)).toBe(true);
192
- });
193
- });
194
- describe('getSessionState()', () => {
195
- const aliveDeps = {
196
- bootTime: () => 1_600_000_000_000,
197
- pidAlive: (_pid) => true,
198
- };
199
- it('returns "none" when no file exists', () => {
200
- expect(getSessionState(appDir, 'test-app', aliveDeps)).toEqual({ state: 'none' });
201
- });
202
- it('returns "stale: malformed" when the file is not valid JSON', () => {
203
- const { dir, file } = getSessionPaths(appDir);
204
- fs.mkdirSync(dir, { recursive: true });
205
- fs.writeFileSync(file, '{ broken', 'utf-8');
206
- const result = getSessionState(appDir, 'test-app', aliveDeps);
207
- expect(result.state).toBe('stale');
208
- if (result.state === 'stale') {
209
- expect(result.reason).toBe('malformed');
210
- expect(result.file).toBeUndefined();
211
- }
212
- });
213
- it('returns "stale: malformed" when required fields are missing', () => {
214
- const { dir, file } = getSessionPaths(appDir);
215
- fs.mkdirSync(dir, { recursive: true });
216
- fs.writeFileSync(file, JSON.stringify({ version: 1, pid: 123 }), 'utf-8');
217
- const result = getSessionState(appDir, 'test-app', aliveDeps);
218
- expect(result.state).toBe('stale');
219
- if (result.state === 'stale') {
220
- expect(result.reason).toBe('malformed');
221
- }
222
- });
223
- it('returns "stale: version-mismatch" when the schema version is unknown', () => {
224
- writeSessionFile(appDir, makeValidSessionFile({ version: 999 }));
225
- const result = getSessionState(appDir, 'test-app', aliveDeps);
226
- expect(result.state).toBe('stale');
227
- if (result.state === 'stale') {
228
- expect(result.reason).toBe('version-mismatch');
229
- expect(result.file?.version).toBe(999);
230
- }
231
- });
232
- it('returns "stale: app-id-mismatch" when appId does not match the expected app', () => {
233
- writeSessionFile(appDir, makeValidSessionFile({ appId: 'a-different-app' }));
234
- const result = getSessionState(appDir, 'test-app', aliveDeps);
235
- expect(result.state).toBe('stale');
236
- if (result.state === 'stale') {
237
- expect(result.reason).toBe('app-id-mismatch');
238
- expect(result.file?.appId).toBe('a-different-app');
239
- }
240
- });
241
- it('returns "stale: boot-time-mismatch" when the system has rebooted since the session was written', () => {
242
- const writtenBoot = 1_600_000_000_000;
243
- writeSessionFile(appDir, makeValidSessionFile({ bootTime: writtenBoot }));
244
- const result = getSessionState(appDir, 'test-app', {
245
- bootTime: () => writtenBoot + 24 * 60 * 60 * 1000, // 24h later
246
- pidAlive: () => true,
247
- });
248
- expect(result.state).toBe('stale');
249
- if (result.state === 'stale') {
250
- expect(result.reason).toBe('boot-time-mismatch');
251
- }
252
- });
253
- it('returns "stale: pid-dead" when the PID is no longer running', () => {
254
- writeSessionFile(appDir, makeValidSessionFile());
255
- const result = getSessionState(appDir, 'test-app', {
256
- bootTime: () => 1_600_000_000_000,
257
- pidAlive: () => false,
258
- });
259
- expect(result.state).toBe('stale');
260
- if (result.state === 'stale') {
261
- expect(result.reason).toBe('pid-dead');
262
- }
263
- });
264
- it('returns "alive" when version + appId + bootTime + pid all check out', () => {
265
- const file = makeValidSessionFile();
266
- writeSessionFile(appDir, file);
267
- const result = getSessionState(appDir, 'test-app', aliveDeps);
268
- expect(result.state).toBe('alive');
269
- if (result.state === 'alive') {
270
- expect(result.file).toEqual(file);
271
- }
272
- });
273
- it('uses real defaults when no deps are passed (regression: undefined-spread)', () => {
274
- // Build a session file claiming to be the current process at the
275
- // current boot time, then call getSessionState with NO deps. This
276
- // exercises the real `currentBootTime` + `isPidAlive` defaults --
277
- // the path that a buggy `{...realDeps, ...{bootTime: undefined}}`
278
- // spread would have crashed.
279
- const file = makeValidSessionFile({
280
- pid: process.pid,
281
- bootTime: Date.now() - os.uptime() * 1000,
282
- });
283
- writeSessionFile(appDir, file);
284
- const result = getSessionState(appDir, 'test-app');
285
- expect(result.state).toBe('alive');
286
- });
287
- it('checks version BEFORE app-id, so a v999 with wrong appId reports version-mismatch', () => {
288
- writeSessionFile(appDir, makeValidSessionFile({ version: 999, appId: 'wrong' }));
289
- const result = getSessionState(appDir, 'test-app', aliveDeps);
290
- if (result.state === 'stale') {
291
- expect(result.reason).toBe('version-mismatch');
292
- }
293
- });
294
- it('checks bootTime BEFORE pidAlive, so a stale-rebooted file does not depend on the PID syscall', () => {
295
- writeSessionFile(appDir, makeValidSessionFile({ bootTime: 1_600_000_000_000 }));
296
- const pidAlive = (_pid) => {
297
- throw new Error('pidAlive should not be called when bootTime is stale');
298
- };
299
- const result = getSessionState(appDir, 'test-app', {
300
- bootTime: () => 1_600_000_000_000 + 24 * 60 * 60 * 1000,
301
- pidAlive,
302
- });
303
- expect(result.state).toBe('stale');
304
- if (result.state === 'stale') {
305
- expect(result.reason).toBe('boot-time-mismatch');
306
- }
307
- });
308
- });
309
- describe('buildSessionFile()', () => {
310
- it('fills version + bootTime + startedAt and passes through the rest', () => {
311
- const before = Date.now();
312
- const file = buildSessionFile({
313
- pid: 12345,
314
- sessionId: 'sess_x',
315
- appId: 'app',
316
- previewUrl: 'https://x',
317
- cliVersion: '1.2.3',
318
- mode: 'foreground',
319
- deps: { bootTime: () => 42 },
320
- });
321
- const after = Date.now();
322
- expect(file.version).toBe(SESSION_FILE_SCHEMA_VERSION);
323
- expect(file.pid).toBe(12345);
324
- expect(file.sessionId).toBe('sess_x');
325
- expect(file.appId).toBe('app');
326
- expect(file.previewUrl).toBe('https://x');
327
- expect(file.cliVersion).toBe('1.2.3');
328
- expect(file.mode).toBe('foreground');
329
- expect(file.bootTime).toBe(42);
330
- expect(file.startedAt).toBeGreaterThanOrEqual(before);
331
- expect(file.startedAt).toBeLessThanOrEqual(after);
332
- });
333
- it('honors an explicit startedAt override', () => {
334
- const file = buildSessionFile({
335
- pid: 1,
336
- sessionId: 's',
337
- appId: 'a',
338
- previewUrl: '',
339
- cliVersion: '0.0.0',
340
- mode: 'detached',
341
- startedAt: 12345,
342
- deps: { bootTime: () => 0 },
343
- });
344
- expect(file.startedAt).toBe(12345);
345
- });
346
- it('defaults noSync to false when omitted', () => {
347
- const file = buildSessionFile({
348
- pid: 1,
349
- sessionId: 's',
350
- appId: 'a',
351
- previewUrl: '',
352
- cliVersion: '0.0.0',
353
- mode: 'foreground',
354
- deps: { bootTime: () => 0 },
355
- });
356
- expect(file.noSync).toBe(false);
357
- });
358
- it('sets noSync when provided', () => {
359
- const file = buildSessionFile({
360
- pid: 1,
361
- sessionId: 's',
362
- appId: 'a',
363
- previewUrl: '',
364
- cliVersion: '0.0.0',
365
- mode: 'foreground',
366
- noSync: true,
367
- deps: { bootTime: () => 0 },
368
- });
369
- expect(file.noSync).toBe(true);
370
- });
371
- it('omits startupSync when not provided, includes it when provided', () => {
372
- const base = {
373
- pid: 1, sessionId: 's', appId: 'a', previewUrl: '', cliVersion: '0.0.0',
374
- mode: 'detached', deps: { bootTime: () => 0 },
375
- };
376
- expect(buildSessionFile(base).startupSync).toBeUndefined();
377
- const withSync = buildSessionFile({
378
- ...base,
379
- startupSync: { syncStatus: 'sync-failed', syncFailed: true, staleCode: true },
380
- });
381
- expect(withSync.startupSync).toEqual({ syncStatus: 'sync-failed', syncFailed: true, staleCode: true });
382
- });
383
- });
384
- describe('startupSync field back-compat', () => {
385
- it('round-trips startupSync through write/read', () => {
386
- const file = buildSessionFile({
387
- pid: process.pid,
388
- sessionId: 's',
389
- appId: 'a',
390
- previewUrl: 'https://x',
391
- cliVersion: '0.0.0',
392
- mode: 'detached',
393
- startupSync: { syncStatus: 'sync-failed', syncFailed: true, staleCode: true },
394
- deps: { bootTime: () => 0 },
395
- });
396
- writeSessionFile(appDir, file);
397
- const read = readSessionFile(appDir);
398
- expect(read?.startupSync).toEqual({ syncStatus: 'sync-failed', syncFailed: true, staleCode: true });
399
- });
400
- it('accepts an older session file with no startupSync field', () => {
401
- const file = buildSessionFile({
402
- pid: process.pid, sessionId: 's', appId: 'a', previewUrl: 'https://x',
403
- cliVersion: '0.0.0', mode: 'detached', deps: { bootTime: () => 0 },
404
- });
405
- writeSessionFile(appDir, file);
406
- const read = readSessionFile(appDir);
407
- expect(read).not.toBeNull();
408
- expect(read?.startupSync).toBeUndefined();
409
- });
410
- });
411
- describe('noSync field back-compat', () => {
412
- it('round-trips noSync through write/read', () => {
413
- const file = buildSessionFile({
414
- pid: process.pid,
415
- sessionId: 's',
416
- appId: 'a',
417
- previewUrl: 'https://x',
418
- cliVersion: '0.0.0',
419
- mode: 'detached',
420
- noSync: true,
421
- deps: { bootTime: () => 0 },
422
- });
423
- writeSessionFile(appDir, file);
424
- expect(readSessionFile(appDir)?.noSync).toBe(true);
425
- });
426
- it('validates a v1-style file that lacks noSync and reads it back falsy', () => {
427
- const { dir, file } = getSessionPaths(appDir);
428
- fs.mkdirSync(dir, { recursive: true });
429
- // v1 file shape: every required field present, no noSync key at all.
430
- const v1 = makeValidSessionFile();
431
- // makeValidSessionFile does not add noSync, so this is already v1-shaped.
432
- expect('noSync' in v1).toBe(false);
433
- fs.writeFileSync(file, JSON.stringify(v1), 'utf-8');
434
- const read = readSessionFile(appDir);
435
- expect(read).not.toBeNull();
436
- expect(read?.noSync).toBeFalsy();
437
- });
438
- it('rejects a file whose noSync field is the wrong type', () => {
439
- const { dir, file } = getSessionPaths(appDir);
440
- fs.mkdirSync(dir, { recursive: true });
441
- const bad = { ...makeValidSessionFile(), noSync: 'yes' };
442
- fs.writeFileSync(file, JSON.stringify(bad), 'utf-8');
443
- expect(readSessionFile(appDir)).toBeNull();
444
- });
445
- });
446
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,32 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { buildStartupSyncSummary } from '../startup-sync.js';
3
- describe('dev/startup-sync', () => {
4
- it('reports no-sync mode as neither failed nor stale', () => {
5
- expect(buildStartupSyncSummary({ noSync: true })).toEqual({
6
- syncStatus: 'no-sync',
7
- syncFailed: false,
8
- staleCode: false,
9
- });
10
- });
11
- it('flags a failed startup sync as stale (preview serving old code)', () => {
12
- expect(buildStartupSyncSummary({ noSync: false, status: 'sync-failed' })).toEqual({
13
- syncStatus: 'sync-failed',
14
- syncFailed: true,
15
- staleCode: true,
16
- });
17
- });
18
- it.each(['synced', 'merged', 'skipped'])('treats a %s startup sync as live (not stale)', (status) => {
19
- expect(buildStartupSyncSummary({ noSync: false, status })).toEqual({
20
- syncStatus: status,
21
- syncFailed: false,
22
- staleCode: false,
23
- });
24
- });
25
- it('defaults to skipped when no status is provided', () => {
26
- expect(buildStartupSyncSummary({ noSync: false })).toEqual({
27
- syncStatus: 'skipped',
28
- syncFailed: false,
29
- staleCode: false,
30
- });
31
- });
32
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,172 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
- import * as fs from 'fs';
3
- import * as os from 'os';
4
- import * as path from 'path';
5
- import { stopSession } from '../stop.js';
6
- import { buildSessionFile, getSessionPaths, writeSessionFile, } from '../session.js';
7
- function createTmpAppDir() {
8
- return fs.mkdtempSync(path.join(os.tmpdir(), 'runwork-stop-test-'));
9
- }
10
- function makeAliveFile(overrides = {}) {
11
- return buildSessionFile({
12
- pid: 99999,
13
- sessionId: 'sess_test',
14
- appId: 'test-app',
15
- previewUrl: 'https://x',
16
- cliVersion: '0.10.2',
17
- mode: 'foreground',
18
- deps: { bootTime: () => 1_000_000_000_000 },
19
- ...overrides,
20
- });
21
- }
22
- describe('stopSession()', () => {
23
- let appDir;
24
- beforeEach(() => {
25
- appDir = createTmpAppDir();
26
- });
27
- afterEach(() => {
28
- if (fs.existsSync(appDir)) {
29
- fs.rmSync(appDir, { recursive: true, force: true });
30
- }
31
- });
32
- it('returns "no-session" when there is no file to act on', async () => {
33
- const out = await stopSession(appDir, 'test-app');
34
- expect(out).toEqual({ result: 'no-session' });
35
- });
36
- it('cleans up a stale file (dead PID) without trying to kill anything', async () => {
37
- const file = makeAliveFile();
38
- writeSessionFile(appDir, file);
39
- const killProcess = vi.fn();
40
- const out = await stopSession(appDir, 'test-app', {
41
- bootTime: () => 1_000_000_000_000,
42
- pidAlive: () => false,
43
- killProcess,
44
- });
45
- expect(out.result).toBe('stale-cleaned');
46
- if (out.result === 'stale-cleaned') {
47
- expect(out.reason).toBe('pid-dead');
48
- expect(out.pid).toBe(file.pid);
49
- }
50
- expect(killProcess).not.toHaveBeenCalled();
51
- expect(fs.existsSync(getSessionPaths(appDir).file)).toBe(false);
52
- });
53
- it('cleans up a stale file (app-id mismatch) without killing the other app\'s process', async () => {
54
- writeSessionFile(appDir, makeAliveFile({ appId: 'a-different-app' }));
55
- const killProcess = vi.fn();
56
- const out = await stopSession(appDir, 'test-app', {
57
- bootTime: () => 1_000_000_000_000,
58
- pidAlive: () => true,
59
- killProcess,
60
- });
61
- expect(out.result).toBe('stale-cleaned');
62
- if (out.result === 'stale-cleaned') {
63
- expect(out.reason).toBe('app-id-mismatch');
64
- }
65
- expect(killProcess).not.toHaveBeenCalled();
66
- expect(fs.existsSync(getSessionPaths(appDir).file)).toBe(false);
67
- });
68
- it('on Unix: sends SIGTERM, waits for the PID to die, returns "stopped" gracefully', async () => {
69
- writeSessionFile(appDir, makeAliveFile({ pid: 12345 }));
70
- let alive = true;
71
- const killCalls = [];
72
- const out = await stopSession(appDir, 'test-app', {
73
- platform: 'darwin',
74
- bootTime: () => 1_000_000_000_000,
75
- pidAlive: () => alive,
76
- killProcess: (pid, signal) => {
77
- killCalls.push({ pid, signal });
78
- if (signal === 'SIGTERM') {
79
- // Simulate the child cleaning up after SIGTERM with a tiny delay.
80
- setTimeout(() => { alive = false; }, 50);
81
- }
82
- },
83
- sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
84
- graceTimeoutMs: 2000,
85
- pollIntervalMs: 25,
86
- });
87
- expect(out.result).toBe('stopped');
88
- if (out.result === 'stopped') {
89
- expect(out.pid).toBe(12345);
90
- expect(out.gracefully).toBe(true);
91
- }
92
- expect(killCalls).toHaveLength(1);
93
- expect(killCalls[0]).toEqual({ pid: 12345, signal: 'SIGTERM' });
94
- expect(fs.existsSync(getSessionPaths(appDir).file)).toBe(false);
95
- });
96
- it('on Unix: falls back to SIGKILL when SIGTERM does not stop the process within graceTimeoutMs', async () => {
97
- writeSessionFile(appDir, makeAliveFile({ pid: 12345 }));
98
- const killCalls = [];
99
- const out = await stopSession(appDir, 'test-app', {
100
- platform: 'linux',
101
- bootTime: () => 1_000_000_000_000,
102
- pidAlive: () => true, // never dies on its own
103
- killProcess: (pid, signal) => {
104
- killCalls.push({ pid, signal });
105
- },
106
- sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
107
- graceTimeoutMs: 200,
108
- pollIntervalMs: 25,
109
- });
110
- expect(out.result).toBe('stopped');
111
- if (out.result === 'stopped') {
112
- expect(out.gracefully).toBe(false);
113
- }
114
- expect(killCalls.map((c) => c.signal)).toEqual(['SIGTERM', 'SIGKILL']);
115
- expect(fs.existsSync(getSessionPaths(appDir).file)).toBe(false);
116
- });
117
- it('on Windows: kills with TerminateProcess (no signal arg), no SIGKILL fallback path', async () => {
118
- writeSessionFile(appDir, makeAliveFile({ pid: 12345 }));
119
- const killCalls = [];
120
- const out = await stopSession(appDir, 'test-app', {
121
- platform: 'win32',
122
- bootTime: () => 1_000_000_000_000,
123
- pidAlive: () => true,
124
- killProcess: (pid, signal) => {
125
- killCalls.push({ pid, signal });
126
- },
127
- sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
128
- });
129
- expect(out.result).toBe('stopped');
130
- expect(killCalls).toHaveLength(1);
131
- expect(killCalls[0].signal).toBeUndefined();
132
- expect(fs.existsSync(getSessionPaths(appDir).file)).toBe(false);
133
- });
134
- it('returns "kill-failed" but still removes the session file when the kill throws', async () => {
135
- writeSessionFile(appDir, makeAliveFile({ pid: 12345 }));
136
- const out = await stopSession(appDir, 'test-app', {
137
- platform: 'linux',
138
- bootTime: () => 1_000_000_000_000,
139
- pidAlive: () => true,
140
- killProcess: () => {
141
- const err = new Error('EPERM');
142
- err.code = 'EPERM';
143
- throw err;
144
- },
145
- sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
146
- });
147
- expect(out.result).toBe('kill-failed');
148
- if (out.result === 'kill-failed') {
149
- expect(out.pid).toBe(12345);
150
- }
151
- expect(fs.existsSync(getSessionPaths(appDir).file)).toBe(false);
152
- });
153
- it('treats the session file disappearing during the SIGTERM wait as graceful exit', async () => {
154
- writeSessionFile(appDir, makeAliveFile({ pid: 12345 }));
155
- const out = await stopSession(appDir, 'test-app', {
156
- platform: 'darwin',
157
- bootTime: () => 1_000_000_000_000,
158
- pidAlive: () => true, // PID stays alive in the mock...
159
- killProcess: () => {
160
- // ...but the "child" removes its file mid-grace-period.
161
- setTimeout(() => { fs.rmSync(getSessionPaths(appDir).file); }, 30);
162
- },
163
- sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
164
- graceTimeoutMs: 500,
165
- pollIntervalMs: 10,
166
- });
167
- expect(out.result).toBe('stopped');
168
- if (out.result === 'stopped') {
169
- expect(out.gracefully).toBe(true);
170
- }
171
- });
172
- });