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
@@ -1,25 +1,35 @@
1
1
  #!/usr/bin/env node
2
- import { D as pathIsInsideDir, E as writeSettings, T as writeChatSettings, a as enableEnvironment, c as getClawminiDir, d as getSocketPath, g as readChatSettings, i as deleteAgent, m as listAgents, n as copyAgentSkill, p as isValidAgentId, r as copyAgentSkills, s as getAgent, t as applyTemplateToAgent, w as writeAgentSettings, x as resolveSkillsTemplatePath, y as readSettings } from "../workspace-BJmJBfKi.mjs";
3
- import { a as writeLiteScript, c as createChat, d as getChatsDir, f as getDefaultChatId, g as setDefaultChatId, h as listChats, i as resolveCompiledScript, l as deleteChat, m as isValidChatId, o as DEFAULT_CHAT_ID, p as getMessages, r as getLiteScriptContent, t as exportLiteToAllEnvironments } from "../lite-CBxOT1y5.mjs";
2
+ import { C as readSettings, D as resolveSkillsTemplatePath, F as pathIsInsideDir, M as writeAgentSettings, N as writeChatSettings, P as writeSettings, R as BUILTIN_POLICIES, T as refreshAgentTemplate, _ as isValidAgentId, a as enableEnvironment, b as readChatSettings, c as getAgent, h as getSocketPath, i as deleteAgent, l as getAgentOverlay, n as copyAgentSkill, o as formatPlanActions, p as getPoliciesPath, r as copyAgentSkills, t as applyTemplateToAgent, u as getClawminiDir, v as listAgents, w as refreshAgentSkills } from "../workspace-oWmVh5mi.mjs";
3
+ import { A as getMessages, C as writeLiteScript, E as deleteChat, M as listChats, N as setDefaultChatId, O as getChatsDir, S as resolveCompiledScript, T as createChat, _ as removeSupervisorPid, f as getControlSocketPath, g as readSupervisorPid, h as getSupervisorPidPath, j as isValidChatId, k as getDefaultChatId, n as startSupervisorControl, v as writeSupervisorPid, w as DEFAULT_CHAT_ID, x as getLiteScriptContent, y as exportLiteToAllEnvironments } from "../supervisor-actions-CiW56eLi.mjs";
4
4
  import { t as createUnixSocketFetch } from "../fetch-Cn1XNyiO.mjs";
5
+ import { t as getDiscordConfigPath } from "../config-CPFQIGdG.mjs";
6
+ import { t as getGoogleChatConfigPath } from "../config-Dvl-Pov4.mjs";
5
7
  import { Command } from "commander";
6
8
  import fs from "node:fs";
7
9
  import path from "node:path";
8
- import { spawn } from "node:child_process";
9
- import * as fs$2 from "node:fs/promises";
10
10
  import { fileURLToPath } from "node:url";
11
+ import { spawn } from "node:child_process";
12
+ import { createHash } from "node:crypto";
13
+ import * as fs$1 from "node:fs/promises";
14
+ import fsPromises from "node:fs/promises";
11
15
  import { z } from "zod";
12
16
  import { createTRPCClient, httpLink } from "@trpc/client";
13
17
  import http from "node:http";
18
+ import net from "node:net";
19
+ import readline from "node:readline";
14
20
  import { promises } from "fs";
15
21
 
16
22
  //#region src/shared/agent-utils.ts
17
- async function createAgentWithChat(agentId, agentData, template, startDir = process.cwd()) {
23
+ async function createAgentWithChat(agentId, agentData, template, startDir = process.cwd(), opts = {}) {
18
24
  await writeAgentSettings(agentId, agentData, startDir);
19
- if (template) await applyTemplateToAgent(agentId, template, agentData, startDir);
25
+ if (template) await applyTemplateToAgent(agentId, template, agentData, startDir, opts);
20
26
  try {
21
- await copyAgentSkills(agentId, startDir);
22
- console.log(`Copied skills to agent ${agentId}.`);
27
+ const resolved = await getAgent(agentId, startDir);
28
+ if (resolved?.skillsDir === null) console.log(`Skipping skills for agent ${agentId} (skillsDir is null).`);
29
+ else if (resolved) {
30
+ await refreshAgentSkills(agentId, resolved, startDir, { firstInstall: true });
31
+ console.log(`Installed skills for agent ${agentId}.`);
32
+ }
23
33
  } catch (err) {
24
34
  console.warn(`Warning: Failed to copy skills to agent ${agentId}: ${err instanceof Error ? err.message : String(err)}`);
25
35
  }
@@ -40,8 +50,43 @@ function handleError$1(action, err) {
40
50
  process.exit(1);
41
51
  }
42
52
 
53
+ //#endregion
54
+ //#region src/cli/builtin-policies.ts
55
+ const HASHBANG = "#!/usr/bin/env node\n";
56
+ function sha256(content) {
57
+ return createHash("sha256").update(content).digest("hex");
58
+ }
59
+ async function installBuiltinPolicies(dirPath = getClawminiDir()) {
60
+ const policyScriptsDir = path.join(dirPath, "policy-scripts");
61
+ await fsPromises.mkdir(policyScriptsDir, { recursive: true });
62
+ for (const name of Object.keys(BUILTIN_POLICIES)) try {
63
+ const sourcePath = await resolveCompiledScript(name, import.meta.url);
64
+ let scriptContent = await fsPromises.readFile(sourcePath, "utf8");
65
+ if (!scriptContent.startsWith("#!")) scriptContent = HASHBANG + scriptContent;
66
+ const destPath = path.join(policyScriptsDir, `${name}.js`);
67
+ let existing = null;
68
+ try {
69
+ existing = await fsPromises.readFile(destPath, "utf8");
70
+ } catch {}
71
+ if (existing !== null && sha256(existing) === sha256(scriptContent)) continue;
72
+ await fsPromises.writeFile(destPath, scriptContent, { mode: 493 });
73
+ } catch (err) {
74
+ console.warn(`Warning: Could not install built-in policy ${name}:`, err instanceof Error ? err.message : String(err));
75
+ }
76
+ }
77
+
43
78
  //#endregion
44
79
  //#region src/cli/commands/init.ts
80
+ function readBackupGitignoreTemplate() {
81
+ let currentDir = path.dirname(fileURLToPath(import.meta.url));
82
+ while (currentDir !== path.parse(currentDir).root && !fs.existsSync(path.join(currentDir, "package.json"))) currentDir = path.dirname(currentDir);
83
+ const templatePath = path.join(currentDir, "docs", "backups", "clawmini.gitignore");
84
+ try {
85
+ return fs.readFileSync(templatePath, "utf8");
86
+ } catch {
87
+ return null;
88
+ }
89
+ }
45
90
  const initCmd = new Command("init").description("Initialize a new .clawmini settings folder").option("--agent <name>", "Initialize with a specific agent").option("--agent-template <name>", "Template to use for the agent").option("--environment <name>", "Enable a specific environment").action(async (options) => {
46
91
  if (options.agentTemplate && !options.agent) handleError$1("initialize", /* @__PURE__ */ new Error("--agent-template cannot be used without --agent"));
47
92
  if (options.agent && !isValidAgentId(options.agent)) handleError$1("initialize", /* @__PURE__ */ new Error(`Invalid agent ID: ${options.agent}`));
@@ -62,6 +107,15 @@ const initCmd = new Command("init").description("Initialize a new .clawmini sett
62
107
  if (!fs.existsSync(dirPath)) fs.mkdirSync(dirPath, { recursive: true });
63
108
  fs.writeFileSync(settingsPath, JSON.stringify(defaultSettings, null, 2));
64
109
  console.log("Initialized .clawmini/settings.json");
110
+ const gitignoreTemplate = readBackupGitignoreTemplate();
111
+ if (gitignoreTemplate) {
112
+ const gitignorePath = path.join(dirPath, ".gitignore");
113
+ if (!fs.existsSync(gitignorePath)) {
114
+ fs.writeFileSync(gitignorePath, gitignoreTemplate);
115
+ console.log("Initialized .clawmini/.gitignore");
116
+ }
117
+ } else console.warn("Warning: backup .gitignore template not found; skipping .clawmini/.gitignore");
118
+ await installBuiltinPolicies(dirPath);
65
119
  if (options.agent) try {
66
120
  const agentId = options.agent;
67
121
  await createAgentWithChat(agentId, {}, options.agentTemplate);
@@ -129,10 +183,10 @@ messagesCmd.command("send <message>").description("Send a new message").option("
129
183
  if (options.file && options.file.length > 0) {
130
184
  finalFiles = [];
131
185
  const tmpDir = path.join(getClawminiDir(process.cwd()), "tmp");
132
- await fs$2.mkdir(tmpDir, { recursive: true });
186
+ await fs$1.mkdir(tmpDir, { recursive: true });
133
187
  for (const f of options.file) {
134
188
  const dest = path.join(tmpDir, `cli-${Date.now()}-${Math.random().toString(36).substring(2, 7)}-${path.basename(f)}`);
135
- await fs$2.copyFile(path.resolve(process.cwd(), f), dest);
189
+ await fs$1.copyFile(path.resolve(process.cwd(), f), dest);
136
190
  finalFiles.push(dest);
137
191
  }
138
192
  }
@@ -256,10 +310,10 @@ agentsCmd.command("list").description("Display existing agents").action(async ()
256
310
  handleError$1("list agents", err);
257
311
  }
258
312
  });
259
- agentsCmd.command("add <id>").description("Create a new agent").option("-d, --directory <dir>", "Working directory for the agent").option("-t, --template <name>", "Template to use for the agent").option("-e, --env <env...>", "Environment variables in KEY=VALUE format (can be specified multiple times)").action(async (id, options) => {
313
+ agentsCmd.command("add <id>").description("Create a new agent").option("-d, --directory <dir>", "Working directory for the agent").option("-t, --template <name>", "Template to use for the agent").option("-e, --env <env...>", "Environment variables in KEY=VALUE format (can be specified multiple times)").option("--fork", "Copy the template settings into the agent fully (legacy, no auto-update)").option("--force", "Overwrite existing files in the target directory on first install").action(async (id, options) => {
260
314
  try {
261
315
  assertValidAgentId(id);
262
- if (await getAgent(id)) throw new Error(`Agent ${id} already exists.`);
316
+ if (await getAgentOverlay(id)) throw new Error(`Agent ${id} already exists.`);
263
317
  const agentData = {};
264
318
  if (options.directory) agentData.directory = options.directory;
265
319
  const env = parseEnv(options.env);
@@ -267,7 +321,11 @@ agentsCmd.command("add <id>").description("Create a new agent").option("-d, --di
267
321
  ...agentData.env || {},
268
322
  ...env
269
323
  };
270
- await createAgentWithChat(id, agentData, options.template);
324
+ const applyOpts = {
325
+ ...options.fork ? { fork: true } : {},
326
+ ...options.force ? { force: true } : {}
327
+ };
328
+ await createAgentWithChat(id, agentData, options.template, process.cwd(), applyOpts);
271
329
  console.log(`Agent ${id} created successfully.`);
272
330
  } catch (err) {
273
331
  handleError$1("create agent", err);
@@ -276,7 +334,7 @@ agentsCmd.command("add <id>").description("Create a new agent").option("-d, --di
276
334
  agentsCmd.command("update <id>").description("Update an existing agent").option("-d, --directory <dir>", "Working directory for the agent").option("-e, --env <env...>", "Environment variables in KEY=VALUE format (can be specified multiple times)").action(async (id, options) => {
277
335
  try {
278
336
  assertValidAgentId(id);
279
- const existing = await getAgent(id);
337
+ const existing = await getAgentOverlay(id);
280
338
  if (!existing) throw new Error(`Agent ${id} does not exist.`);
281
339
  const agentData = { ...existing };
282
340
  if (options.directory !== void 0) agentData.directory = options.directory;
@@ -294,17 +352,76 @@ agentsCmd.command("update <id>").description("Update an existing agent").option(
294
352
  agentsCmd.command("delete <id>").description("Remove an agent").action(async (id) => {
295
353
  try {
296
354
  assertValidAgentId(id);
297
- if (!await getAgent(id)) throw new Error(`Agent ${id} does not exist.`);
355
+ if (!await getAgentOverlay(id)) throw new Error(`Agent ${id} does not exist.`);
298
356
  await deleteAgent(id);
299
357
  console.log(`Agent ${id} deleted successfully.`);
300
358
  } catch (err) {
301
359
  handleError$1("delete agent", err);
302
360
  }
303
361
  });
362
+ agentsCmd.command("refresh <id>").description("Refresh the agent's tracked template files against the installed clawmini").option("--accept", "Overwrite files that have diverged from the recorded SHA").option("--dry-run", "Print the per-file plan without writing anything").action(async (id, options) => {
363
+ try {
364
+ assertValidAgentId(id);
365
+ const overlay = await getAgentOverlay(id);
366
+ if (!overlay) throw new Error(`Agent ${id} does not exist.`);
367
+ const refreshOpts = {
368
+ ...options.accept === void 0 ? {} : { accept: options.accept },
369
+ ...options.dryRun === void 0 ? {} : { dryRun: options.dryRun }
370
+ };
371
+ if (overlay.extends) {
372
+ const plan = await refreshAgentTemplate(id, overlay, process.cwd(), refreshOpts);
373
+ if (plan) for (const line of formatPlanActions(plan)) console.log(line);
374
+ }
375
+ const resolved = await getAgent(id);
376
+ if (resolved) {
377
+ const skillsPlan = await refreshAgentSkills(id, resolved, process.cwd(), refreshOpts);
378
+ if (skillsPlan) for (const line of formatPlanActions(skillsPlan)) console.log(line);
379
+ }
380
+ } catch (err) {
381
+ handleError$1("refresh agent", err);
382
+ }
383
+ });
304
384
 
305
385
  //#endregion
306
386
  //#region src/cli/commands/down.ts
307
- const downCmd = new Command("down").description("Stop the local clawmini daemon server").action(async () => {
387
+ function isErrnoCode(err, code) {
388
+ return err instanceof Error && err.code === code;
389
+ }
390
+ async function stopSupervisor(pid) {
391
+ process.stdout.write(`Stopping clawmini supervisor (pid ${pid})`);
392
+ try {
393
+ process.kill(pid, "SIGTERM");
394
+ } catch (err) {
395
+ if (isErrnoCode(err, "ESRCH")) {
396
+ process.stdout.write("\nSupervisor already exited.\n");
397
+ return;
398
+ }
399
+ throw new Error(`Failed to signal supervisor: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
400
+ }
401
+ const TIMEOUT_MS = 9e4;
402
+ const deadline = Date.now() + TIMEOUT_MS;
403
+ while (Date.now() < deadline) {
404
+ await new Promise((r) => setTimeout(r, 200));
405
+ process.stdout.write(".");
406
+ try {
407
+ process.kill(pid, 0);
408
+ } catch {
409
+ process.stdout.write("\nSuccessfully shut down clawmini supervisor.\n");
410
+ return;
411
+ }
412
+ }
413
+ throw new Error(`Supervisor did not exit within ${TIMEOUT_MS / 1e3} seconds.`);
414
+ }
415
+ const downCmd = new Command("down").description("Stop the local clawmini supervisor or daemon").action(async () => {
416
+ const supPid = readSupervisorPid();
417
+ if (supPid) try {
418
+ await stopSupervisor(supPid);
419
+ return;
420
+ } catch (err) {
421
+ console.error("\n", err instanceof Error ? err.message : String(err));
422
+ process.exit(1);
423
+ }
424
+ removeSupervisorPid();
308
425
  try {
309
426
  const client = await getDaemonClient({ autoStart: false });
310
427
  process.stdout.write("Shutting down clawmini daemon...");
@@ -326,10 +443,69 @@ const downCmd = new Command("down").description("Stop the local clawmini daemon
326
443
 
327
444
  //#endregion
328
445
  //#region src/cli/commands/up.ts
329
- const upCmd = new Command("up").description("Start the local clawmini daemon server").action(async () => {
446
+ function ensureDefaultPoliciesFile() {
447
+ const policiesPath = getPoliciesPath();
448
+ if (fs.existsSync(policiesPath)) return;
449
+ fs.mkdirSync(path.dirname(policiesPath), { recursive: true });
450
+ fs.writeFileSync(policiesPath, JSON.stringify({ policies: {} }, null, 2));
451
+ }
452
+ function logPlanWarnings(agentId, directory, actions) {
453
+ const workdir = directory ?? agentId;
454
+ for (const rawAction of actions) {
455
+ if (rawAction.action !== "skip-diverged") continue;
456
+ const action = rawAction;
457
+ if (action.reason === "edited") console.warn(`./${path.join(workdir, action.relPath)} differs from template; skipping refresh. Run 'clawmini agents refresh ${agentId} --accept' to overwrite.`);
458
+ else if (action.reason === "no-recorded-sha") console.warn(`./${path.join(workdir, action.relPath)} has no recorded SHA; skipping. Run 'clawmini agents refresh ${agentId} --accept' to adopt the current file.`);
459
+ }
460
+ }
461
+ async function refreshAllAgents(opts = {}) {
462
+ const output = [];
463
+ const agentIds = await listAgents();
464
+ for (const agentId of agentIds) {
465
+ let overlay;
466
+ try {
467
+ overlay = await getAgentOverlay(agentId);
468
+ } catch (err) {
469
+ console.warn(`Skipping refresh for agent '${agentId}': ${err instanceof Error ? err.message : String(err)}`);
470
+ continue;
471
+ }
472
+ if (!overlay) continue;
473
+ try {
474
+ if (overlay.extends) {
475
+ const plan = await refreshAgentTemplate(agentId, overlay, process.cwd(), opts.dryRun ? { dryRun: true } : {});
476
+ if (plan) {
477
+ logPlanWarnings(agentId, overlay.directory, plan.actions);
478
+ output.push(...formatPlanActions(plan, { agentId }));
479
+ }
480
+ }
481
+ const resolved = await getAgent(agentId);
482
+ if (resolved) {
483
+ const skillsPlan = await refreshAgentSkills(agentId, resolved, process.cwd(), opts.dryRun ? { dryRun: true } : {});
484
+ if (skillsPlan) {
485
+ logPlanWarnings(agentId, overlay.directory, skillsPlan.actions);
486
+ output.push(...formatPlanActions(skillsPlan, { agentId }));
487
+ }
488
+ }
489
+ } catch (err) {
490
+ console.warn(`Failed to refresh agent '${agentId}': ${err instanceof Error ? err.message : String(err)}`);
491
+ }
492
+ }
493
+ return output;
494
+ }
495
+ const upCmd = new Command("up").description("Start the local clawmini daemon server").option("--dry-run", "Print the per-file refresh plan without writing anything").action(async (options) => {
330
496
  try {
331
497
  const socketPath = getSocketPath();
332
498
  const wasRunning = fs.existsSync(socketPath);
499
+ if (options.dryRun) {
500
+ const lines = await refreshAllAgents({ dryRun: true });
501
+ if (lines.length === 0) console.log("Dry run: no agents to refresh.");
502
+ else for (const line of lines) console.log(line);
503
+ return;
504
+ }
505
+ await installBuiltinPolicies();
506
+ ensureDefaultPoliciesFile();
507
+ await exportLiteToAllEnvironments();
508
+ await refreshAllAgents();
333
509
  await (await getDaemonClient({ autoStart: true })).ping.query();
334
510
  if (wasRunning) console.log("Daemon is already running.");
335
511
  else console.log("Successfully started clawmini daemon.");
@@ -339,6 +515,486 @@ const upCmd = new Command("up").description("Start the local clawmini daemon ser
339
515
  }
340
516
  });
341
517
 
518
+ //#endregion
519
+ //#region src/cli/supervisor.ts
520
+ const DISPLAY_NAMES = {
521
+ daemon: "daemon",
522
+ web: "web",
523
+ "adapter-discord": "discord",
524
+ "adapter-google-chat": "google-chat"
525
+ };
526
+ const ADAPTER_TERMINATE_TIMEOUT_MS = 1e4;
527
+ const DAEMON_TERMINATE_TIMEOUT_MS = 6e4;
528
+ function resolveServiceCommand(service) {
529
+ const cliPath = fileURLToPath(import.meta.url);
530
+ switch (service) {
531
+ case "daemon": return {
532
+ command: process.execPath,
533
+ args: [new URL("../daemon/index.mjs", import.meta.url).pathname]
534
+ };
535
+ case "web": return {
536
+ command: process.execPath,
537
+ args: [cliPath, "web"]
538
+ };
539
+ case "adapter-discord": return {
540
+ command: process.execPath,
541
+ args: [new URL("../adapter-discord/index.mjs", import.meta.url).pathname]
542
+ };
543
+ case "adapter-google-chat": return {
544
+ command: process.execPath,
545
+ args: [new URL("../adapter-google-chat/index.mjs", import.meta.url).pathname]
546
+ };
547
+ }
548
+ }
549
+ var Supervisor = class Supervisor {
550
+ children = /* @__PURE__ */ new Map();
551
+ logFds = /* @__PURE__ */ new Map();
552
+ shuttingDown = false;
553
+ restarting = /* @__PURE__ */ new Set();
554
+ enabledServices = /* @__PURE__ */ new Set();
555
+ logDir;
556
+ constructor(logDir) {
557
+ this.logDir = logDir;
558
+ if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true });
559
+ }
560
+ async startService(service) {
561
+ this.enabledServices.add(service);
562
+ const { command, args } = resolveServiceCommand(service);
563
+ const logPath = path.join(this.logDir, `${service}.log`);
564
+ const logFd = fs.openSync(logPath, "a");
565
+ this.logFds.set(service, logFd);
566
+ fs.writeSync(logFd, `\n--- clawmini serve: ${service} starting at ${(/* @__PURE__ */ new Date()).toISOString()} ---\n`);
567
+ const child = spawn(command, args, {
568
+ stdio: [
569
+ "ignore",
570
+ "pipe",
571
+ "pipe"
572
+ ],
573
+ env: process.env,
574
+ cwd: process.cwd()
575
+ });
576
+ this.children.set(service, child);
577
+ this.attachPipe(service, child.stdout, logFd, "stdout");
578
+ this.attachPipe(service, child.stderr, logFd, "stderr");
579
+ child.on("exit", (code, signal) => {
580
+ const msg = `exited code=${code} signal=${signal}`;
581
+ process.stderr.write(`[${DISPLAY_NAMES[service]}] ${msg}\n`);
582
+ try {
583
+ fs.writeSync(logFd, `--- ${msg} at ${(/* @__PURE__ */ new Date()).toISOString()} ---\n`);
584
+ fs.closeSync(logFd);
585
+ } catch {}
586
+ this.logFds.delete(service);
587
+ this.children.delete(service);
588
+ if (service === "daemon" && !this.shuttingDown && !this.restarting.has("daemon")) {
589
+ process.stderr.write("[supervisor] daemon exited unexpectedly — shutting down\n");
590
+ this.shutdown(1);
591
+ }
592
+ });
593
+ }
594
+ attachPipe(service, stream, logFd, kind) {
595
+ const prefix = `[${DISPLAY_NAMES[service]}] `;
596
+ const target = kind === "stderr" ? process.stderr : process.stdout;
597
+ readline.createInterface({ input: stream }).on("line", (line) => {
598
+ try {
599
+ fs.writeSync(logFd, line + "\n");
600
+ } catch {}
601
+ target.write(prefix + line + "\n");
602
+ });
603
+ }
604
+ async waitForDaemonSocket(timeoutMs = 1e4) {
605
+ const socketPath = getSocketPath();
606
+ const start = Date.now();
607
+ while (Date.now() - start < timeoutMs) {
608
+ if (fs.existsSync(socketPath)) return;
609
+ if (!this.children.has("daemon")) throw new Error("Daemon exited before socket became available.");
610
+ await new Promise((r) => setTimeout(r, 100));
611
+ }
612
+ throw new Error(`Daemon did not start within ${timeoutMs}ms`);
613
+ }
614
+ async shutdown(exitCode = 0) {
615
+ if (this.shuttingDown) return;
616
+ this.shuttingDown = true;
617
+ process.stderr.write("\n[supervisor] shutting down...\n");
618
+ await this.stopAllChildren();
619
+ process.exit(exitCode);
620
+ }
621
+ /**
622
+ * Stop all children and close log fds without exiting the process. Used by
623
+ * `/upgrade`, which needs to tear down everything, run an install, then
624
+ * launch a fresh supervisor.
625
+ */
626
+ async stopAllChildren() {
627
+ const adapterStops = [];
628
+ for (const [name, child] of this.children) {
629
+ if (name === "daemon") continue;
630
+ adapterStops.push(Supervisor.terminateChild(name, child, ADAPTER_TERMINATE_TIMEOUT_MS));
631
+ }
632
+ await Promise.allSettled(adapterStops);
633
+ const daemonChild = this.children.get("daemon");
634
+ if (daemonChild) await Supervisor.terminateChild("daemon", daemonChild, DAEMON_TERMINATE_TIMEOUT_MS);
635
+ for (const fd of this.logFds.values()) try {
636
+ fs.closeSync(fd);
637
+ } catch {}
638
+ this.logFds.clear();
639
+ }
640
+ /**
641
+ * Bounce every service that has ever been started under this supervisor.
642
+ * Daemon goes down with the adapters, then comes back first so the
643
+ * adapters can re-establish their tRPC subscriptions to the new daemon.
644
+ *
645
+ * This is what `/restart` and the `/upgrade` failure recovery paths call:
646
+ * just bouncing the daemon would leave adapter-discord (and any other
647
+ * adapter) holding a dead subscription, so outbound messages would never
648
+ * reach the chat. The user-visible symptom was "I send /restart and then
649
+ * the daemon's reply never shows up in Discord."
650
+ */
651
+ async restartAll() {
652
+ if (this.shuttingDown) return;
653
+ this.restarting.add("daemon");
654
+ try {
655
+ await this.stopAllChildren();
656
+ await new Promise((r) => setImmediate(r));
657
+ if (this.enabledServices.has("daemon")) {
658
+ await this.startService("daemon");
659
+ await this.waitForDaemonSocket();
660
+ }
661
+ for (const name of this.enabledServices) {
662
+ if (name === "daemon") continue;
663
+ await this.startService(name);
664
+ }
665
+ } finally {
666
+ this.restarting.delete("daemon");
667
+ }
668
+ }
669
+ /**
670
+ * Stop and re-spawn a single service. The exit handler is suppressed so a
671
+ * restarted daemon doesn't trigger a full shutdown.
672
+ */
673
+ async restartService(service) {
674
+ if (this.shuttingDown) return;
675
+ this.restarting.add(service);
676
+ try {
677
+ const child = this.children.get(service);
678
+ if (child) {
679
+ const timeoutMs = service === "daemon" ? DAEMON_TERMINATE_TIMEOUT_MS : ADAPTER_TERMINATE_TIMEOUT_MS;
680
+ await Supervisor.terminateChild(service, child, timeoutMs);
681
+ }
682
+ await new Promise((r) => setImmediate(r));
683
+ await this.startService(service);
684
+ if (service === "daemon") await this.waitForDaemonSocket();
685
+ } finally {
686
+ this.restarting.delete(service);
687
+ }
688
+ }
689
+ static terminateChild(name, child, timeoutMs) {
690
+ return new Promise((resolve) => {
691
+ if (child.exitCode !== null || child.signalCode !== null) {
692
+ resolve();
693
+ return;
694
+ }
695
+ const timer = setTimeout(() => {
696
+ process.stderr.write(`[supervisor] ${name} did not exit in ${Math.round(timeoutMs / 1e3)}s, sending SIGKILL\n`);
697
+ try {
698
+ child.kill("SIGKILL");
699
+ } catch {}
700
+ }, timeoutMs);
701
+ child.once("exit", () => {
702
+ clearTimeout(timer);
703
+ resolve();
704
+ });
705
+ try {
706
+ child.kill("SIGTERM");
707
+ } catch {
708
+ clearTimeout(timer);
709
+ resolve();
710
+ }
711
+ });
712
+ }
713
+ };
714
+
715
+ //#endregion
716
+ //#region src/cli/commands/serve.ts
717
+ const ALL_SERVICES = [
718
+ "daemon",
719
+ "web",
720
+ "adapter-discord",
721
+ "adapter-google-chat"
722
+ ];
723
+ const SERVICE_ALIASES$1 = {
724
+ daemon: "daemon",
725
+ web: "web",
726
+ discord: "adapter-discord",
727
+ "adapter-discord": "adapter-discord",
728
+ "google-chat": "adapter-google-chat",
729
+ "adapter-google-chat": "adapter-google-chat"
730
+ };
731
+ function resolveEnabledServices(opts) {
732
+ const parseList = (csv) => {
733
+ if (!csv) return [];
734
+ return csv.split(",").map((s) => s.trim()).filter(Boolean).map((name) => {
735
+ const resolved = SERVICE_ALIASES$1[name];
736
+ if (!resolved) throw new Error(`Unknown service: '${name}'. Valid: ${Object.keys(SERVICE_ALIASES$1).join(", ")}`);
737
+ return resolved;
738
+ });
739
+ };
740
+ const configPresent = {
741
+ daemon: true,
742
+ web: true,
743
+ "adapter-discord": false,
744
+ "adapter-google-chat": false,
745
+ ...opts.adapterConfigPresent ?? {}
746
+ };
747
+ const excluded = new Set(parseList(opts.exclude));
748
+ const only = parseList(opts.only);
749
+ if (only.length > 0) return only.filter((n) => !excluded.has(n));
750
+ return ALL_SERVICES.filter((n) => configPresent[n] && !excluded.has(n));
751
+ }
752
+ async function runPreStart() {
753
+ await installBuiltinPolicies();
754
+ ensureDefaultPoliciesFile();
755
+ await exportLiteToAllEnvironments();
756
+ await refreshAllAgents();
757
+ }
758
+ async function runForeground(enabled) {
759
+ const supervisor = new Supervisor(path.join(getClawminiDir(), "logs"));
760
+ writeSupervisorPid(process.pid);
761
+ process.on("exit", () => {
762
+ removeSupervisorPid();
763
+ try {
764
+ fs.unlinkSync(getControlSocketPath());
765
+ } catch {}
766
+ });
767
+ const onSignal = () => {
768
+ supervisor.shutdown(0);
769
+ };
770
+ process.on("SIGINT", onSignal);
771
+ process.on("SIGTERM", onSignal);
772
+ if (enabled.includes("daemon")) {
773
+ process.stderr.write("[supervisor] starting daemon...\n");
774
+ await supervisor.startService("daemon");
775
+ await supervisor.waitForDaemonSocket();
776
+ process.stderr.write("[supervisor] daemon ready\n");
777
+ }
778
+ for (const name of enabled) {
779
+ if (name === "daemon") continue;
780
+ await supervisor.startService(name);
781
+ }
782
+ startSupervisorControl(supervisor);
783
+ process.stderr.write(`[supervisor] running: ${enabled.join(", ")}\n`);
784
+ process.stderr.write("[supervisor] press Ctrl-C to stop (or run 'clawmini down' elsewhere)\n");
785
+ }
786
+ function isSocketLive(socketPath) {
787
+ return new Promise((resolve) => {
788
+ const client = net.createConnection({ path: socketPath });
789
+ client.on("connect", () => {
790
+ client.destroy();
791
+ resolve(true);
792
+ });
793
+ client.on("error", () => resolve(false));
794
+ });
795
+ }
796
+ function printSupervisorLogTail(supLog, fromOffset) {
797
+ if (!fs.existsSync(supLog)) return;
798
+ const size = fs.statSync(supLog).size;
799
+ if (size <= fromOffset) return;
800
+ const fd = fs.openSync(supLog, "r");
801
+ try {
802
+ const len = size - fromOffset;
803
+ const buf = Buffer.alloc(len);
804
+ fs.readSync(fd, buf, 0, len, fromOffset);
805
+ const text = buf.toString("utf-8");
806
+ process.stderr.write("--- supervisor.log ---\n");
807
+ process.stderr.write(text);
808
+ if (!text.endsWith("\n")) process.stderr.write("\n");
809
+ } finally {
810
+ fs.closeSync(fd);
811
+ }
812
+ }
813
+ async function runDetached(args) {
814
+ const clawDir = getClawminiDir();
815
+ const logDir = path.join(clawDir, "logs");
816
+ if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true });
817
+ const supLog = path.join(logDir, "supervisor.log");
818
+ const supLogOffset = fs.existsSync(supLog) ? fs.statSync(supLog).size : 0;
819
+ const outFd = fs.openSync(supLog, "a");
820
+ removeSupervisorPid();
821
+ const childArgs = args.filter((a) => a !== "--detach" && a !== "-d");
822
+ const child = spawn(process.execPath, childArgs, {
823
+ detached: true,
824
+ stdio: [
825
+ "ignore",
826
+ outFd,
827
+ outFd
828
+ ],
829
+ cwd: process.cwd(),
830
+ env: process.env
831
+ });
832
+ child.unref();
833
+ fs.closeSync(outFd);
834
+ const pidPath = getSupervisorPidPath();
835
+ const socketPath = getSocketPath();
836
+ const STARTUP_TIMEOUT_MS = 3e4;
837
+ const deadline = Date.now() + STARTUP_TIMEOUT_MS;
838
+ while (Date.now() < deadline) {
839
+ if (child.exitCode !== null || child.signalCode !== null) {
840
+ console.error(`clawmini serve exited during startup (code=${child.exitCode}, signal=${child.signalCode}).`);
841
+ printSupervisorLogTail(supLog, supLogOffset);
842
+ process.exit(1);
843
+ }
844
+ if (fs.existsSync(pidPath) && fs.existsSync(socketPath)) {
845
+ console.log(`Started clawmini supervisor in background (pid ${child.pid}).`);
846
+ console.log(` Logs: clawmini logs -f`);
847
+ console.log(` Stop: clawmini down`);
848
+ process.exit(0);
849
+ }
850
+ await new Promise((r) => setTimeout(r, 100));
851
+ }
852
+ console.error(`clawmini serve did not become ready within ${STARTUP_TIMEOUT_MS / 1e3}s.`);
853
+ printSupervisorLogTail(supLog, supLogOffset);
854
+ process.exit(1);
855
+ }
856
+ const serveCmd = new Command("serve").description("Run daemon, web UI, and configured adapters under one supervisor").option("-d, --detach", "Run in the background; logs go to .clawmini/logs/").option("--only <services>", "Comma-separated subset to run (daemon,web,discord,google-chat)").option("--exclude <services>", "Comma-separated services to skip").action(async (options) => {
857
+ const clawDir = getClawminiDir();
858
+ if (!fs.existsSync(clawDir)) {
859
+ console.error(`Not a clawmini workspace (no ${clawDir}). Run 'clawmini init' first.`);
860
+ process.exit(1);
861
+ }
862
+ const existingSupervisorPid = readSupervisorPid();
863
+ if (existingSupervisorPid) {
864
+ console.error(`clawmini serve is already running (pid ${existingSupervisorPid}). Run 'clawmini down' to stop it.`);
865
+ process.exit(1);
866
+ }
867
+ const socketPath = getSocketPath();
868
+ if (fs.existsSync(socketPath)) {
869
+ if (await isSocketLive(socketPath)) {
870
+ console.error("A clawmini daemon is already running (socket present). Run 'clawmini down' before 'clawmini serve'.");
871
+ process.exit(1);
872
+ }
873
+ try {
874
+ fs.unlinkSync(socketPath);
875
+ process.stderr.write(`[supervisor] removed stale socket at ${socketPath}\n`);
876
+ } catch (err) {
877
+ console.error(`Failed to remove stale socket at ${socketPath}: ${err instanceof Error ? err.message : String(err)}`);
878
+ process.exit(1);
879
+ }
880
+ }
881
+ let enabled;
882
+ try {
883
+ enabled = resolveEnabledServices({
884
+ only: options.only,
885
+ exclude: options.exclude,
886
+ adapterConfigPresent: {
887
+ "adapter-discord": fs.existsSync(getDiscordConfigPath()),
888
+ "adapter-google-chat": fs.existsSync(getGoogleChatConfigPath())
889
+ }
890
+ });
891
+ } catch (err) {
892
+ console.error(err instanceof Error ? err.message : String(err));
893
+ process.exit(1);
894
+ }
895
+ if (enabled.length === 0) {
896
+ console.error("No services selected. Check --only/--exclude.");
897
+ process.exit(1);
898
+ }
899
+ if (options.detach) {
900
+ await runDetached(process.argv.slice(1));
901
+ return;
902
+ }
903
+ try {
904
+ await runPreStart();
905
+ } catch (err) {
906
+ console.error("Pre-start setup failed:", err instanceof Error ? err.message : String(err));
907
+ process.exit(1);
908
+ }
909
+ try {
910
+ await runForeground(enabled);
911
+ } catch (err) {
912
+ console.error("Failed to start services:", err instanceof Error ? err.message : String(err));
913
+ removeSupervisorPid();
914
+ process.exit(1);
915
+ }
916
+ });
917
+
918
+ //#endregion
919
+ //#region src/cli/commands/logs.ts
920
+ const SERVICE_ALIASES = {
921
+ daemon: "daemon",
922
+ web: "web",
923
+ discord: "adapter-discord",
924
+ "adapter-discord": "adapter-discord",
925
+ "google-chat": "adapter-google-chat",
926
+ "adapter-google-chat": "adapter-google-chat",
927
+ supervisor: "supervisor"
928
+ };
929
+ function displayNameFor(logBase) {
930
+ if (logBase.startsWith("adapter-")) return logBase.slice(8);
931
+ return logBase;
932
+ }
933
+ function tailString(content, n) {
934
+ const lines = content.split("\n");
935
+ if (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
936
+ return lines.slice(-n);
937
+ }
938
+ const logsCmd = new Command("logs").description("View logs from clawmini services (daemon, web, adapters)").option("-f, --follow", "Follow the logs as new lines are written").option("-s, --service <name>", "Restrict to one service: daemon, web, discord, google-chat, supervisor").option("-n, --lines <count>", "Number of lines to show from the tail of each file", "50").action(async (options) => {
939
+ const logDir = path.join(getClawminiDir(), "logs");
940
+ if (!fs.existsSync(logDir)) {
941
+ console.error(`No log directory at ${logDir}. Start the supervisor with 'clawmini serve'.`);
942
+ process.exit(1);
943
+ }
944
+ let targets;
945
+ if (options.service) {
946
+ const resolved = SERVICE_ALIASES[options.service];
947
+ if (!resolved) {
948
+ console.error(`Unknown service '${options.service}'. Valid: ${Object.keys(SERVICE_ALIASES).join(", ")}`);
949
+ process.exit(1);
950
+ }
951
+ targets = [`${resolved}.log`];
952
+ } else targets = fs.readdirSync(logDir).filter((f) => f.endsWith(".log"));
953
+ const linesCount = Math.max(0, parseInt(options.lines ?? "50", 10) || 50);
954
+ for (const file of targets) {
955
+ const full = path.join(logDir, file);
956
+ if (!fs.existsSync(full)) continue;
957
+ const prefix = `[${displayNameFor(file.replace(/\.log$/, ""))}] `;
958
+ const content = fs.readFileSync(full, "utf-8");
959
+ for (const line of tailString(content, linesCount)) process.stdout.write(prefix + line + "\n");
960
+ }
961
+ if (!options.follow) return;
962
+ const watched = [];
963
+ for (const file of targets) {
964
+ const full = path.join(logDir, file);
965
+ const prefix = `[${displayNameFor(file.replace(/\.log$/, ""))}] `;
966
+ const position = fs.existsSync(full) ? fs.statSync(full).size : 0;
967
+ watched.push({
968
+ file,
969
+ full,
970
+ prefix,
971
+ position
972
+ });
973
+ fs.watchFile(full, { interval: 500 }, (curr) => {
974
+ const entry = watched.find((w) => w.full === full);
975
+ if (!entry) return;
976
+ if (curr.size < entry.position) entry.position = 0;
977
+ if (curr.size > entry.position) {
978
+ const fd = fs.openSync(full, "r");
979
+ const buf = Buffer.alloc(curr.size - entry.position);
980
+ fs.readSync(fd, buf, 0, buf.length, entry.position);
981
+ fs.closeSync(fd);
982
+ entry.position = curr.size;
983
+ const lines = buf.toString().split("\n");
984
+ if (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
985
+ for (const line of lines) process.stdout.write(entry.prefix + line + "\n");
986
+ }
987
+ });
988
+ }
989
+ const stop = () => {
990
+ for (const entry of watched) fs.unwatchFile(entry.full);
991
+ process.exit(0);
992
+ };
993
+ process.on("SIGINT", stop);
994
+ process.on("SIGTERM", stop);
995
+ await new Promise(() => {});
996
+ });
997
+
342
998
  //#endregion
343
999
  //#region src/cli/commands/web-api/utils.ts
344
1000
  async function parseJsonBody(req, schema) {
@@ -385,7 +1041,7 @@ async function handleApiAgents(req, res, urlPath) {
385
1041
  env: z.record(z.string(), z.string()).optional(),
386
1042
  commands: z.record(z.string(), z.string()).optional()
387
1043
  }));
388
- if (await getAgent(body.id)) {
1044
+ if (await getAgentOverlay(body.id)) {
389
1045
  sendJsonResponse(res, 409, { error: "Agent already exists" });
390
1046
  return true;
391
1047
  }
@@ -439,7 +1095,7 @@ async function handleApiAgents(req, res, urlPath) {
439
1095
  env: z.record(z.string(), z.string()).optional(),
440
1096
  commands: z.record(z.string(), z.string()).optional()
441
1097
  }));
442
- const agent = await getAgent(agentId) || {};
1098
+ const agent = await getAgentOverlay(agentId) || {};
443
1099
  if (body.directory !== void 0) agent.directory = body.directory;
444
1100
  if (body.env !== void 0) agent.env = body.env;
445
1101
  if (body.commands !== void 0) agent.commands = body.commands;
@@ -817,9 +1473,9 @@ const exportLiteCmd = new Command("export-lite").description("Export the standal
817
1473
  //#endregion
818
1474
  //#region src/cli/commands/environments.ts
819
1475
  const environmentsCmd = new Command("environments").description("Manage environments");
820
- environmentsCmd.command("enable <name>").description("Enable an environment for a path in the workspace").option("-p, --path <subpath>", "Path to apply the environment to", "./").action(async (name, options) => {
1476
+ environmentsCmd.command("enable <name>").description("Enable an environment for a path in the workspace").option("-p, --path <subpath>", "Path to apply the environment to", "./").option("--fork", "Clone the built-in template directory instead of writing a thin overlay").action(async (name, options) => {
821
1477
  try {
822
- await enableEnvironment(name, options.path);
1478
+ await enableEnvironment(name, options.path, process.cwd(), options.fork ? { fork: true } : {});
823
1479
  } catch (err) {
824
1480
  handleError$1("enable environment", err);
825
1481
  }
@@ -842,7 +1498,7 @@ environmentsCmd.command("disable").description("Disable an environment mapping")
842
1498
 
843
1499
  //#endregion
844
1500
  //#region src/cli/commands/policies.ts
845
- const SUPPORTED_POLICIES = ["propose-policy"];
1501
+ const SUPPORTED_POLICIES = ["manage-policies"];
846
1502
  const policiesCmd = new Command("policies").description("Manage sandbox policies");
847
1503
  policiesCmd.command("add <name>").description("Add a new policy").action(async (name) => {
848
1504
  if (!SUPPORTED_POLICIES.includes(name)) handleError$1("add policy", /* @__PURE__ */ new Error(`Unsupported policy: "${name}". Supported policies: ${SUPPORTED_POLICIES.join(", ")}`));
@@ -853,10 +1509,12 @@ policiesCmd.command("add <name>").description("Add a new policy").action(async (
853
1509
  if (!fs.existsSync(policyScriptsDir)) fs.mkdirSync(policyScriptsDir, { recursive: true });
854
1510
  let policies = { policies: {} };
855
1511
  if (fs.existsSync(policiesPath)) policies = JSON.parse(fs.readFileSync(policiesPath, "utf8"));
1512
+ const builtin = BUILTIN_POLICIES[name];
856
1513
  policies.policies[name] = {
857
- description: "Propose a new policy to create",
1514
+ description: builtin?.description ?? `Built-in policy ${name}`,
858
1515
  command: `./.clawmini/policy-scripts/${name}.js`,
859
- allowHelp: true
1516
+ allowHelp: builtin?.allowHelp ?? true,
1517
+ ...builtin?.autoApprove !== void 0 ? { autoApprove: builtin.autoApprove } : {}
860
1518
  };
861
1519
  fs.writeFileSync(policiesPath, JSON.stringify(policies, null, 2));
862
1520
  console.log(`Registered ${name} in .clawmini/policies.json`);
@@ -934,6 +1592,8 @@ program.addCommand(environmentsCmd);
934
1592
  program.addCommand(skillsCmd);
935
1593
  program.addCommand(downCmd);
936
1594
  program.addCommand(upCmd);
1595
+ program.addCommand(serveCmd);
1596
+ program.addCommand(logsCmd);
937
1597
  program.addCommand(webCmd);
938
1598
  program.addCommand(jobsCmd);
939
1599
  program.addCommand(exportLiteCmd);