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.
- package/.changeset/README.md +8 -0
- package/.changeset/config.json +14 -0
- package/.github/workflows/release.yml +49 -0
- package/CHANGELOG.md +36 -0
- package/README.md +5 -4
- package/dist/adapter-discord/index.d.mts.map +1 -1
- package/dist/adapter-discord/index.mjs +465 -282
- package/dist/adapter-discord/index.mjs.map +1 -1
- package/dist/adapter-google-chat/index.mjs +367 -243
- package/dist/adapter-google-chat/index.mjs.map +1 -1
- package/dist/cli/index.mjs +684 -24
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lite.mjs +43 -13
- package/dist/cli/lite.mjs.map +1 -1
- package/dist/cli/{propose-policy.mjs → manage-policies.mjs} +270 -47
- package/dist/cli/manage-policies.mjs.map +1 -0
- package/dist/cli/run-host.d.mts +1 -0
- package/dist/cli/run-host.mjs +3090 -0
- package/dist/cli/run-host.mjs.map +1 -0
- package/dist/config-CPFQIGdG.mjs +57 -0
- package/dist/config-CPFQIGdG.mjs.map +1 -0
- package/dist/config-Dvl-Pov4.mjs +76 -0
- package/dist/config-Dvl-Pov4.mjs.map +1 -0
- package/dist/daemon/index.d.mts.map +1 -1
- package/dist/daemon/index.mjs +970 -332
- package/dist/daemon/index.mjs.map +1 -1
- package/dist/supervisor-actions-CiW56eLi.mjs +843 -0
- package/dist/supervisor-actions-CiW56eLi.mjs.map +1 -0
- package/dist/turn-log-buffer-DRgW53gl.mjs +767 -0
- package/dist/turn-log-buffer-DRgW53gl.mjs.map +1 -0
- package/dist/web/_app/immutable/chunks/{Drm9vgeP.js → 3AZlWB6U.js} +1 -1
- package/dist/web/_app/immutable/chunks/BhRSsUCh.js +2 -0
- package/dist/web/_app/immutable/chunks/BiLeM2i1.js +1 -0
- package/{web/.svelte-kit/output/client/_app/immutable/chunks/CME08kGM.js → dist/web/_app/immutable/chunks/BmBj85Ll.js} +1 -1
- package/dist/web/_app/immutable/chunks/BrERcKAH.js +1 -0
- package/dist/web/_app/immutable/chunks/Bv9252RM.js +1 -0
- package/dist/web/_app/immutable/chunks/CIXNBPKi.js +1 -0
- package/dist/web/_app/immutable/chunks/DISKL3GN.js +2 -0
- package/dist/web/_app/immutable/chunks/{Zeh-C-mx.js → DcpaLzmX.js} +1 -1
- package/dist/web/_app/immutable/chunks/DnQ3vS13.js +1 -0
- package/dist/web/_app/immutable/chunks/KsloHTKS.js +1 -0
- package/{web/.svelte-kit/output/client/_app/immutable/chunks/Ck-be5J2.js → dist/web/_app/immutable/chunks/RsHsUj-8.js} +2 -2
- package/dist/web/_app/immutable/chunks/{vDehDcuJ.js → wpfV79dV.js} +1 -1
- package/dist/web/_app/immutable/entry/app.CIw1Qj0n.js +2 -0
- package/dist/web/_app/immutable/entry/start.Di0-Jhte.js +1 -0
- package/dist/web/_app/immutable/nodes/{0.CUGC2p-K.js → 0.DYyUA1au.js} +1 -1
- package/dist/web/_app/immutable/nodes/1.D-3QEMMZ.js +1 -0
- package/dist/web/_app/immutable/nodes/{2.BnwnD1Ki.js → 2.4olHnH7U.js} +1 -1
- package/{web/.svelte-kit/output/client/_app/immutable/nodes/3.0arZe_Uf.js → dist/web/_app/immutable/nodes/3.4w0bE-m2.js} +3 -3
- package/dist/web/_app/immutable/nodes/4.CZvjhVHt.js +60 -0
- package/dist/web/_app/immutable/nodes/{5.Bq2JzCEj.js → 5.DLbPVJY2.js} +1 -1
- package/dist/web/_app/version.json +1 -1
- package/dist/web/index.html +12 -12
- package/dist/workspace-oWmVh5mi.mjs +1001 -0
- package/dist/workspace-oWmVh5mi.mjs.map +1 -0
- package/docs/23_adapter_slash_autocomplete/development_log.md +19 -0
- package/docs/23_adapter_slash_autocomplete/notes.md +18 -0
- package/docs/23_adapter_slash_autocomplete/prd.md +46 -0
- package/docs/23_adapter_slash_autocomplete/questions.md +6 -0
- package/docs/23_adapter_slash_autocomplete/tickets.md +21 -0
- package/docs/24_subagent_job_policy_fixes/development_log.md +22 -0
- package/docs/24_subagent_job_policy_fixes/notes.md +28 -0
- package/docs/24_subagent_job_policy_fixes/prd.md +59 -0
- package/docs/24_subagent_job_policy_fixes/questions.md +3 -0
- package/docs/24_subagent_job_policy_fixes/tickets.md +49 -0
- package/docs/25_e2e_test_improvements/development_log.md +30 -0
- package/docs/25_e2e_test_improvements/notes.md +29 -0
- package/docs/25_e2e_test_improvements/prd.md +43 -0
- package/docs/25_e2e_test_improvements/questions.md +12 -0
- package/docs/25_e2e_test_improvements/tickets-2.md +22 -0
- package/docs/25_e2e_test_improvements/tickets.md +22 -0
- package/docs/25_policy_cwd/development_log.md +30 -0
- package/docs/25_policy_cwd/notes.md +28 -0
- package/docs/25_policy_cwd/prd.md +77 -0
- package/docs/25_policy_cwd/questions.md +6 -0
- package/docs/25_policy_cwd/tickets.md +77 -0
- package/docs/CLI_REFERENCE.md +3 -1
- package/docs/PHILOSOPHY.md +35 -0
- package/docs/adapter-visibility/SPEC.md +461 -0
- package/docs/adapter-visibility/SPEC_v2.md +202 -0
- package/docs/auto-update/SPEC.md +344 -0
- package/docs/backups/SPEC.md +296 -0
- package/docs/backups/clawmini.gitignore +69 -0
- package/docs/guides/assets/clawmini-avatar.png +0 -0
- package/docs/guides/backups.md +332 -0
- package/docs/guides/discord_adapter_setup.md +1 -1
- package/docs/guides/google_chat_adapter_setup.md +81 -0
- package/docs/unified-startup/SPEC.md +203 -0
- package/e2e/_helpers/test-environment.test.ts +49 -0
- package/e2e/_helpers/test-environment.ts +548 -0
- package/e2e/adapters/_google-chat-fixtures.ts +340 -0
- package/{src/cli/e2e → e2e/adapters}/adapter-discord.test.ts +22 -23
- package/e2e/adapters/adapter-google-chat-downtime.test.ts +157 -0
- package/e2e/adapters/adapter-google-chat-inbound.test.ts +697 -0
- package/e2e/adapters/adapter-google-chat-outbound.test.ts +297 -0
- package/e2e/adapters/adapter-google-chat-roundtrip.test.ts +56 -0
- package/e2e/adapters/adapter-google-chat-threads.test.ts +1078 -0
- package/e2e/agents/custom-api-env.test.ts +80 -0
- package/e2e/agents/export-lite-func.test.ts +104 -0
- package/e2e/agents/fallbacks.test.ts +124 -0
- package/e2e/agents/interrupt.test.ts +50 -0
- package/e2e/agents/no-reply-necessary.test.ts +57 -0
- package/e2e/agents/session-timeout-subagents.test.ts +76 -0
- package/e2e/agents/subagent-authorization.test.ts +246 -0
- package/e2e/agents/subagent-env.test.ts +49 -0
- package/e2e/agents/subagent-lifecycle.test.ts +782 -0
- package/e2e/agents/subagents-depth.test.ts +47 -0
- package/e2e/cli/agents.test.ts +176 -0
- package/e2e/cli/auto-update.test.ts +741 -0
- package/e2e/cli/basic.test.ts +44 -0
- package/{src/cli/e2e → e2e/cli}/export-lite.test.ts +16 -12
- package/e2e/cli/init-gitignore.test.ts +86 -0
- package/e2e/cli/init.test.ts +76 -0
- package/e2e/cli/messages.test.ts +363 -0
- package/e2e/cli/serve.test.ts +76 -0
- package/{src/cli/e2e → e2e/cli}/skills.test.ts +11 -10
- package/{src/cli/e2e → e2e/daemon}/daemon.test.ts +57 -195
- package/e2e/jobs/agent-jobs.test.ts +216 -0
- package/e2e/jobs/cron.test.ts +64 -0
- package/e2e/jobs/restart.test.ts +108 -0
- package/e2e/policies/approval-session.test.ts +69 -0
- package/e2e/policies/auto-create-policies-file.test.ts +35 -0
- package/e2e/policies/builtin-manage-policies.test.ts +184 -0
- package/e2e/policies/builtin-run-host.test.ts +180 -0
- package/e2e/policies/environment-policies.test.ts +177 -0
- package/e2e/policies/manage-policies.test.ts +566 -0
- package/e2e/policies/output-size.test.ts +98 -0
- package/e2e/policies/policies-context-cwd.test.ts +160 -0
- package/e2e/policies/relative-script-path.test.ts +60 -0
- package/e2e/policies/requests-show.test.ts +135 -0
- package/e2e/policies/requests.test.ts +208 -0
- package/e2e/policies/slash-policies.test.ts +308 -0
- package/e2e/policies/startup-cleanup.test.ts +48 -0
- package/e2e/routers/session-timeout.test.ts +106 -0
- package/e2e/routers/slash-model.test.ts +152 -0
- package/e2e/routers/slash-new.test.ts +50 -0
- package/e2e/routers/slash-restart-adapter.test.ts +96 -0
- package/e2e/routers/slash-restart.test.ts +114 -0
- package/e2e/routers/slash-shutdown.test.ts +55 -0
- package/e2e/routers/slash-stop.test.ts +232 -0
- package/e2e/routers/slash-upgrade.test.ts +88 -0
- package/{src/cli/e2e → e2e/sandbox}/environments.test.ts +14 -13
- package/eslint.config.js +6 -0
- package/napkin.md +1 -1
- package/package.json +8 -3
- package/src/adapter-discord/commands.test.ts +42 -0
- package/src/adapter-discord/commands.ts +33 -0
- package/src/adapter-discord/config.ts +12 -0
- package/src/adapter-discord/forwarder.test.ts +499 -21
- package/src/adapter-discord/forwarder.ts +343 -124
- package/src/adapter-discord/inbound-cache.test.ts +47 -0
- package/src/adapter-discord/inbound-cache.ts +37 -0
- package/src/adapter-discord/index.test.ts +67 -2
- package/src/adapter-discord/index.ts +84 -216
- package/src/adapter-discord/interactions.test.ts +54 -3
- package/src/adapter-discord/interactions.ts +97 -53
- package/src/adapter-discord/processMessage.ts +239 -0
- package/src/adapter-discord/state.ts +1 -0
- package/src/adapter-google-chat/auth.test.ts +9 -5
- package/src/adapter-google-chat/auth.ts +29 -23
- package/src/adapter-google-chat/cards.ts +7 -2
- package/src/adapter-google-chat/client.test.ts +37 -2
- package/src/adapter-google-chat/client.ts +138 -38
- package/src/adapter-google-chat/config.ts +19 -0
- package/src/adapter-google-chat/forwarder.test.ts +81 -56
- package/src/adapter-google-chat/forwarder.ts +394 -185
- package/src/adapter-google-chat/inbound-cache.test.ts +61 -0
- package/src/adapter-google-chat/inbound-cache.ts +36 -0
- package/src/adapter-google-chat/state.test.ts +1 -0
- package/src/adapter-google-chat/state.ts +9 -1
- package/src/adapter-google-chat/subscriptions.ts +8 -6
- package/src/cli/builtin-policies.ts +44 -0
- package/src/cli/commands/agents.ts +59 -5
- package/src/cli/commands/down.ts +54 -2
- package/src/cli/commands/environments.ts +8 -2
- package/src/cli/commands/init.ts +31 -0
- package/src/cli/commands/logs.ts +116 -0
- package/src/cli/commands/policies.ts +6 -4
- package/src/cli/commands/serve.test.ts +67 -0
- package/src/cli/commands/serve.ts +284 -0
- package/src/cli/commands/up.ts +122 -2
- package/src/cli/commands/web-api/agents.ts +3 -2
- package/src/cli/index.ts +4 -0
- package/src/cli/install-detection.test.ts +72 -0
- package/src/cli/install-detection.ts +48 -0
- package/src/cli/lite.ts +54 -22
- package/src/cli/manage-policies-utils.ts +104 -0
- package/src/cli/manage-policies.ts +291 -0
- package/src/cli/run-host.ts +45 -0
- package/src/cli/supervisor-actions.ts +267 -0
- package/src/cli/supervisor-control.test.ts +129 -0
- package/src/cli/supervisor-control.ts +155 -0
- package/src/cli/supervisor-pid.ts +68 -0
- package/src/cli/supervisor.ts +277 -0
- package/src/daemon/agent/agent-context.ts +11 -11
- package/src/daemon/agent/agent-session.ts +8 -1
- package/src/daemon/agent/chat-logger.test.ts +78 -9
- package/src/daemon/agent/chat-logger.ts +25 -5
- package/src/daemon/agent/turn-registry.test.ts +89 -0
- package/src/daemon/agent/turn-registry.ts +94 -0
- package/src/daemon/agent/types.ts +2 -0
- package/src/daemon/api/agent-policy-endpoints.ts +263 -0
- package/src/daemon/api/agent-router.ts +47 -126
- package/src/daemon/api/index.test.ts +1 -0
- package/src/daemon/api/policy-request.test.ts +7 -5
- package/src/daemon/api/router-utils.ts +6 -5
- package/src/daemon/api/subagent-router.ts +110 -74
- package/src/daemon/api/subagent-utils.test.ts +60 -0
- package/src/daemon/api/subagent-utils.ts +113 -87
- package/src/daemon/api/user-router.ts +34 -8
- package/src/daemon/auth.ts +1 -0
- package/src/daemon/cron.test.ts +62 -4
- package/src/daemon/cron.ts +42 -16
- package/src/daemon/events.ts +65 -0
- package/src/daemon/index.ts +24 -1
- package/src/daemon/message-interruption.test.ts +1 -0
- package/src/daemon/message-jobs.test.ts +1 -0
- package/src/daemon/message.ts +78 -14
- package/src/daemon/observation.test.ts +26 -18
- package/src/daemon/pending-replies.test.ts +112 -0
- package/src/daemon/pending-replies.ts +162 -0
- package/src/daemon/policy-request-service.ts +3 -1
- package/src/daemon/policy-utils.test.ts +66 -1
- package/src/daemon/policy-utils.ts +126 -1
- package/src/daemon/request-store.ts +31 -0
- package/src/daemon/routers/session-timeout.ts +4 -0
- package/src/daemon/routers/slash-model.test.ts +344 -0
- package/src/daemon/routers/slash-model.ts +207 -0
- package/src/daemon/routers/slash-policies.test.ts +38 -32
- package/src/daemon/routers/slash-policies.ts +84 -33
- package/src/daemon/routers/slash-restart.test.ts +69 -0
- package/src/daemon/routers/slash-restart.ts +36 -0
- package/src/daemon/routers/slash-shutdown.test.ts +50 -0
- package/src/daemon/routers/slash-shutdown.ts +28 -0
- package/src/daemon/routers/slash-upgrade.test.ts +116 -0
- package/src/daemon/routers/slash-upgrade.ts +76 -0
- package/src/daemon/routers/types.ts +7 -0
- package/src/daemon/routers.ts +16 -0
- package/src/shared/adapters/blockquote.test.ts +28 -0
- package/src/shared/adapters/blockquote.ts +20 -0
- package/src/shared/adapters/filtering.test.ts +224 -10
- package/src/shared/adapters/filtering.ts +95 -7
- package/src/shared/adapters/inbound-cache.test.ts +48 -0
- package/src/shared/adapters/inbound-cache.ts +54 -0
- package/src/shared/adapters/turn-log-buffer.ts +266 -0
- package/src/shared/adapters/turn-log.test.ts +389 -0
- package/src/shared/adapters/turn-log.ts +357 -0
- package/src/shared/agent-utils.ts +12 -5
- package/src/shared/chats.test.ts +4 -0
- package/src/shared/chats.ts +9 -0
- package/src/shared/config.ts +16 -1
- package/src/shared/lite.ts +76 -2
- package/src/shared/policies.ts +26 -0
- package/src/shared/template-manifest.ts +267 -0
- package/src/shared/utils/shell.ts +61 -0
- package/src/shared/version.ts +34 -0
- package/src/shared/workspace.test.ts +217 -0
- package/src/shared/workspace.ts +626 -48
- package/templates/environments/cladding/allowlist-domain.mjs +125 -0
- package/templates/environments/cladding/env.json +21 -1
- package/templates/environments/cladding/run-with-network.mjs +54 -0
- package/templates/environments/macos-proxy/allowlist-domain.mjs +95 -0
- package/templates/environments/macos-proxy/env.json +8 -1
- package/templates/environments/macos-proxy/proxy.mjs +0 -1
- package/templates/gemini/template.json +5 -0
- package/templates/gemini-claw/template.json +13 -0
- package/templates/skills/clawmini-requests/SKILL.md +69 -10
- package/templates/skills/run-host/SKILL.md +51 -0
- package/templates/skills/skill-creator/SKILL.md +4 -3
- package/templates/skills/skill-creator/scripts/validate.sh +52 -0
- package/tsdown.config.ts +10 -1
- package/vitest.config.ts +2 -2
- package/web/.svelte-kit/ambient.d.ts +292 -118
- package/web/.svelte-kit/generated/server/internal.js +1 -1
- package/web/.svelte-kit/output/client/.vite/manifest.json +126 -136
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{Drm9vgeP.js → 3AZlWB6U.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BhRSsUCh.js +2 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BiLeM2i1.js +1 -0
- package/{dist/web/_app/immutable/chunks/CME08kGM.js → web/.svelte-kit/output/client/_app/immutable/chunks/BmBj85Ll.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BrERcKAH.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/Bv9252RM.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/CIXNBPKi.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DISKL3GN.js +2 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{Zeh-C-mx.js → DcpaLzmX.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DnQ3vS13.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/KsloHTKS.js +1 -0
- package/{dist/web/_app/immutable/chunks/Ck-be5J2.js → web/.svelte-kit/output/client/_app/immutable/chunks/RsHsUj-8.js} +2 -2
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{vDehDcuJ.js → wpfV79dV.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/entry/app.CIw1Qj0n.js +2 -0
- package/web/.svelte-kit/output/client/_app/immutable/entry/start.Di0-Jhte.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{0.CUGC2p-K.js → 0.DYyUA1au.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/1.D-3QEMMZ.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{2.BnwnD1Ki.js → 2.4olHnH7U.js} +1 -1
- package/{dist/web/_app/immutable/nodes/3.0arZe_Uf.js → web/.svelte-kit/output/client/_app/immutable/nodes/3.4w0bE-m2.js} +3 -3
- package/web/.svelte-kit/output/client/_app/immutable/nodes/4.CZvjhVHt.js +60 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{5.Bq2JzCEj.js → 5.DLbPVJY2.js} +1 -1
- package/web/.svelte-kit/output/client/_app/version.json +1 -1
- package/web/.svelte-kit/output/server/.vite/manifest.json +12 -10
- package/web/.svelte-kit/output/server/chunks/Icon.js +1 -1
- package/web/.svelte-kit/output/server/chunks/client.js +1 -1
- package/web/.svelte-kit/output/server/chunks/exports.js +1 -1
- package/web/.svelte-kit/output/server/chunks/index-server.js +2 -1
- package/web/.svelte-kit/output/server/chunks/internal.js +1 -1
- package/web/.svelte-kit/output/server/chunks/render-context.js +77 -0
- package/web/.svelte-kit/output/server/chunks/root.js +739 -788
- package/web/.svelte-kit/output/server/chunks/shared.js +234 -21
- package/web/.svelte-kit/output/server/index.js +126 -90
- package/web/.svelte-kit/output/server/manifest-full.js +1 -1
- package/web/.svelte-kit/output/server/manifest.js +1 -1
- package/web/.svelte-kit/output/server/nodes/0.js +1 -1
- package/web/.svelte-kit/output/server/nodes/1.js +1 -1
- package/web/.svelte-kit/output/server/nodes/2.js +1 -1
- package/web/.svelte-kit/output/server/nodes/3.js +1 -1
- package/web/.svelte-kit/output/server/nodes/4.js +1 -1
- package/web/.svelte-kit/output/server/nodes/5.js +1 -1
- package/web/.svelte-kit/output/server/remote-entry.js +245 -81
- package/web/.svelte-kit/tsconfig.json +4 -1
- package/dist/cli/propose-policy.mjs.map +0 -1
- package/dist/lite-CBxOT1y5.mjs +0 -241
- package/dist/lite-CBxOT1y5.mjs.map +0 -1
- package/dist/routing-D8rTxtaV.mjs +0 -245
- package/dist/routing-D8rTxtaV.mjs.map +0 -1
- package/dist/web/_app/immutable/chunks/B6YN0Nuq.js +0 -1
- package/dist/web/_app/immutable/chunks/BmRlVmv6.js +0 -1
- package/dist/web/_app/immutable/chunks/CK9JZLaG.js +0 -2
- package/dist/web/_app/immutable/chunks/Ck3rYNON.js +0 -1
- package/dist/web/_app/immutable/chunks/D5iV40bG.js +0 -1
- package/dist/web/_app/immutable/chunks/DMtIqaiV.js +0 -2
- package/dist/web/_app/immutable/chunks/DhD271EB.js +0 -1
- package/dist/web/_app/immutable/chunks/DpuLqk8d.js +0 -1
- package/dist/web/_app/immutable/chunks/DsIToJCP.js +0 -1
- package/dist/web/_app/immutable/entry/app.BCSV3nrG.js +0 -2
- package/dist/web/_app/immutable/entry/start.D4eLEZUM.js +0 -1
- package/dist/web/_app/immutable/nodes/1.CGC_42IQ.js +0 -1
- package/dist/web/_app/immutable/nodes/4.ClM1bXLE.js +0 -60
- package/dist/workspace-BJmJBfKi.mjs +0 -456
- package/dist/workspace-BJmJBfKi.mjs.map +0 -1
- package/src/cli/e2e/agents.test.ts +0 -140
- package/src/cli/e2e/basic.test.ts +0 -43
- package/src/cli/e2e/cron.test.ts +0 -132
- package/src/cli/e2e/export-lite-func.test.ts +0 -206
- package/src/cli/e2e/fallbacks.test.ts +0 -175
- package/src/cli/e2e/init.test.ts +0 -77
- package/src/cli/e2e/messages.test.ts +0 -332
- package/src/cli/e2e/propose-policy.test.ts +0 -203
- package/src/cli/e2e/requests.test.ts +0 -180
- package/src/cli/e2e/session-timeout.test.ts +0 -192
- package/src/cli/e2e/slash-new.test.ts +0 -93
- package/src/cli/e2e/subagents.test.ts +0 -106
- package/src/cli/e2e/utils.ts +0 -66
- package/src/cli/propose-policy.ts +0 -91
- package/web/.svelte-kit/output/client/_app/immutable/chunks/B6YN0Nuq.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BmRlVmv6.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/CK9JZLaG.js +0 -2
- package/web/.svelte-kit/output/client/_app/immutable/chunks/Ck3rYNON.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/D5iV40bG.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DMtIqaiV.js +0 -2
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DhD271EB.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DpuLqk8d.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DsIToJCP.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/entry/app.BCSV3nrG.js +0 -2
- package/web/.svelte-kit/output/client/_app/immutable/entry/start.D4eLEZUM.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/1.CGC_42IQ.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/4.ClM1bXLE.js +0 -60
- package/web/.svelte-kit/output/server/chunks/false.js +0 -4
- /package/dist/cli/{propose-policy.d.mts → manage-policies.d.mts} +0 -0
- /package/{src/cli/e2e → e2e/_helpers}/global-setup.ts +0 -0
package/dist/cli/index.mjs
CHANGED
|
@@ -1,25 +1,35 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { D as pathIsInsideDir,
|
|
3
|
-
import {
|
|
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
|
|
22
|
-
console.log(`
|
|
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$
|
|
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$
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 = ["
|
|
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:
|
|
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);
|