runwork 0.15.1 → 0.16.0

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 (404) hide show
  1. package/bundled-types/task-delegation/task-delegation/types.d.ts +6 -0
  2. package/dist/index.js +20237 -116
  3. package/package.json +4 -4
  4. package/dist/__tests__/curl-parser.test.d.ts +0 -1
  5. package/dist/__tests__/curl-parser.test.js +0 -250
  6. package/dist/__tests__/install-scripts.test.d.ts +0 -1
  7. package/dist/__tests__/install-scripts.test.js +0 -280
  8. package/dist/__tests__/types-manager.test.d.ts +0 -1
  9. package/dist/__tests__/types-manager.test.js +0 -89
  10. package/dist/agents/__tests__/chatgpt-registry.test.d.ts +0 -1
  11. package/dist/agents/__tests__/chatgpt-registry.test.js +0 -61
  12. package/dist/agents/__tests__/claude-code-managed-block.test.d.ts +0 -1
  13. package/dist/agents/__tests__/claude-code-managed-block.test.js +0 -105
  14. package/dist/agents/__tests__/claude-code-plugin.test.d.ts +0 -1
  15. package/dist/agents/__tests__/claude-code-plugin.test.js +0 -47
  16. package/dist/agents/__tests__/claude-code-stats.test.d.ts +0 -1
  17. package/dist/agents/__tests__/claude-code-stats.test.js +0 -390
  18. package/dist/agents/__tests__/claude-desktop-memory.test.d.ts +0 -1
  19. package/dist/agents/__tests__/claude-desktop-memory.test.js +0 -154
  20. package/dist/agents/__tests__/claude-desktop-plugin-tree.test.d.ts +0 -1
  21. package/dist/agents/__tests__/claude-desktop-plugin-tree.test.js +0 -161
  22. package/dist/agents/__tests__/claude-desktop-plugin.test.d.ts +0 -1
  23. package/dist/agents/__tests__/claude-desktop-plugin.test.js +0 -117
  24. package/dist/agents/__tests__/claude-desktop-rpm.test.d.ts +0 -1
  25. package/dist/agents/__tests__/claude-desktop-rpm.test.js +0 -126
  26. package/dist/agents/__tests__/claude-desktop-stats.test.d.ts +0 -1
  27. package/dist/agents/__tests__/claude-desktop-stats.test.js +0 -280
  28. package/dist/agents/__tests__/codex-instructions.test.d.ts +0 -1
  29. package/dist/agents/__tests__/codex-instructions.test.js +0 -57
  30. package/dist/agents/__tests__/codex-minimum-permissions.test.d.ts +0 -1
  31. package/dist/agents/__tests__/codex-minimum-permissions.test.js +0 -82
  32. package/dist/agents/__tests__/codex-stats.test.d.ts +0 -1
  33. package/dist/agents/__tests__/codex-stats.test.js +0 -370
  34. package/dist/agents/__tests__/cursor-merge.test.d.ts +0 -1
  35. package/dist/agents/__tests__/cursor-merge.test.js +0 -66
  36. package/dist/agents/__tests__/cursor-stats.test.d.ts +0 -1
  37. package/dist/agents/__tests__/cursor-stats.test.js +0 -183
  38. package/dist/agents/__tests__/defaults-merge.test.d.ts +0 -1
  39. package/dist/agents/__tests__/defaults-merge.test.js +0 -268
  40. package/dist/agents/__tests__/detection.test.d.ts +0 -1
  41. package/dist/agents/__tests__/detection.test.js +0 -89
  42. package/dist/agents/__tests__/graceful-degradation.test.d.ts +0 -1
  43. package/dist/agents/__tests__/graceful-degradation.test.js +0 -129
  44. package/dist/agents/__tests__/instruction-hint.test.d.ts +0 -1
  45. package/dist/agents/__tests__/instruction-hint.test.js +0 -69
  46. package/dist/agents/__tests__/intro-skill.test.d.ts +0 -1
  47. package/dist/agents/__tests__/intro-skill.test.js +0 -193
  48. package/dist/agents/__tests__/schema-validation.test.d.ts +0 -1
  49. package/dist/agents/__tests__/schema-validation.test.js +0 -212
  50. package/dist/agents/__tests__/skill-slug.test.d.ts +0 -1
  51. package/dist/agents/__tests__/skill-slug.test.js +0 -77
  52. package/dist/agents/claude-code.d.ts +0 -59
  53. package/dist/agents/claude-code.js +0 -753
  54. package/dist/agents/claude-desktop-plugin-tree.d.ts +0 -59
  55. package/dist/agents/claude-desktop-plugin-tree.js +0 -143
  56. package/dist/agents/claude-desktop.d.ts +0 -47
  57. package/dist/agents/claude-desktop.js +0 -684
  58. package/dist/agents/cline.d.ts +0 -15
  59. package/dist/agents/cline.js +0 -130
  60. package/dist/agents/codex.d.ts +0 -44
  61. package/dist/agents/codex.js +0 -453
  62. package/dist/agents/conversation-skills.d.ts +0 -18
  63. package/dist/agents/conversation-skills.js +0 -229
  64. package/dist/agents/cursor.d.ts +0 -35
  65. package/dist/agents/cursor.js +0 -294
  66. package/dist/agents/default-config.d.ts +0 -30
  67. package/dist/agents/default-config.js +0 -67
  68. package/dist/agents/defaults-merge.d.ts +0 -87
  69. package/dist/agents/defaults-merge.js +0 -150
  70. package/dist/agents/detect.d.ts +0 -4
  71. package/dist/agents/detect.js +0 -67
  72. package/dist/agents/detection.d.ts +0 -18
  73. package/dist/agents/detection.js +0 -94
  74. package/dist/agents/gemini.d.ts +0 -15
  75. package/dist/agents/gemini.js +0 -122
  76. package/dist/agents/generic-adapter.d.ts +0 -25
  77. package/dist/agents/generic-adapter.js +0 -151
  78. package/dist/agents/intro-skill.d.ts +0 -45
  79. package/dist/agents/intro-skill.js +0 -447
  80. package/dist/agents/registry-data.d.ts +0 -192
  81. package/dist/agents/registry-data.js +0 -669
  82. package/dist/agents/registry.d.ts +0 -13
  83. package/dist/agents/registry.js +0 -36
  84. package/dist/agents/runtime-detection.d.ts +0 -82
  85. package/dist/agents/runtime-detection.js +0 -271
  86. package/dist/agents/session-start-hook.d.ts +0 -37
  87. package/dist/agents/session-start-hook.js +0 -159
  88. package/dist/agents/types.d.ts +0 -171
  89. package/dist/agents/types.js +0 -75
  90. package/dist/agents/utils/instruction-hint.d.ts +0 -21
  91. package/dist/agents/utils/instruction-hint.js +0 -108
  92. package/dist/agents/utils/json-config.d.ts +0 -19
  93. package/dist/agents/utils/json-config.js +0 -69
  94. package/dist/agents/utils/toml-config.d.ts +0 -8
  95. package/dist/agents/utils/toml-config.js +0 -27
  96. package/dist/agents/windsurf.d.ts +0 -14
  97. package/dist/agents/windsurf.js +0 -102
  98. package/dist/api/__tests__/client.test.d.ts +0 -1
  99. package/dist/api/__tests__/client.test.js +0 -272
  100. package/dist/api/client.d.ts +0 -326
  101. package/dist/api/client.js +0 -427
  102. package/dist/auth/__tests__/login-flow.test.d.ts +0 -1
  103. package/dist/auth/__tests__/login-flow.test.js +0 -101
  104. package/dist/auth/__tests__/store.test.d.ts +0 -1
  105. package/dist/auth/__tests__/store.test.js +0 -132
  106. package/dist/auth/login-flow.d.ts +0 -26
  107. package/dist/auth/login-flow.js +0 -137
  108. package/dist/auth/store.d.ts +0 -5
  109. package/dist/auth/store.js +0 -62
  110. package/dist/commands/__tests__/clone-args.test.d.ts +0 -1
  111. package/dist/commands/__tests__/clone-args.test.js +0 -51
  112. package/dist/commands/__tests__/clone-source-result.test.d.ts +0 -1
  113. package/dist/commands/__tests__/clone-source-result.test.js +0 -45
  114. package/dist/commands/__tests__/info-merge.test.d.ts +0 -1
  115. package/dist/commands/__tests__/info-merge.test.js +0 -55
  116. package/dist/commands/__tests__/mcp-entries.test.d.ts +0 -1
  117. package/dist/commands/__tests__/mcp-entries.test.js +0 -48
  118. package/dist/commands/__tests__/setup-persona.test.d.ts +0 -1
  119. package/dist/commands/__tests__/setup-persona.test.js +0 -31
  120. package/dist/commands/__tests__/setup.test.d.ts +0 -1
  121. package/dist/commands/__tests__/setup.test.js +0 -81
  122. package/dist/commands/__tests__/sync-redetect.test.d.ts +0 -1
  123. package/dist/commands/__tests__/sync-redetect.test.js +0 -96
  124. package/dist/commands/__tests__/sync-telemetry.test.d.ts +0 -1
  125. package/dist/commands/__tests__/sync-telemetry.test.js +0 -281
  126. package/dist/commands/__tests__/upgrade.test.d.ts +0 -1
  127. package/dist/commands/__tests__/upgrade.test.js +0 -157
  128. package/dist/commands/agents.d.ts +0 -2
  129. package/dist/commands/agents.js +0 -160
  130. package/dist/commands/api-keys.d.ts +0 -2
  131. package/dist/commands/api-keys.js +0 -95
  132. package/dist/commands/apps.d.ts +0 -2
  133. package/dist/commands/apps.js +0 -82
  134. package/dist/commands/build-plugin.d.ts +0 -2
  135. package/dist/commands/build-plugin.js +0 -135
  136. package/dist/commands/clone.d.ts +0 -60
  137. package/dist/commands/clone.js +0 -271
  138. package/dist/commands/components.d.ts +0 -2
  139. package/dist/commands/components.js +0 -43
  140. package/dist/commands/deploy.d.ts +0 -2
  141. package/dist/commands/deploy.js +0 -279
  142. package/dist/commands/dev.d.ts +0 -11
  143. package/dist/commands/dev.js +0 -1011
  144. package/dist/commands/doctor.d.ts +0 -2
  145. package/dist/commands/doctor.js +0 -230
  146. package/dist/commands/endpoints.d.ts +0 -2
  147. package/dist/commands/endpoints.js +0 -189
  148. package/dist/commands/entities.d.ts +0 -2
  149. package/dist/commands/entities.js +0 -198
  150. package/dist/commands/files.d.ts +0 -2
  151. package/dist/commands/files.js +0 -193
  152. package/dist/commands/inbox.d.ts +0 -11
  153. package/dist/commands/inbox.js +0 -60
  154. package/dist/commands/info.d.ts +0 -178
  155. package/dist/commands/info.js +0 -455
  156. package/dist/commands/init.d.ts +0 -21
  157. package/dist/commands/init.js +0 -185
  158. package/dist/commands/integrations.d.ts +0 -2
  159. package/dist/commands/integrations.js +0 -233
  160. package/dist/commands/login.d.ts +0 -2
  161. package/dist/commands/login.js +0 -44
  162. package/dist/commands/logout.d.ts +0 -2
  163. package/dist/commands/logout.js +0 -13
  164. package/dist/commands/logs.d.ts +0 -2
  165. package/dist/commands/logs.js +0 -232
  166. package/dist/commands/mcp-entries.d.ts +0 -13
  167. package/dist/commands/mcp-entries.js +0 -26
  168. package/dist/commands/mcp.d.ts +0 -2
  169. package/dist/commands/mcp.js +0 -245
  170. package/dist/commands/open.d.ts +0 -2
  171. package/dist/commands/open.js +0 -53
  172. package/dist/commands/resume.d.ts +0 -2
  173. package/dist/commands/resume.js +0 -289
  174. package/dist/commands/save-convo.d.ts +0 -8
  175. package/dist/commands/save-convo.js +0 -20
  176. package/dist/commands/schedules.d.ts +0 -2
  177. package/dist/commands/schedules.js +0 -221
  178. package/dist/commands/setup.d.ts +0 -27
  179. package/dist/commands/setup.js +0 -177
  180. package/dist/commands/share-convo.d.ts +0 -24
  181. package/dist/commands/share-convo.js +0 -167
  182. package/dist/commands/skills.d.ts +0 -2
  183. package/dist/commands/skills.js +0 -352
  184. package/dist/commands/sync-telemetry.d.ts +0 -33
  185. package/dist/commands/sync-telemetry.js +0 -186
  186. package/dist/commands/sync.d.ts +0 -37
  187. package/dist/commands/sync.js +0 -702
  188. package/dist/commands/uninstall.d.ts +0 -2
  189. package/dist/commands/uninstall.js +0 -145
  190. package/dist/commands/upgrade.d.ts +0 -14
  191. package/dist/commands/upgrade.js +0 -217
  192. package/dist/commands/validate.d.ts +0 -2
  193. package/dist/commands/validate.js +0 -89
  194. package/dist/commands/welcome.d.ts +0 -1
  195. package/dist/commands/welcome.js +0 -86
  196. package/dist/commands/workflows.d.ts +0 -2
  197. package/dist/commands/workflows.js +0 -187
  198. package/dist/deploy/__tests__/deploy-state.test.d.ts +0 -1
  199. package/dist/deploy/__tests__/deploy-state.test.js +0 -80
  200. package/dist/deploy/__tests__/deploy-status.test.d.ts +0 -1
  201. package/dist/deploy/__tests__/deploy-status.test.js +0 -41
  202. package/dist/deploy/__tests__/detach-args.test.d.ts +0 -1
  203. package/dist/deploy/__tests__/detach-args.test.js +0 -30
  204. package/dist/deploy/deploy-state.d.ts +0 -36
  205. package/dist/deploy/deploy-state.js +0 -63
  206. package/dist/deploy/deploy-status.d.ts +0 -21
  207. package/dist/deploy/deploy-status.js +0 -36
  208. package/dist/deploy/detach.d.ts +0 -32
  209. package/dist/deploy/detach.js +0 -71
  210. package/dist/dev/__tests__/attach.test.d.ts +0 -1
  211. package/dist/dev/__tests__/attach.test.js +0 -296
  212. package/dist/dev/__tests__/detach.test.d.ts +0 -1
  213. package/dist/dev/__tests__/detach.test.js +0 -404
  214. package/dist/dev/__tests__/preview-url-poller.test.d.ts +0 -1
  215. package/dist/dev/__tests__/preview-url-poller.test.js +0 -149
  216. package/dist/dev/__tests__/session.test.d.ts +0 -1
  217. package/dist/dev/__tests__/session.test.js +0 -446
  218. package/dist/dev/__tests__/startup-sync.test.d.ts +0 -1
  219. package/dist/dev/__tests__/startup-sync.test.js +0 -32
  220. package/dist/dev/__tests__/stop.test.d.ts +0 -1
  221. package/dist/dev/__tests__/stop.test.js +0 -172
  222. package/dist/dev/attach.d.ts +0 -120
  223. package/dist/dev/attach.js +0 -269
  224. package/dist/dev/detach.d.ts +0 -187
  225. package/dist/dev/detach.js +0 -292
  226. package/dist/dev/preview-url-poller.d.ts +0 -35
  227. package/dist/dev/preview-url-poller.js +0 -50
  228. package/dist/dev/session.d.ts +0 -175
  229. package/dist/dev/session.js +0 -258
  230. package/dist/dev/startup-sync.d.ts +0 -23
  231. package/dist/dev/startup-sync.js +0 -14
  232. package/dist/dev/stop.d.ts +0 -52
  233. package/dist/dev/stop.js +0 -101
  234. package/dist/devtools/registry-data.d.ts +0 -46
  235. package/dist/devtools/registry-data.js +0 -40
  236. package/dist/generated/bundled-types.d.ts +0 -1
  237. package/dist/generated/bundled-types.js +0 -46
  238. package/dist/generated/version.d.ts +0 -1
  239. package/dist/generated/version.js +0 -2
  240. package/dist/git/__tests__/auto-commit.test.d.ts +0 -1
  241. package/dist/git/__tests__/auto-commit.test.js +0 -431
  242. package/dist/git/__tests__/classify-sync-error.test.d.ts +0 -1
  243. package/dist/git/__tests__/classify-sync-error.test.js +0 -57
  244. package/dist/git/__tests__/credential-helper-e2e.test.d.ts +0 -21
  245. package/dist/git/__tests__/credential-helper-e2e.test.js +0 -195
  246. package/dist/git/__tests__/credentials.test.d.ts +0 -1
  247. package/dist/git/__tests__/credentials.test.js +0 -141
  248. package/dist/git/__tests__/deploy-guard.test.d.ts +0 -1
  249. package/dist/git/__tests__/deploy-guard.test.js +0 -61
  250. package/dist/git/__tests__/ensure-git-credential-helper.test.d.ts +0 -1
  251. package/dist/git/__tests__/ensure-git-credential-helper.test.js +0 -133
  252. package/dist/git/__tests__/ensure-runwork-remote.test.d.ts +0 -1
  253. package/dist/git/__tests__/ensure-runwork-remote.test.js +0 -109
  254. package/dist/git/__tests__/identity.test.d.ts +0 -1
  255. package/dist/git/__tests__/identity.test.js +0 -146
  256. package/dist/git/__tests__/manifest.test.d.ts +0 -1
  257. package/dist/git/__tests__/manifest.test.js +0 -446
  258. package/dist/git/__tests__/preflight-resolution.test.d.ts +0 -1
  259. package/dist/git/__tests__/preflight-resolution.test.js +0 -366
  260. package/dist/git/__tests__/preflight.test.d.ts +0 -1
  261. package/dist/git/__tests__/preflight.test.js +0 -36
  262. package/dist/git/__tests__/repo-config.test.d.ts +0 -1
  263. package/dist/git/__tests__/repo-config.test.js +0 -26
  264. package/dist/git/__tests__/sync.test.d.ts +0 -1
  265. package/dist/git/__tests__/sync.test.js +0 -405
  266. package/dist/git/auto-commit.d.ts +0 -35
  267. package/dist/git/auto-commit.js +0 -293
  268. package/dist/git/classify-sync-error.d.ts +0 -21
  269. package/dist/git/classify-sync-error.js +0 -111
  270. package/dist/git/credentials.d.ts +0 -75
  271. package/dist/git/credentials.js +0 -164
  272. package/dist/git/critical-files.d.ts +0 -20
  273. package/dist/git/critical-files.js +0 -68
  274. package/dist/git/deploy-guard.d.ts +0 -53
  275. package/dist/git/deploy-guard.js +0 -78
  276. package/dist/git/identity.d.ts +0 -44
  277. package/dist/git/identity.js +0 -133
  278. package/dist/git/preflight.d.ts +0 -57
  279. package/dist/git/preflight.js +0 -276
  280. package/dist/git/remote.d.ts +0 -37
  281. package/dist/git/remote.js +0 -75
  282. package/dist/git/repo-config.d.ts +0 -8
  283. package/dist/git/repo-config.js +0 -29
  284. package/dist/git/sync.d.ts +0 -15
  285. package/dist/git/sync.js +0 -159
  286. package/dist/health/__tests__/checks.test.d.ts +0 -1
  287. package/dist/health/__tests__/checks.test.js +0 -357
  288. package/dist/health/__tests__/cli-distribution-checks.test.d.ts +0 -1
  289. package/dist/health/__tests__/cli-distribution-checks.test.js +0 -196
  290. package/dist/health/__tests__/fix.test.d.ts +0 -1
  291. package/dist/health/__tests__/fix.test.js +0 -72
  292. package/dist/health/__tests__/runner-filter.test.d.ts +0 -1
  293. package/dist/health/__tests__/runner-filter.test.js +0 -61
  294. package/dist/health/checks.d.ts +0 -42
  295. package/dist/health/checks.js +0 -636
  296. package/dist/health/fix.d.ts +0 -22
  297. package/dist/health/fix.js +0 -62
  298. package/dist/health/runner.d.ts +0 -8
  299. package/dist/health/runner.js +0 -47
  300. package/dist/health/types.d.ts +0 -12
  301. package/dist/health/types.js +0 -1
  302. package/dist/index.d.ts +0 -1
  303. package/dist/logs/__tests__/tailer-format.test.d.ts +0 -1
  304. package/dist/logs/__tests__/tailer-format.test.js +0 -43
  305. package/dist/logs/tailer.d.ts +0 -17
  306. package/dist/logs/tailer.js +0 -217
  307. package/dist/sync/__tests__/change-detect.test.d.ts +0 -1
  308. package/dist/sync/__tests__/change-detect.test.js +0 -123
  309. package/dist/sync/__tests__/hash.test.d.ts +0 -1
  310. package/dist/sync/__tests__/hash.test.js +0 -18
  311. package/dist/sync/change-detect.d.ts +0 -19
  312. package/dist/sync/change-detect.js +0 -136
  313. package/dist/sync/conflict-ui.d.ts +0 -5
  314. package/dist/sync/conflict-ui.js +0 -78
  315. package/dist/sync/executor.d.ts +0 -23
  316. package/dist/sync/executor.js +0 -102
  317. package/dist/sync/hash.d.ts +0 -1
  318. package/dist/sync/hash.js +0 -4
  319. package/dist/sync/types.d.ts +0 -24
  320. package/dist/sync/types.js +0 -1
  321. package/dist/template/manifest.d.ts +0 -17
  322. package/dist/template/manifest.js +0 -145
  323. package/dist/tools/types.d.ts +0 -52
  324. package/dist/tools/types.js +0 -8
  325. package/dist/types-manager.d.ts +0 -1
  326. package/dist/types-manager.js +0 -35
  327. package/dist/types.d.ts +0 -349
  328. package/dist/types.js +0 -1
  329. package/dist/ui/__tests__/banner.test.d.ts +0 -1
  330. package/dist/ui/__tests__/banner.test.js +0 -98
  331. package/dist/ui/__tests__/colors.test.d.ts +0 -1
  332. package/dist/ui/__tests__/colors.test.js +0 -22
  333. package/dist/ui/__tests__/keyboard.test.d.ts +0 -1
  334. package/dist/ui/__tests__/keyboard.test.js +0 -34
  335. package/dist/ui/__tests__/status-line.test.d.ts +0 -1
  336. package/dist/ui/__tests__/status-line.test.js +0 -54
  337. package/dist/ui/banner.d.ts +0 -29
  338. package/dist/ui/banner.js +0 -153
  339. package/dist/ui/colors.d.ts +0 -4
  340. package/dist/ui/colors.js +0 -7
  341. package/dist/ui/keyboard.d.ts +0 -12
  342. package/dist/ui/keyboard.js +0 -61
  343. package/dist/ui/status-line.d.ts +0 -6
  344. package/dist/ui/status-line.js +0 -53
  345. package/dist/utils/__tests__/format-error.test.d.ts +0 -1
  346. package/dist/utils/__tests__/format-error.test.js +0 -43
  347. package/dist/utils/__tests__/help-json.test.d.ts +0 -1
  348. package/dist/utils/__tests__/help-json.test.js +0 -39
  349. package/dist/utils/__tests__/http.test.d.ts +0 -1
  350. package/dist/utils/__tests__/http.test.js +0 -381
  351. package/dist/utils/__tests__/ignore-matcher.test.d.ts +0 -1
  352. package/dist/utils/__tests__/ignore-matcher.test.js +0 -205
  353. package/dist/utils/__tests__/output.test.d.ts +0 -1
  354. package/dist/utils/__tests__/output.test.js +0 -38
  355. package/dist/utils/__tests__/prompt.test.d.ts +0 -1
  356. package/dist/utils/__tests__/prompt.test.js +0 -27
  357. package/dist/utils/__tests__/resolve.test.d.ts +0 -1
  358. package/dist/utils/__tests__/resolve.test.js +0 -95
  359. package/dist/utils/__tests__/sqlite.test.d.ts +0 -1
  360. package/dist/utils/__tests__/sqlite.test.js +0 -89
  361. package/dist/utils/agent-guidance.d.ts +0 -47
  362. package/dist/utils/agent-guidance.js +0 -128
  363. package/dist/utils/app-info.d.ts +0 -70
  364. package/dist/utils/app-info.js +0 -167
  365. package/dist/utils/curl-parser.d.ts +0 -12
  366. package/dist/utils/curl-parser.js +0 -162
  367. package/dist/utils/data-input.d.ts +0 -1
  368. package/dist/utils/data-input.js +0 -38
  369. package/dist/utils/format-error.d.ts +0 -10
  370. package/dist/utils/format-error.js +0 -38
  371. package/dist/utils/fs.d.ts +0 -5
  372. package/dist/utils/fs.js +0 -23
  373. package/dist/utils/help-json.d.ts +0 -70
  374. package/dist/utils/help-json.js +0 -88
  375. package/dist/utils/http.d.ts +0 -46
  376. package/dist/utils/http.js +0 -421
  377. package/dist/utils/ignore-matcher.d.ts +0 -47
  378. package/dist/utils/ignore-matcher.js +0 -132
  379. package/dist/utils/output.d.ts +0 -17
  380. package/dist/utils/output.js +0 -27
  381. package/dist/utils/prompt.d.ts +0 -5
  382. package/dist/utils/prompt.js +0 -42
  383. package/dist/utils/resolve.d.ts +0 -12
  384. package/dist/utils/resolve.js +0 -52
  385. package/dist/utils/setup-state.d.ts +0 -3
  386. package/dist/utils/setup-state.js +0 -19
  387. package/dist/utils/sqlite-adapter.d.ts +0 -48
  388. package/dist/utils/sqlite-adapter.js +0 -147
  389. package/dist/utils/sqlite.d.ts +0 -14
  390. package/dist/utils/sqlite.js +0 -14
  391. package/dist/utils/subprocess.d.ts +0 -19
  392. package/dist/utils/subprocess.js +0 -27
  393. package/dist/utils/which.d.ts +0 -14
  394. package/dist/utils/which.js +0 -47
  395. package/dist/utils/zip.d.ts +0 -7
  396. package/dist/utils/zip.js +0 -42
  397. package/dist/validate/__tests__/validate.test.d.ts +0 -1
  398. package/dist/validate/__tests__/validate.test.js +0 -42
  399. package/dist/validate/freshness.d.ts +0 -28
  400. package/dist/validate/freshness.js +0 -48
  401. package/dist/validate/preview.d.ts +0 -21
  402. package/dist/validate/preview.js +0 -39
  403. package/dist/workspace/resolve.d.ts +0 -19
  404. 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
- });