clawmini 0.0.8 → 0.0.9

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 (367) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +14 -0
  3. package/.github/workflows/release.yml +49 -0
  4. package/CHANGELOG.md +36 -0
  5. package/README.md +5 -4
  6. package/dist/adapter-discord/index.d.mts.map +1 -1
  7. package/dist/adapter-discord/index.mjs +465 -282
  8. package/dist/adapter-discord/index.mjs.map +1 -1
  9. package/dist/adapter-google-chat/index.mjs +367 -243
  10. package/dist/adapter-google-chat/index.mjs.map +1 -1
  11. package/dist/cli/index.mjs +684 -24
  12. package/dist/cli/index.mjs.map +1 -1
  13. package/dist/cli/lite.mjs +43 -13
  14. package/dist/cli/lite.mjs.map +1 -1
  15. package/dist/cli/{propose-policy.mjs → manage-policies.mjs} +270 -47
  16. package/dist/cli/manage-policies.mjs.map +1 -0
  17. package/dist/cli/run-host.d.mts +1 -0
  18. package/dist/cli/run-host.mjs +3090 -0
  19. package/dist/cli/run-host.mjs.map +1 -0
  20. package/dist/config-CPFQIGdG.mjs +57 -0
  21. package/dist/config-CPFQIGdG.mjs.map +1 -0
  22. package/dist/config-Dvl-Pov4.mjs +76 -0
  23. package/dist/config-Dvl-Pov4.mjs.map +1 -0
  24. package/dist/daemon/index.d.mts.map +1 -1
  25. package/dist/daemon/index.mjs +970 -332
  26. package/dist/daemon/index.mjs.map +1 -1
  27. package/dist/supervisor-actions-CiW56eLi.mjs +843 -0
  28. package/dist/supervisor-actions-CiW56eLi.mjs.map +1 -0
  29. package/dist/turn-log-buffer-DRgW53gl.mjs +767 -0
  30. package/dist/turn-log-buffer-DRgW53gl.mjs.map +1 -0
  31. package/dist/web/_app/immutable/chunks/{Drm9vgeP.js → 3AZlWB6U.js} +1 -1
  32. package/dist/web/_app/immutable/chunks/BhRSsUCh.js +2 -0
  33. package/dist/web/_app/immutable/chunks/BiLeM2i1.js +1 -0
  34. package/{web/.svelte-kit/output/client/_app/immutable/chunks/CME08kGM.js → dist/web/_app/immutable/chunks/BmBj85Ll.js} +1 -1
  35. package/dist/web/_app/immutable/chunks/BrERcKAH.js +1 -0
  36. package/dist/web/_app/immutable/chunks/Bv9252RM.js +1 -0
  37. package/dist/web/_app/immutable/chunks/CIXNBPKi.js +1 -0
  38. package/dist/web/_app/immutable/chunks/DISKL3GN.js +2 -0
  39. package/dist/web/_app/immutable/chunks/{Zeh-C-mx.js → DcpaLzmX.js} +1 -1
  40. package/dist/web/_app/immutable/chunks/DnQ3vS13.js +1 -0
  41. package/dist/web/_app/immutable/chunks/KsloHTKS.js +1 -0
  42. package/{web/.svelte-kit/output/client/_app/immutable/chunks/Ck-be5J2.js → dist/web/_app/immutable/chunks/RsHsUj-8.js} +2 -2
  43. package/dist/web/_app/immutable/chunks/{vDehDcuJ.js → wpfV79dV.js} +1 -1
  44. package/dist/web/_app/immutable/entry/app.CIw1Qj0n.js +2 -0
  45. package/dist/web/_app/immutable/entry/start.Di0-Jhte.js +1 -0
  46. package/dist/web/_app/immutable/nodes/{0.CUGC2p-K.js → 0.DYyUA1au.js} +1 -1
  47. package/dist/web/_app/immutable/nodes/1.D-3QEMMZ.js +1 -0
  48. package/dist/web/_app/immutable/nodes/{2.BnwnD1Ki.js → 2.4olHnH7U.js} +1 -1
  49. package/{web/.svelte-kit/output/client/_app/immutable/nodes/3.0arZe_Uf.js → dist/web/_app/immutable/nodes/3.4w0bE-m2.js} +3 -3
  50. package/dist/web/_app/immutable/nodes/4.CZvjhVHt.js +60 -0
  51. package/dist/web/_app/immutable/nodes/{5.Bq2JzCEj.js → 5.DLbPVJY2.js} +1 -1
  52. package/dist/web/_app/version.json +1 -1
  53. package/dist/web/index.html +12 -12
  54. package/dist/workspace-oWmVh5mi.mjs +1001 -0
  55. package/dist/workspace-oWmVh5mi.mjs.map +1 -0
  56. package/docs/23_adapter_slash_autocomplete/development_log.md +19 -0
  57. package/docs/23_adapter_slash_autocomplete/notes.md +18 -0
  58. package/docs/23_adapter_slash_autocomplete/prd.md +46 -0
  59. package/docs/23_adapter_slash_autocomplete/questions.md +6 -0
  60. package/docs/23_adapter_slash_autocomplete/tickets.md +21 -0
  61. package/docs/24_subagent_job_policy_fixes/development_log.md +22 -0
  62. package/docs/24_subagent_job_policy_fixes/notes.md +28 -0
  63. package/docs/24_subagent_job_policy_fixes/prd.md +59 -0
  64. package/docs/24_subagent_job_policy_fixes/questions.md +3 -0
  65. package/docs/24_subagent_job_policy_fixes/tickets.md +49 -0
  66. package/docs/25_e2e_test_improvements/development_log.md +30 -0
  67. package/docs/25_e2e_test_improvements/notes.md +29 -0
  68. package/docs/25_e2e_test_improvements/prd.md +43 -0
  69. package/docs/25_e2e_test_improvements/questions.md +12 -0
  70. package/docs/25_e2e_test_improvements/tickets-2.md +22 -0
  71. package/docs/25_e2e_test_improvements/tickets.md +22 -0
  72. package/docs/25_policy_cwd/development_log.md +30 -0
  73. package/docs/25_policy_cwd/notes.md +28 -0
  74. package/docs/25_policy_cwd/prd.md +77 -0
  75. package/docs/25_policy_cwd/questions.md +6 -0
  76. package/docs/25_policy_cwd/tickets.md +77 -0
  77. package/docs/CLI_REFERENCE.md +3 -1
  78. package/docs/PHILOSOPHY.md +35 -0
  79. package/docs/adapter-visibility/SPEC.md +461 -0
  80. package/docs/adapter-visibility/SPEC_v2.md +202 -0
  81. package/docs/auto-update/SPEC.md +344 -0
  82. package/docs/backups/SPEC.md +296 -0
  83. package/docs/backups/clawmini.gitignore +69 -0
  84. package/docs/guides/assets/clawmini-avatar.png +0 -0
  85. package/docs/guides/backups.md +332 -0
  86. package/docs/guides/discord_adapter_setup.md +1 -1
  87. package/docs/guides/google_chat_adapter_setup.md +81 -0
  88. package/docs/unified-startup/SPEC.md +203 -0
  89. package/e2e/_helpers/test-environment.test.ts +49 -0
  90. package/e2e/_helpers/test-environment.ts +548 -0
  91. package/e2e/adapters/_google-chat-fixtures.ts +340 -0
  92. package/{src/cli/e2e → e2e/adapters}/adapter-discord.test.ts +22 -23
  93. package/e2e/adapters/adapter-google-chat-downtime.test.ts +157 -0
  94. package/e2e/adapters/adapter-google-chat-inbound.test.ts +697 -0
  95. package/e2e/adapters/adapter-google-chat-outbound.test.ts +297 -0
  96. package/e2e/adapters/adapter-google-chat-roundtrip.test.ts +56 -0
  97. package/e2e/adapters/adapter-google-chat-threads.test.ts +1078 -0
  98. package/e2e/agents/custom-api-env.test.ts +80 -0
  99. package/e2e/agents/export-lite-func.test.ts +104 -0
  100. package/e2e/agents/fallbacks.test.ts +124 -0
  101. package/e2e/agents/interrupt.test.ts +50 -0
  102. package/e2e/agents/no-reply-necessary.test.ts +57 -0
  103. package/e2e/agents/session-timeout-subagents.test.ts +76 -0
  104. package/e2e/agents/subagent-authorization.test.ts +246 -0
  105. package/e2e/agents/subagent-env.test.ts +49 -0
  106. package/e2e/agents/subagent-lifecycle.test.ts +782 -0
  107. package/e2e/agents/subagents-depth.test.ts +47 -0
  108. package/e2e/cli/agents.test.ts +176 -0
  109. package/e2e/cli/auto-update.test.ts +741 -0
  110. package/e2e/cli/basic.test.ts +44 -0
  111. package/{src/cli/e2e → e2e/cli}/export-lite.test.ts +16 -12
  112. package/e2e/cli/init-gitignore.test.ts +86 -0
  113. package/e2e/cli/init.test.ts +76 -0
  114. package/e2e/cli/messages.test.ts +363 -0
  115. package/e2e/cli/serve.test.ts +76 -0
  116. package/{src/cli/e2e → e2e/cli}/skills.test.ts +11 -10
  117. package/{src/cli/e2e → e2e/daemon}/daemon.test.ts +57 -195
  118. package/e2e/jobs/agent-jobs.test.ts +216 -0
  119. package/e2e/jobs/cron.test.ts +64 -0
  120. package/e2e/jobs/restart.test.ts +108 -0
  121. package/e2e/policies/approval-session.test.ts +69 -0
  122. package/e2e/policies/auto-create-policies-file.test.ts +35 -0
  123. package/e2e/policies/builtin-manage-policies.test.ts +184 -0
  124. package/e2e/policies/builtin-run-host.test.ts +180 -0
  125. package/e2e/policies/environment-policies.test.ts +177 -0
  126. package/e2e/policies/manage-policies.test.ts +566 -0
  127. package/e2e/policies/output-size.test.ts +98 -0
  128. package/e2e/policies/policies-context-cwd.test.ts +160 -0
  129. package/e2e/policies/relative-script-path.test.ts +60 -0
  130. package/e2e/policies/requests-show.test.ts +135 -0
  131. package/e2e/policies/requests.test.ts +208 -0
  132. package/e2e/policies/slash-policies.test.ts +308 -0
  133. package/e2e/policies/startup-cleanup.test.ts +48 -0
  134. package/e2e/routers/session-timeout.test.ts +106 -0
  135. package/e2e/routers/slash-model.test.ts +152 -0
  136. package/e2e/routers/slash-new.test.ts +50 -0
  137. package/e2e/routers/slash-restart-adapter.test.ts +96 -0
  138. package/e2e/routers/slash-restart.test.ts +114 -0
  139. package/e2e/routers/slash-shutdown.test.ts +55 -0
  140. package/e2e/routers/slash-stop.test.ts +232 -0
  141. package/e2e/routers/slash-upgrade.test.ts +88 -0
  142. package/{src/cli/e2e → e2e/sandbox}/environments.test.ts +14 -13
  143. package/eslint.config.js +6 -0
  144. package/napkin.md +1 -1
  145. package/package.json +8 -3
  146. package/src/adapter-discord/commands.test.ts +42 -0
  147. package/src/adapter-discord/commands.ts +33 -0
  148. package/src/adapter-discord/config.ts +12 -0
  149. package/src/adapter-discord/forwarder.test.ts +499 -21
  150. package/src/adapter-discord/forwarder.ts +343 -124
  151. package/src/adapter-discord/inbound-cache.test.ts +47 -0
  152. package/src/adapter-discord/inbound-cache.ts +37 -0
  153. package/src/adapter-discord/index.test.ts +67 -2
  154. package/src/adapter-discord/index.ts +84 -216
  155. package/src/adapter-discord/interactions.test.ts +54 -3
  156. package/src/adapter-discord/interactions.ts +97 -53
  157. package/src/adapter-discord/processMessage.ts +239 -0
  158. package/src/adapter-discord/state.ts +1 -0
  159. package/src/adapter-google-chat/auth.test.ts +9 -5
  160. package/src/adapter-google-chat/auth.ts +29 -23
  161. package/src/adapter-google-chat/cards.ts +7 -2
  162. package/src/adapter-google-chat/client.test.ts +37 -2
  163. package/src/adapter-google-chat/client.ts +138 -38
  164. package/src/adapter-google-chat/config.ts +19 -0
  165. package/src/adapter-google-chat/forwarder.test.ts +81 -56
  166. package/src/adapter-google-chat/forwarder.ts +394 -185
  167. package/src/adapter-google-chat/inbound-cache.test.ts +61 -0
  168. package/src/adapter-google-chat/inbound-cache.ts +36 -0
  169. package/src/adapter-google-chat/state.test.ts +1 -0
  170. package/src/adapter-google-chat/state.ts +9 -1
  171. package/src/adapter-google-chat/subscriptions.ts +8 -6
  172. package/src/cli/builtin-policies.ts +44 -0
  173. package/src/cli/commands/agents.ts +59 -5
  174. package/src/cli/commands/down.ts +54 -2
  175. package/src/cli/commands/environments.ts +8 -2
  176. package/src/cli/commands/init.ts +31 -0
  177. package/src/cli/commands/logs.ts +116 -0
  178. package/src/cli/commands/policies.ts +6 -4
  179. package/src/cli/commands/serve.test.ts +67 -0
  180. package/src/cli/commands/serve.ts +284 -0
  181. package/src/cli/commands/up.ts +122 -2
  182. package/src/cli/commands/web-api/agents.ts +3 -2
  183. package/src/cli/index.ts +4 -0
  184. package/src/cli/install-detection.test.ts +72 -0
  185. package/src/cli/install-detection.ts +48 -0
  186. package/src/cli/lite.ts +54 -22
  187. package/src/cli/manage-policies-utils.ts +104 -0
  188. package/src/cli/manage-policies.ts +291 -0
  189. package/src/cli/run-host.ts +45 -0
  190. package/src/cli/supervisor-actions.ts +267 -0
  191. package/src/cli/supervisor-control.test.ts +129 -0
  192. package/src/cli/supervisor-control.ts +155 -0
  193. package/src/cli/supervisor-pid.ts +68 -0
  194. package/src/cli/supervisor.ts +277 -0
  195. package/src/daemon/agent/agent-context.ts +11 -11
  196. package/src/daemon/agent/agent-session.ts +8 -1
  197. package/src/daemon/agent/chat-logger.test.ts +78 -9
  198. package/src/daemon/agent/chat-logger.ts +25 -5
  199. package/src/daemon/agent/turn-registry.test.ts +89 -0
  200. package/src/daemon/agent/turn-registry.ts +94 -0
  201. package/src/daemon/agent/types.ts +2 -0
  202. package/src/daemon/api/agent-policy-endpoints.ts +263 -0
  203. package/src/daemon/api/agent-router.ts +47 -126
  204. package/src/daemon/api/index.test.ts +1 -0
  205. package/src/daemon/api/policy-request.test.ts +7 -5
  206. package/src/daemon/api/router-utils.ts +6 -5
  207. package/src/daemon/api/subagent-router.ts +110 -74
  208. package/src/daemon/api/subagent-utils.test.ts +60 -0
  209. package/src/daemon/api/subagent-utils.ts +113 -87
  210. package/src/daemon/api/user-router.ts +34 -8
  211. package/src/daemon/auth.ts +1 -0
  212. package/src/daemon/cron.test.ts +62 -4
  213. package/src/daemon/cron.ts +42 -16
  214. package/src/daemon/events.ts +65 -0
  215. package/src/daemon/index.ts +24 -1
  216. package/src/daemon/message-interruption.test.ts +1 -0
  217. package/src/daemon/message-jobs.test.ts +1 -0
  218. package/src/daemon/message.ts +78 -14
  219. package/src/daemon/observation.test.ts +26 -18
  220. package/src/daemon/pending-replies.test.ts +112 -0
  221. package/src/daemon/pending-replies.ts +162 -0
  222. package/src/daemon/policy-request-service.ts +3 -1
  223. package/src/daemon/policy-utils.test.ts +66 -1
  224. package/src/daemon/policy-utils.ts +126 -1
  225. package/src/daemon/request-store.ts +31 -0
  226. package/src/daemon/routers/session-timeout.ts +4 -0
  227. package/src/daemon/routers/slash-model.test.ts +344 -0
  228. package/src/daemon/routers/slash-model.ts +207 -0
  229. package/src/daemon/routers/slash-policies.test.ts +38 -32
  230. package/src/daemon/routers/slash-policies.ts +84 -33
  231. package/src/daemon/routers/slash-restart.test.ts +69 -0
  232. package/src/daemon/routers/slash-restart.ts +36 -0
  233. package/src/daemon/routers/slash-shutdown.test.ts +50 -0
  234. package/src/daemon/routers/slash-shutdown.ts +28 -0
  235. package/src/daemon/routers/slash-upgrade.test.ts +116 -0
  236. package/src/daemon/routers/slash-upgrade.ts +76 -0
  237. package/src/daemon/routers/types.ts +7 -0
  238. package/src/daemon/routers.ts +16 -0
  239. package/src/shared/adapters/blockquote.test.ts +28 -0
  240. package/src/shared/adapters/blockquote.ts +20 -0
  241. package/src/shared/adapters/filtering.test.ts +224 -10
  242. package/src/shared/adapters/filtering.ts +95 -7
  243. package/src/shared/adapters/inbound-cache.test.ts +48 -0
  244. package/src/shared/adapters/inbound-cache.ts +54 -0
  245. package/src/shared/adapters/turn-log-buffer.ts +266 -0
  246. package/src/shared/adapters/turn-log.test.ts +389 -0
  247. package/src/shared/adapters/turn-log.ts +357 -0
  248. package/src/shared/agent-utils.ts +12 -5
  249. package/src/shared/chats.test.ts +4 -0
  250. package/src/shared/chats.ts +9 -0
  251. package/src/shared/config.ts +16 -1
  252. package/src/shared/lite.ts +76 -2
  253. package/src/shared/policies.ts +26 -0
  254. package/src/shared/template-manifest.ts +267 -0
  255. package/src/shared/utils/shell.ts +61 -0
  256. package/src/shared/version.ts +34 -0
  257. package/src/shared/workspace.test.ts +217 -0
  258. package/src/shared/workspace.ts +626 -48
  259. package/templates/environments/cladding/allowlist-domain.mjs +125 -0
  260. package/templates/environments/cladding/env.json +21 -1
  261. package/templates/environments/cladding/run-with-network.mjs +54 -0
  262. package/templates/environments/macos-proxy/allowlist-domain.mjs +95 -0
  263. package/templates/environments/macos-proxy/env.json +8 -1
  264. package/templates/environments/macos-proxy/proxy.mjs +0 -1
  265. package/templates/gemini/template.json +5 -0
  266. package/templates/gemini-claw/template.json +13 -0
  267. package/templates/skills/clawmini-requests/SKILL.md +69 -10
  268. package/templates/skills/run-host/SKILL.md +51 -0
  269. package/templates/skills/skill-creator/SKILL.md +4 -3
  270. package/templates/skills/skill-creator/scripts/validate.sh +52 -0
  271. package/tsdown.config.ts +10 -1
  272. package/vitest.config.ts +2 -2
  273. package/web/.svelte-kit/ambient.d.ts +292 -118
  274. package/web/.svelte-kit/generated/server/internal.js +1 -1
  275. package/web/.svelte-kit/output/client/.vite/manifest.json +126 -136
  276. package/web/.svelte-kit/output/client/_app/immutable/chunks/{Drm9vgeP.js → 3AZlWB6U.js} +1 -1
  277. package/web/.svelte-kit/output/client/_app/immutable/chunks/BhRSsUCh.js +2 -0
  278. package/web/.svelte-kit/output/client/_app/immutable/chunks/BiLeM2i1.js +1 -0
  279. package/{dist/web/_app/immutable/chunks/CME08kGM.js → web/.svelte-kit/output/client/_app/immutable/chunks/BmBj85Ll.js} +1 -1
  280. package/web/.svelte-kit/output/client/_app/immutable/chunks/BrERcKAH.js +1 -0
  281. package/web/.svelte-kit/output/client/_app/immutable/chunks/Bv9252RM.js +1 -0
  282. package/web/.svelte-kit/output/client/_app/immutable/chunks/CIXNBPKi.js +1 -0
  283. package/web/.svelte-kit/output/client/_app/immutable/chunks/DISKL3GN.js +2 -0
  284. package/web/.svelte-kit/output/client/_app/immutable/chunks/{Zeh-C-mx.js → DcpaLzmX.js} +1 -1
  285. package/web/.svelte-kit/output/client/_app/immutable/chunks/DnQ3vS13.js +1 -0
  286. package/web/.svelte-kit/output/client/_app/immutable/chunks/KsloHTKS.js +1 -0
  287. package/{dist/web/_app/immutable/chunks/Ck-be5J2.js → web/.svelte-kit/output/client/_app/immutable/chunks/RsHsUj-8.js} +2 -2
  288. package/web/.svelte-kit/output/client/_app/immutable/chunks/{vDehDcuJ.js → wpfV79dV.js} +1 -1
  289. package/web/.svelte-kit/output/client/_app/immutable/entry/app.CIw1Qj0n.js +2 -0
  290. package/web/.svelte-kit/output/client/_app/immutable/entry/start.Di0-Jhte.js +1 -0
  291. package/web/.svelte-kit/output/client/_app/immutable/nodes/{0.CUGC2p-K.js → 0.DYyUA1au.js} +1 -1
  292. package/web/.svelte-kit/output/client/_app/immutable/nodes/1.D-3QEMMZ.js +1 -0
  293. package/web/.svelte-kit/output/client/_app/immutable/nodes/{2.BnwnD1Ki.js → 2.4olHnH7U.js} +1 -1
  294. package/{dist/web/_app/immutable/nodes/3.0arZe_Uf.js → web/.svelte-kit/output/client/_app/immutable/nodes/3.4w0bE-m2.js} +3 -3
  295. package/web/.svelte-kit/output/client/_app/immutable/nodes/4.CZvjhVHt.js +60 -0
  296. package/web/.svelte-kit/output/client/_app/immutable/nodes/{5.Bq2JzCEj.js → 5.DLbPVJY2.js} +1 -1
  297. package/web/.svelte-kit/output/client/_app/version.json +1 -1
  298. package/web/.svelte-kit/output/server/.vite/manifest.json +12 -10
  299. package/web/.svelte-kit/output/server/chunks/Icon.js +1 -1
  300. package/web/.svelte-kit/output/server/chunks/client.js +1 -1
  301. package/web/.svelte-kit/output/server/chunks/exports.js +1 -1
  302. package/web/.svelte-kit/output/server/chunks/index-server.js +2 -1
  303. package/web/.svelte-kit/output/server/chunks/internal.js +1 -1
  304. package/web/.svelte-kit/output/server/chunks/render-context.js +77 -0
  305. package/web/.svelte-kit/output/server/chunks/root.js +739 -788
  306. package/web/.svelte-kit/output/server/chunks/shared.js +234 -21
  307. package/web/.svelte-kit/output/server/index.js +126 -90
  308. package/web/.svelte-kit/output/server/manifest-full.js +1 -1
  309. package/web/.svelte-kit/output/server/manifest.js +1 -1
  310. package/web/.svelte-kit/output/server/nodes/0.js +1 -1
  311. package/web/.svelte-kit/output/server/nodes/1.js +1 -1
  312. package/web/.svelte-kit/output/server/nodes/2.js +1 -1
  313. package/web/.svelte-kit/output/server/nodes/3.js +1 -1
  314. package/web/.svelte-kit/output/server/nodes/4.js +1 -1
  315. package/web/.svelte-kit/output/server/nodes/5.js +1 -1
  316. package/web/.svelte-kit/output/server/remote-entry.js +245 -81
  317. package/web/.svelte-kit/tsconfig.json +4 -1
  318. package/dist/cli/propose-policy.mjs.map +0 -1
  319. package/dist/lite-CBxOT1y5.mjs +0 -241
  320. package/dist/lite-CBxOT1y5.mjs.map +0 -1
  321. package/dist/routing-D8rTxtaV.mjs +0 -245
  322. package/dist/routing-D8rTxtaV.mjs.map +0 -1
  323. package/dist/web/_app/immutable/chunks/B6YN0Nuq.js +0 -1
  324. package/dist/web/_app/immutable/chunks/BmRlVmv6.js +0 -1
  325. package/dist/web/_app/immutable/chunks/CK9JZLaG.js +0 -2
  326. package/dist/web/_app/immutable/chunks/Ck3rYNON.js +0 -1
  327. package/dist/web/_app/immutable/chunks/D5iV40bG.js +0 -1
  328. package/dist/web/_app/immutable/chunks/DMtIqaiV.js +0 -2
  329. package/dist/web/_app/immutable/chunks/DhD271EB.js +0 -1
  330. package/dist/web/_app/immutable/chunks/DpuLqk8d.js +0 -1
  331. package/dist/web/_app/immutable/chunks/DsIToJCP.js +0 -1
  332. package/dist/web/_app/immutable/entry/app.BCSV3nrG.js +0 -2
  333. package/dist/web/_app/immutable/entry/start.D4eLEZUM.js +0 -1
  334. package/dist/web/_app/immutable/nodes/1.CGC_42IQ.js +0 -1
  335. package/dist/web/_app/immutable/nodes/4.ClM1bXLE.js +0 -60
  336. package/dist/workspace-BJmJBfKi.mjs +0 -456
  337. package/dist/workspace-BJmJBfKi.mjs.map +0 -1
  338. package/src/cli/e2e/agents.test.ts +0 -140
  339. package/src/cli/e2e/basic.test.ts +0 -43
  340. package/src/cli/e2e/cron.test.ts +0 -132
  341. package/src/cli/e2e/export-lite-func.test.ts +0 -206
  342. package/src/cli/e2e/fallbacks.test.ts +0 -175
  343. package/src/cli/e2e/init.test.ts +0 -77
  344. package/src/cli/e2e/messages.test.ts +0 -332
  345. package/src/cli/e2e/propose-policy.test.ts +0 -203
  346. package/src/cli/e2e/requests.test.ts +0 -180
  347. package/src/cli/e2e/session-timeout.test.ts +0 -192
  348. package/src/cli/e2e/slash-new.test.ts +0 -93
  349. package/src/cli/e2e/subagents.test.ts +0 -106
  350. package/src/cli/e2e/utils.ts +0 -66
  351. package/src/cli/propose-policy.ts +0 -91
  352. package/web/.svelte-kit/output/client/_app/immutable/chunks/B6YN0Nuq.js +0 -1
  353. package/web/.svelte-kit/output/client/_app/immutable/chunks/BmRlVmv6.js +0 -1
  354. package/web/.svelte-kit/output/client/_app/immutable/chunks/CK9JZLaG.js +0 -2
  355. package/web/.svelte-kit/output/client/_app/immutable/chunks/Ck3rYNON.js +0 -1
  356. package/web/.svelte-kit/output/client/_app/immutable/chunks/D5iV40bG.js +0 -1
  357. package/web/.svelte-kit/output/client/_app/immutable/chunks/DMtIqaiV.js +0 -2
  358. package/web/.svelte-kit/output/client/_app/immutable/chunks/DhD271EB.js +0 -1
  359. package/web/.svelte-kit/output/client/_app/immutable/chunks/DpuLqk8d.js +0 -1
  360. package/web/.svelte-kit/output/client/_app/immutable/chunks/DsIToJCP.js +0 -1
  361. package/web/.svelte-kit/output/client/_app/immutable/entry/app.BCSV3nrG.js +0 -2
  362. package/web/.svelte-kit/output/client/_app/immutable/entry/start.D4eLEZUM.js +0 -1
  363. package/web/.svelte-kit/output/client/_app/immutable/nodes/1.CGC_42IQ.js +0 -1
  364. package/web/.svelte-kit/output/client/_app/immutable/nodes/4.ClM1bXLE.js +0 -60
  365. package/web/.svelte-kit/output/server/chunks/false.js +0 -4
  366. /package/dist/cli/{propose-policy.d.mts → manage-policies.d.mts} +0 -0
  367. /package/{src/cli/e2e → e2e/_helpers}/global-setup.ts +0 -0
@@ -0,0 +1,548 @@
1
+ /* eslint-disable max-lines */
2
+ import { spawn, execSync } from 'node:child_process';
3
+ import path from 'node:path';
4
+ import fs from 'node:fs';
5
+ import os from 'node:os';
6
+ import net from 'node:net';
7
+ import { createTRPCClient, httpLink, splitLink, httpSubscriptionLink } from '@trpc/client';
8
+ import type { UserRouter as AppRouter } from '../../src/daemon/api/index.js';
9
+ import { createUnixSocketFetch } from '../../src/shared/fetch.js';
10
+ import { createUnixSocketEventSource } from '../../src/shared/event-source.js';
11
+ import type {
12
+ ChatMessage,
13
+ CommandLogMessage,
14
+ AgentReplyMessage,
15
+ PolicyRequestMessage,
16
+ SystemMessage,
17
+ ToolMessage,
18
+ } from '../../src/daemon/chats.js';
19
+
20
+ export type {
21
+ ChatMessage,
22
+ CommandLogMessage,
23
+ AgentReplyMessage,
24
+ PolicyRequestMessage,
25
+ SystemMessage,
26
+ ToolMessage,
27
+ };
28
+
29
+ export function commandWith(
30
+ text: string
31
+ ): (msg: ChatMessage) => msg is CommandLogMessage {
32
+ return (msg): msg is CommandLogMessage =>
33
+ msg.role === 'command' && msg.stdout.includes(text);
34
+ }
35
+
36
+ export function commandMatching(
37
+ predicate: (msg: CommandLogMessage) => boolean
38
+ ): (msg: ChatMessage) => msg is CommandLogMessage {
39
+ return (msg): msg is CommandLogMessage => msg.role === 'command' && predicate(msg);
40
+ }
41
+
42
+ export function agentReply(): (msg: ChatMessage) => msg is AgentReplyMessage {
43
+ return (msg): msg is AgentReplyMessage => msg.role === 'agent';
44
+ }
45
+
46
+ export function agentReplyWith(
47
+ text: string
48
+ ): (msg: ChatMessage) => msg is AgentReplyMessage {
49
+ return (msg): msg is AgentReplyMessage =>
50
+ msg.role === 'agent' && msg.content === text;
51
+ }
52
+
53
+ export function policyWith(
54
+ status?: PolicyRequestMessage['status']
55
+ ): (msg: ChatMessage) => msg is PolicyRequestMessage {
56
+ return (msg): msg is PolicyRequestMessage =>
57
+ msg.role === 'policy' && (status === undefined || (msg as PolicyRequestMessage).status === status);
58
+ }
59
+
60
+ export async function findFreePort(): Promise<number> {
61
+ return new Promise((resolve, reject) => {
62
+ const srv = net.createServer();
63
+ srv.on('error', reject);
64
+ srv.listen(0, '127.0.0.1', () => {
65
+ const address = srv.address();
66
+ if (!address || typeof address === 'string') {
67
+ srv.close();
68
+ reject(new Error('Failed to get free port'));
69
+ return;
70
+ }
71
+ const port = address.port;
72
+ srv.close(() => resolve(port));
73
+ });
74
+ });
75
+ }
76
+
77
+ export interface ChatSubscription {
78
+ messageBuffer: ChatMessage[];
79
+ waitForMessage<T extends ChatMessage>(
80
+ predicate: (msg: ChatMessage) => msg is T,
81
+ timeoutMs?: number
82
+ ): Promise<T>;
83
+ waitForMessage(predicate: (msg: ChatMessage) => boolean, timeoutMs?: number): Promise<ChatMessage>;
84
+ disconnect(): Promise<void>;
85
+ }
86
+
87
+ export class TestEnvironment {
88
+ public e2eDir: string;
89
+ public binPath: string;
90
+ public id: string;
91
+ public trpcClient: ReturnType<typeof createTRPCClient<AppRouter>> | null = null;
92
+ private openSubscriptions: Set<ChatSubscription> = new Set();
93
+ private credentials: { url: string; token: string } | null = null;
94
+
95
+ constructor(prefix: string) {
96
+ this.id = `${prefix}-${Date.now()}-${Math.floor(Math.random() * 1000)}`;
97
+ this.e2eDir = path.join(os.homedir(), '.gemini', 'tmp', `clawmini-${this.id}`);
98
+ this.binPath = path.resolve(__dirname, '../../dist/cli/index.mjs');
99
+ }
100
+
101
+ public getClawminiPath(...parts: string[]): string {
102
+ return path.resolve(this.e2eDir, '.clawmini', ...parts);
103
+ }
104
+
105
+ public getChatPath(chatId: string, ...parts: string[]): string {
106
+ return this.getClawminiPath('chats', chatId, ...parts);
107
+ }
108
+
109
+ public getAgentPath(agentId: string, ...parts: string[]): string {
110
+ return this.getClawminiPath('agents', agentId, ...parts);
111
+ }
112
+
113
+ public runCli(args: string[]): Promise<{ stdout: string; stderr: string; code: number | null }> {
114
+ const isInit = args[0] === 'init';
115
+ return new Promise((resolve) => {
116
+ const child = spawn('node', [this.binPath, ...args], {
117
+ cwd: this.e2eDir,
118
+ env: { ...process.env },
119
+ });
120
+
121
+ let stdout = '';
122
+ let stderr = '';
123
+
124
+ child.stdout.on('data', (data) => {
125
+ stdout += data.toString();
126
+ });
127
+
128
+ child.stderr.on('data', (data) => {
129
+ stderr += data.toString();
130
+ });
131
+
132
+ child.on('close', (code) => {
133
+ if (isInit && code === 0) {
134
+ // Update settings to set API port to 0, assigning a random available port
135
+ const settingsPath = this.getClawminiPath('settings.json');
136
+ if (fs.existsSync(settingsPath)) {
137
+ try {
138
+ const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
139
+ settings.api = { port: 0 }; // Use random available port to avoid EADDRINUSE during parallel e2e tests
140
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
141
+ } catch {
142
+ // ignore
143
+ }
144
+ }
145
+ }
146
+ resolve({ stdout, stderr, code });
147
+ });
148
+ });
149
+ }
150
+
151
+ public async connect(
152
+ chatId: string = 'default',
153
+ opts: { lastMessageId?: string } = {}
154
+ ): Promise<ChatSubscription> {
155
+ await this.ensureTrpcClient();
156
+
157
+ const messageBuffer: ChatMessage[] = [];
158
+ type Waiter = {
159
+ predicate: (msg: ChatMessage) => boolean;
160
+ resolve: (value: ChatMessage | PromiseLike<ChatMessage>) => void;
161
+ };
162
+ const waiters: Waiter[] = [];
163
+
164
+ const sub = this.trpcClient!.waitForMessages.subscribe(
165
+ { chatId, ...(opts.lastMessageId ? { lastMessageId: opts.lastMessageId } : {}) },
166
+ {
167
+ onData: (items) => {
168
+ for (const item of items) {
169
+ // Skip turn lifecycle envelopes; tests observe messages only.
170
+ if (item.kind !== 'message') continue;
171
+ const msg = item.message as ChatMessage;
172
+ messageBuffer.push(msg);
173
+ for (let i = waiters.length - 1; i >= 0; i--) {
174
+ const waiter = waiters[i]!;
175
+ if (waiter.predicate(msg)) {
176
+ waiter.resolve(msg);
177
+ waiters.splice(i, 1);
178
+ }
179
+ }
180
+ }
181
+ },
182
+ onError: (err) => {
183
+ console.error('Subscription error:', err);
184
+ },
185
+ }
186
+ );
187
+
188
+ const handle: ChatSubscription = {
189
+ messageBuffer,
190
+ waitForMessage: (
191
+ predicate: (msg: ChatMessage) => boolean,
192
+ timeoutMs: number = 15000
193
+ ): Promise<ChatMessage> => {
194
+ const existing = messageBuffer.find(predicate);
195
+ if (existing) return Promise.resolve(existing);
196
+
197
+ return new Promise<ChatMessage>((resolve, reject) => {
198
+ const waiter = { predicate, resolve };
199
+ waiters.push(waiter);
200
+
201
+ const timer = setTimeout(() => {
202
+ const idx = waiters.indexOf(waiter);
203
+ if (idx !== -1) {
204
+ waiters.splice(idx, 1);
205
+ reject(new Error(`waitForMessage timed out after ${timeoutMs}ms`));
206
+ }
207
+ }, timeoutMs);
208
+
209
+ const origResolve = waiter.resolve;
210
+ waiter.resolve = (value) => {
211
+ clearTimeout(timer);
212
+ origResolve(value);
213
+ };
214
+ });
215
+ },
216
+ disconnect: async () => {
217
+ sub.unsubscribe();
218
+ this.openSubscriptions.delete(handle);
219
+ },
220
+ };
221
+
222
+ this.openSubscriptions.add(handle);
223
+ return handle;
224
+ }
225
+
226
+ private async ensureTrpcClient() {
227
+ if (this.trpcClient) return;
228
+
229
+ const socketPath = this.getClawminiPath('daemon.sock');
230
+ for (let i = 0; i < 50; i++) {
231
+ if (fs.existsSync(socketPath)) break;
232
+ await new Promise((r) => setTimeout(r, 100));
233
+ }
234
+ if (!fs.existsSync(socketPath)) {
235
+ throw new Error(`Daemon socket not found at ${socketPath}`);
236
+ }
237
+
238
+ const customFetch = createUnixSocketFetch(socketPath);
239
+ const CustomEventSource = createUnixSocketEventSource(socketPath);
240
+
241
+ this.trpcClient = createTRPCClient<AppRouter>({
242
+ links: [
243
+ splitLink({
244
+ condition(op) {
245
+ return op.type === 'subscription';
246
+ },
247
+ true: httpSubscriptionLink({
248
+ url: 'http://localhost',
249
+ EventSource: CustomEventSource as unknown as typeof EventSource,
250
+ }),
251
+ false: httpLink({
252
+ url: 'http://localhost',
253
+ fetch: customFetch,
254
+ }),
255
+ }),
256
+ ],
257
+ });
258
+ }
259
+
260
+ public async setup() {
261
+ if (fs.existsSync(this.e2eDir)) {
262
+ fs.rmSync(this.e2eDir, { recursive: true, force: true });
263
+ }
264
+ fs.mkdirSync(this.e2eDir, { recursive: true });
265
+ execSync('git init', { cwd: this.e2eDir, stdio: 'ignore' });
266
+ }
267
+
268
+ public async disconnectAll() {
269
+ for (const sub of [...this.openSubscriptions]) {
270
+ await sub.disconnect();
271
+ }
272
+ }
273
+
274
+ public async teardown() {
275
+ await this.disconnectAll();
276
+ this.trpcClient = null;
277
+ if (fs.existsSync(this.e2eDir)) {
278
+ await this.runCli(['down']);
279
+ if (fs.existsSync(this.e2eDir)) {
280
+ fs.rmSync(this.e2eDir, { recursive: true, force: true });
281
+ }
282
+ }
283
+ }
284
+
285
+ public async init() {
286
+ return this.runCli(['init']);
287
+ }
288
+
289
+ public async up() {
290
+ return this.runCli(['up']);
291
+ }
292
+
293
+ public async down() {
294
+ return this.runCli(['down']);
295
+ }
296
+
297
+ public async addAgent(name: string, options: { template?: string } = {}) {
298
+ const args = ['agents', 'add', name];
299
+ if (options.template) {
300
+ args.push('--template', options.template);
301
+ }
302
+ return this.runCli(args);
303
+ }
304
+
305
+ public async sendMessage(
306
+ content: string,
307
+ opts: { chat?: string; agent?: string; file?: string; noWait?: boolean } = {}
308
+ ) {
309
+ const args = ['messages', 'send', content];
310
+ if (opts.chat) args.push('--chat', opts.chat);
311
+ if (opts.agent) args.push('--agent', opts.agent);
312
+ if (opts.file) args.push('--file', opts.file);
313
+ if (opts.noWait) args.push('--no-wait');
314
+ return this.runCli(args);
315
+ }
316
+
317
+ public async addChat(id: string, agentName?: string) {
318
+ const result = await this.runCli(['chats', 'add', id]);
319
+ if (agentName) {
320
+ this.writeChatSettings(id, { defaultAgent: agentName });
321
+ }
322
+ return result;
323
+ }
324
+
325
+ public writeChatSettings(chatId: string, settings: Record<string, unknown>) {
326
+ const chatSettingsPath = this.getChatPath(chatId, 'settings.json');
327
+ const chatSettingsDir = path.dirname(chatSettingsPath);
328
+ if (!fs.existsSync(chatSettingsDir)) {
329
+ fs.mkdirSync(chatSettingsDir, { recursive: true });
330
+ }
331
+ fs.writeFileSync(chatSettingsPath, JSON.stringify(settings, null, 2));
332
+ }
333
+
334
+ public getSettings(): Record<string, unknown> {
335
+ const settingsPath = this.getClawminiPath('settings.json');
336
+ if (fs.existsSync(settingsPath)) {
337
+ return JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
338
+ }
339
+ return {};
340
+ }
341
+
342
+ public writeSettings(settings: Record<string, unknown>) {
343
+ const settingsPath = this.getClawminiPath('settings.json');
344
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
345
+ }
346
+
347
+ public getAgentSettings(agentId: string): Record<string, unknown> {
348
+ const agentSettingsPath = this.getAgentPath(agentId, 'settings.json');
349
+ if (fs.existsSync(agentSettingsPath)) {
350
+ return JSON.parse(fs.readFileSync(agentSettingsPath, 'utf8'));
351
+ }
352
+ return {};
353
+ }
354
+
355
+ public writeAgentSettings(agentId: string, settings: Record<string, unknown>) {
356
+ const agentSettingsPath = this.getAgentPath(agentId, 'settings.json');
357
+ const agentSettingsDir = path.dirname(agentSettingsPath);
358
+ if (!fs.existsSync(agentSettingsDir)) {
359
+ fs.mkdirSync(agentSettingsDir, { recursive: true });
360
+ }
361
+ fs.writeFileSync(agentSettingsPath, JSON.stringify(settings, null, 2));
362
+ }
363
+
364
+ public updateAgentSettings(agentId: string, updates: Record<string, unknown>) {
365
+ const settings = this.getAgentSettings(agentId);
366
+ this.writeAgentSettings(agentId, deepMerge(settings, updates));
367
+ }
368
+
369
+ public getChatSettings(chatId: string): Record<string, unknown> {
370
+ const chatSettingsPath = this.getChatPath(chatId, 'settings.json');
371
+ if (fs.existsSync(chatSettingsPath)) {
372
+ return JSON.parse(fs.readFileSync(chatSettingsPath, 'utf8'));
373
+ }
374
+ return {};
375
+ }
376
+
377
+ public getSessionSettings(agentId: string, sessionId: string): Record<string, unknown> {
378
+ const sessionSettingsPath = this.getAgentPath(
379
+ agentId,
380
+ 'sessions',
381
+ sessionId,
382
+ 'settings.json'
383
+ );
384
+ if (fs.existsSync(sessionSettingsPath)) {
385
+ return JSON.parse(fs.readFileSync(sessionSettingsPath, 'utf8'));
386
+ }
387
+ return {};
388
+ }
389
+
390
+ public setDefaultAgent(defaultAgent: unknown) {
391
+ this.writeSettings({ ...this.getSettings(), defaultAgent });
392
+ }
393
+
394
+ public updateSettings(updates: Record<string, unknown>) {
395
+ const settingsPath = this.getClawminiPath('settings.json');
396
+ let settings: Record<string, unknown> = {};
397
+ if (fs.existsSync(settingsPath)) {
398
+ settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
399
+ }
400
+ settings = deepMerge(settings, updates);
401
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
402
+ }
403
+
404
+ // Extracts CLAW_API_URL/CLAW_API_TOKEN from a debug-agent session. Requires
405
+ // setupSubagentEnv() to have run (debug-agent + running daemon + exported
406
+ // lite). Result is cached for the lifetime of the TestEnvironment.
407
+ public async getAgentCredentials(): Promise<{ url: string; token: string }> {
408
+ if (this.credentials) return this.credentials;
409
+
410
+ const chatId = '__creds__';
411
+ await this.runCli(['chats', 'add', chatId]);
412
+ const chat = await this.connect(chatId);
413
+ try {
414
+ await this.sendMessage('echo "URL=$CLAW_API_URL" && echo "TOKEN=$CLAW_API_TOKEN"', {
415
+ chat: chatId,
416
+ agent: 'debug-agent',
417
+ });
418
+ const log = await chat.waitForMessage((m): m is CommandLogMessage => m.role === 'command');
419
+ // Match start-of-line to skip the debug template's own [DEBUG] ... echo
420
+ // line, which contains the literal text "URL=$CLAW_API_URL".
421
+ const url = log.stdout.match(/^URL=(.+)$/m)![1]!.trim();
422
+ const token = log.stdout.match(/^TOKEN=(.+)$/m)![1]!.trim();
423
+ this.credentials = { url, token };
424
+ return this.credentials;
425
+ } finally {
426
+ await chat.disconnect();
427
+ }
428
+ }
429
+
430
+ // Spawns the exported clawmini-lite.js with CLAW_API_URL/CLAW_API_TOKEN
431
+ // populated from the debug-agent. Fetches credentials lazily on first call.
432
+ public async runLite(
433
+ args: string[],
434
+ opts: { cwd?: string; env?: Record<string, string> } = {}
435
+ ): Promise<{ stdout: string; stderr: string; code: number | null }> {
436
+ const { url, token } = await this.getAgentCredentials();
437
+ const litePath = path.resolve(this.e2eDir, 'clawmini-lite.js');
438
+ return new Promise((resolve) => {
439
+ const p = spawn('node', [litePath, ...args], {
440
+ env: {
441
+ ...process.env,
442
+ CLAW_API_URL: url,
443
+ CLAW_API_TOKEN: token,
444
+ ...opts.env,
445
+ },
446
+ cwd: opts.cwd ?? this.e2eDir,
447
+ });
448
+ let stdout = '';
449
+ let stderr = '';
450
+ p.stdout.on('data', (d) => (stdout += d.toString()));
451
+ p.stderr.on('data', (d) => (stderr += d.toString()));
452
+ p.on('close', (code) => resolve({ stdout, stderr, code }));
453
+ });
454
+ }
455
+
456
+ public writePolicies(policies: unknown) {
457
+ const policiesPath = this.getClawminiPath('policies.json');
458
+ const policiesDir = path.dirname(policiesPath);
459
+ if (!fs.existsSync(policiesDir)) {
460
+ fs.mkdirSync(policiesDir, { recursive: true });
461
+ }
462
+ fs.writeFileSync(policiesPath, JSON.stringify({ policies }, null, 2));
463
+ }
464
+
465
+ public async setupSubagentEnv(
466
+ options: { port?: number; routers?: unknown[]; policies?: unknown } = {}
467
+ ) {
468
+ await this.init();
469
+ await this.addAgent('debug-agent', { template: 'debug' });
470
+
471
+ // Lite-based subagents need a real reachable HTTP port. init() stores
472
+ // port: 0 for daemon socket tests, but that produces CLAW_API_URL=
473
+ // http://127.0.0.1:0 which is unreachable. Pick a free port instead.
474
+ const port = options.port ?? (await findFreePort());
475
+ const settingsUpdates: Record<string, unknown> = { api: { host: '127.0.0.1', port } };
476
+ if (options.routers) settingsUpdates.routers = options.routers;
477
+ this.updateSettings(settingsUpdates);
478
+
479
+ if (options.policies) {
480
+ this.writePolicies(options.policies);
481
+ }
482
+
483
+ await this.up();
484
+
485
+ const litePath = path.resolve(this.e2eDir, 'clawmini-lite.js');
486
+ await this.runCli(['export-lite', '--out', litePath]);
487
+ fs.chmodSync(litePath, '755');
488
+
489
+ const binDir = path.resolve(this.e2eDir, 'bin');
490
+ fs.mkdirSync(binDir);
491
+ fs.symlinkSync(litePath, path.join(binDir, 'clawmini-lite.js'));
492
+
493
+ if (fs.existsSync(this.getAgentPath('debug-agent', 'settings.json'))) {
494
+ this.updateAgentSettings('debug-agent', {
495
+ env: { PATH: `${binDir}:${process.env.PATH}` },
496
+ });
497
+ }
498
+ }
499
+
500
+ public runBin(
501
+ binPath: string,
502
+ args: string[]
503
+ ): Promise<{ stdout: string; stderr: string; code: number | null }> {
504
+ return new Promise((resolve) => {
505
+ const child = spawn('node', [binPath, ...args], {
506
+ cwd: this.e2eDir,
507
+ env: { ...process.env },
508
+ });
509
+
510
+ let stdout = '';
511
+ let stderr = '';
512
+
513
+ child.stdout.on('data', (data: Buffer) => (stdout += data.toString()));
514
+ child.stderr.on('data', (data: Buffer) => (stderr += data.toString()));
515
+
516
+ child.on('close', (code) => {
517
+ resolve({ stdout, stderr, code });
518
+ });
519
+ });
520
+ }
521
+ }
522
+
523
+ function deepMerge(
524
+ target: Record<string, unknown>,
525
+ source: Record<string, unknown>
526
+ ): Record<string, unknown> {
527
+ const result = { ...target };
528
+ for (const key of Object.keys(source)) {
529
+ const srcVal = source[key];
530
+ const tgtVal = result[key];
531
+ if (
532
+ srcVal !== null &&
533
+ typeof srcVal === 'object' &&
534
+ !Array.isArray(srcVal) &&
535
+ tgtVal !== null &&
536
+ typeof tgtVal === 'object' &&
537
+ !Array.isArray(tgtVal)
538
+ ) {
539
+ result[key] = deepMerge(
540
+ tgtVal as Record<string, unknown>,
541
+ srcVal as Record<string, unknown>
542
+ );
543
+ } else {
544
+ result[key] = srcVal;
545
+ }
546
+ }
547
+ return result;
548
+ }