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
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
recordInbound,
|
|
4
|
+
resolveInbound,
|
|
5
|
+
INBOUND_TTL_MS,
|
|
6
|
+
_resetInboundCacheForTests,
|
|
7
|
+
} from './inbound-cache.js';
|
|
8
|
+
|
|
9
|
+
describe('inbound-cache', () => {
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
_resetInboundCacheForTests();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
vi.useRealTimers();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('records and resolves an inbound by gchatMessageName', () => {
|
|
19
|
+
recordInbound({
|
|
20
|
+
gchatMessageName: 'spaces/x/messages/m1',
|
|
21
|
+
gchatThreadName: 'spaces/x/threads/t1',
|
|
22
|
+
});
|
|
23
|
+
const record = resolveInbound('spaces/x/messages/m1');
|
|
24
|
+
expect(record).toMatchObject({
|
|
25
|
+
gchatMessageName: 'spaces/x/messages/m1',
|
|
26
|
+
gchatThreadName: 'spaces/x/threads/t1',
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('returns null for unknown keys', () => {
|
|
31
|
+
expect(resolveInbound('spaces/x/messages/unknown')).toBeNull();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('expires entries older than INBOUND_TTL_MS on resolve', () => {
|
|
35
|
+
vi.useFakeTimers();
|
|
36
|
+
recordInbound({
|
|
37
|
+
gchatMessageName: 'spaces/x/messages/m1',
|
|
38
|
+
gchatThreadName: 'spaces/x/threads/t1',
|
|
39
|
+
});
|
|
40
|
+
expect(resolveInbound('spaces/x/messages/m1')).not.toBeNull();
|
|
41
|
+
|
|
42
|
+
vi.advanceTimersByTime(INBOUND_TTL_MS + 1000);
|
|
43
|
+
expect(resolveInbound('spaces/x/messages/m1')).toBeNull();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('sweeps expired entries on every insert', () => {
|
|
47
|
+
vi.useFakeTimers();
|
|
48
|
+
recordInbound({
|
|
49
|
+
gchatMessageName: 'spaces/x/messages/m1',
|
|
50
|
+
gchatThreadName: 'spaces/x/threads/t1',
|
|
51
|
+
});
|
|
52
|
+
vi.advanceTimersByTime(INBOUND_TTL_MS + 1000);
|
|
53
|
+
recordInbound({
|
|
54
|
+
gchatMessageName: 'spaces/x/messages/m2',
|
|
55
|
+
gchatThreadName: 'spaces/x/threads/t2',
|
|
56
|
+
});
|
|
57
|
+
// m1 should have been swept when m2 was inserted.
|
|
58
|
+
expect(resolveInbound('spaces/x/messages/m1')).toBeNull();
|
|
59
|
+
expect(resolveInbound('spaces/x/messages/m2')).not.toBeNull();
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Chat-side wrapper around the shared inbound-message TTL cache.
|
|
3
|
+
*
|
|
4
|
+
* Ingestion records each inbound by its `gchatMessageName` (also sent to the
|
|
5
|
+
* daemon as `externalRef`). When the forwarder later sees `turnStarted` with
|
|
6
|
+
* that `externalRef`, it resolves the thread anchor by looking up the same
|
|
7
|
+
* key here.
|
|
8
|
+
*/
|
|
9
|
+
import { createInboundCache } from '../shared/adapters/inbound-cache.js';
|
|
10
|
+
|
|
11
|
+
export const INBOUND_TTL_MS = 10 * 60 * 1000; // 10 minutes
|
|
12
|
+
|
|
13
|
+
interface GChatInboundValue {
|
|
14
|
+
gchatThreadName: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const cache = createInboundCache<GChatInboundValue>(INBOUND_TTL_MS);
|
|
18
|
+
|
|
19
|
+
export interface GChatInboundRecord {
|
|
20
|
+
gchatMessageName: string;
|
|
21
|
+
gchatThreadName: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function recordInbound(entry: GChatInboundRecord): void {
|
|
25
|
+
cache.record(entry.gchatMessageName, { gchatThreadName: entry.gchatThreadName });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function resolveInbound(gchatMessageName: string): GChatInboundRecord | null {
|
|
29
|
+
const value = cache.resolve(gchatMessageName);
|
|
30
|
+
return value ? { gchatMessageName, gchatThreadName: value.gchatThreadName } : null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Test hook: drop all cached records. */
|
|
34
|
+
export function _resetInboundCacheForTests(): void {
|
|
35
|
+
cache.reset();
|
|
36
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
1
2
|
import fsPromises from 'node:fs/promises';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import { z } from 'zod';
|
|
@@ -13,6 +14,7 @@ export const GoogleChatStateSchema = z.object({
|
|
|
13
14
|
subscriptionId: z.string().optional(),
|
|
14
15
|
expirationDate: z.string().optional(),
|
|
15
16
|
requireMention: z.boolean().optional(),
|
|
17
|
+
threadsDisabled: z.boolean().optional(),
|
|
16
18
|
})
|
|
17
19
|
)
|
|
18
20
|
.optional(),
|
|
@@ -74,7 +76,13 @@ export function updateGoogleChatState(
|
|
|
74
76
|
const statePath = getGoogleChatStatePath(startDir);
|
|
75
77
|
const dir = path.dirname(statePath);
|
|
76
78
|
await fsPromises.mkdir(dir, { recursive: true });
|
|
77
|
-
|
|
79
|
+
// Atomic write: a plain writeFile truncates then writes, so a
|
|
80
|
+
// concurrent reader (resolveSpaceForChat runs on every inbound) can
|
|
81
|
+
// observe an empty file and throw `JSON.parse("")`. rename(2) on the
|
|
82
|
+
// same filesystem is atomic, so readers always see old or new.
|
|
83
|
+
const tmpPath = `${statePath}.${process.pid}.${crypto.randomBytes(4).toString('hex')}.tmp`;
|
|
84
|
+
await fsPromises.writeFile(tmpPath, JSON.stringify(newState, null, 2), 'utf-8');
|
|
85
|
+
await fsPromises.rename(tmpPath, statePath);
|
|
78
86
|
resolve(newState);
|
|
79
87
|
} catch (err) {
|
|
80
88
|
console.error(`Failed to write Google Chat state:`, err);
|
|
@@ -9,11 +9,12 @@ export async function handleAddedToSpace(
|
|
|
9
9
|
spaceType: string | undefined,
|
|
10
10
|
targetChatId: string | null | undefined,
|
|
11
11
|
mappedChatId: string | null | undefined,
|
|
12
|
-
config: GoogleChatConfig
|
|
12
|
+
config: GoogleChatConfig,
|
|
13
|
+
startDir: string = process.cwd()
|
|
13
14
|
) {
|
|
14
15
|
if (spaceType !== 'DIRECT_MESSAGE') {
|
|
15
16
|
try {
|
|
16
|
-
const userAuthClient = await getUserAuthClient(config);
|
|
17
|
+
const userAuthClient = await getUserAuthClient(config, startDir);
|
|
17
18
|
const tokenResponse = await userAuthClient.getAccessToken();
|
|
18
19
|
const token = tokenResponse.token;
|
|
19
20
|
|
|
@@ -48,7 +49,7 @@ export async function handleAddedToSpace(
|
|
|
48
49
|
},
|
|
49
50
|
},
|
|
50
51
|
};
|
|
51
|
-
});
|
|
52
|
+
}, startDir);
|
|
52
53
|
console.log(`Created subscription ${subData.name} for space ${externalContextId}`);
|
|
53
54
|
} else {
|
|
54
55
|
const errText = await res.text();
|
|
@@ -79,12 +80,13 @@ export async function handleAddedToSpace(
|
|
|
79
80
|
export async function handleRemovedFromSpace(
|
|
80
81
|
externalContextId: string,
|
|
81
82
|
currentState: GoogleChatState,
|
|
82
|
-
config: GoogleChatConfig
|
|
83
|
+
config: GoogleChatConfig,
|
|
84
|
+
startDir: string = process.cwd()
|
|
83
85
|
) {
|
|
84
86
|
const subId = currentState.channelChatMap?.[externalContextId]?.subscriptionId;
|
|
85
87
|
if (subId) {
|
|
86
88
|
try {
|
|
87
|
-
const userAuthClient = await getUserAuthClient(config);
|
|
89
|
+
const userAuthClient = await getUserAuthClient(config, startDir);
|
|
88
90
|
const tokenResponse = await userAuthClient.getAccessToken();
|
|
89
91
|
const token = tokenResponse.token;
|
|
90
92
|
|
|
@@ -120,5 +122,5 @@ export async function handleRemovedFromSpace(
|
|
|
120
122
|
}
|
|
121
123
|
}
|
|
122
124
|
return { channelChatMap: map };
|
|
123
|
-
});
|
|
125
|
+
}, startDir);
|
|
124
126
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { resolveCompiledScript } from '../shared/lite.js';
|
|
5
|
+
import { getClawminiDir } from '../shared/workspace.js';
|
|
6
|
+
import { BUILTIN_POLICIES } from '../shared/policies.js';
|
|
7
|
+
|
|
8
|
+
const HASHBANG = '#!/usr/bin/env node\n';
|
|
9
|
+
|
|
10
|
+
function sha256(content: string): string {
|
|
11
|
+
return createHash('sha256').update(content).digest('hex');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function installBuiltinPolicies(dirPath = getClawminiDir()): Promise<void> {
|
|
15
|
+
const policyScriptsDir = path.join(dirPath, 'policy-scripts');
|
|
16
|
+
await fs.mkdir(policyScriptsDir, { recursive: true });
|
|
17
|
+
|
|
18
|
+
for (const name of Object.keys(BUILTIN_POLICIES)) {
|
|
19
|
+
try {
|
|
20
|
+
const sourcePath = await resolveCompiledScript(name, import.meta.url);
|
|
21
|
+
let scriptContent = await fs.readFile(sourcePath, 'utf8');
|
|
22
|
+
if (!scriptContent.startsWith('#!')) {
|
|
23
|
+
scriptContent = HASHBANG + scriptContent;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const destPath = path.join(policyScriptsDir, `${name}.js`);
|
|
27
|
+
let existing: string | null = null;
|
|
28
|
+
try {
|
|
29
|
+
existing = await fs.readFile(destPath, 'utf8');
|
|
30
|
+
} catch {
|
|
31
|
+
// missing — write below
|
|
32
|
+
}
|
|
33
|
+
if (existing !== null && sha256(existing) === sha256(scriptContent)) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
await fs.writeFile(destPath, scriptContent, { mode: 0o755 });
|
|
37
|
+
} catch (err) {
|
|
38
|
+
console.warn(
|
|
39
|
+
`Warning: Could not install built-in policy ${name}:`,
|
|
40
|
+
err instanceof Error ? err.message : String(err)
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -2,9 +2,13 @@ import { Command } from 'commander';
|
|
|
2
2
|
import {
|
|
3
3
|
listAgents,
|
|
4
4
|
getAgent,
|
|
5
|
+
getAgentOverlay,
|
|
5
6
|
writeAgentSettings,
|
|
6
7
|
deleteAgent,
|
|
7
8
|
isValidAgentId,
|
|
9
|
+
refreshAgentTemplate,
|
|
10
|
+
refreshAgentSkills,
|
|
11
|
+
formatPlanActions,
|
|
8
12
|
} from '../../shared/workspace.js';
|
|
9
13
|
import { type Agent } from '../../shared/config.js';
|
|
10
14
|
import { createAgentWithChat } from '../../shared/agent-utils.js';
|
|
@@ -57,11 +61,22 @@ agentsCmd
|
|
|
57
61
|
'-e, --env <env...>',
|
|
58
62
|
'Environment variables in KEY=VALUE format (can be specified multiple times)'
|
|
59
63
|
)
|
|
64
|
+
.option('--fork', 'Copy the template settings into the agent fully (legacy, no auto-update)')
|
|
65
|
+
.option('--force', 'Overwrite existing files in the target directory on first install')
|
|
60
66
|
.action(
|
|
61
|
-
async (
|
|
67
|
+
async (
|
|
68
|
+
id: string,
|
|
69
|
+
options: {
|
|
70
|
+
directory?: string;
|
|
71
|
+
template?: string;
|
|
72
|
+
env?: string[];
|
|
73
|
+
fork?: boolean;
|
|
74
|
+
force?: boolean;
|
|
75
|
+
}
|
|
76
|
+
) => {
|
|
62
77
|
try {
|
|
63
78
|
assertValidAgentId(id);
|
|
64
|
-
const existing = await
|
|
79
|
+
const existing = await getAgentOverlay(id);
|
|
65
80
|
if (existing) {
|
|
66
81
|
throw new Error(`Agent ${id} already exists.`);
|
|
67
82
|
}
|
|
@@ -76,7 +91,11 @@ agentsCmd
|
|
|
76
91
|
agentData.env = { ...(agentData.env || {}), ...env };
|
|
77
92
|
}
|
|
78
93
|
|
|
79
|
-
|
|
94
|
+
const applyOpts = {
|
|
95
|
+
...(options.fork ? { fork: true } : {}),
|
|
96
|
+
...(options.force ? { force: true } : {}),
|
|
97
|
+
};
|
|
98
|
+
await createAgentWithChat(id, agentData, options.template, process.cwd(), applyOpts);
|
|
80
99
|
|
|
81
100
|
console.log(`Agent ${id} created successfully.`);
|
|
82
101
|
} catch (err) {
|
|
@@ -96,7 +115,7 @@ agentsCmd
|
|
|
96
115
|
.action(async (id: string, options: { directory?: string; env?: string[] }) => {
|
|
97
116
|
try {
|
|
98
117
|
assertValidAgentId(id);
|
|
99
|
-
const existing = await
|
|
118
|
+
const existing = await getAgentOverlay(id);
|
|
100
119
|
if (!existing) {
|
|
101
120
|
throw new Error(`Agent ${id} does not exist.`);
|
|
102
121
|
}
|
|
@@ -125,7 +144,7 @@ agentsCmd
|
|
|
125
144
|
.action(async (id: string) => {
|
|
126
145
|
try {
|
|
127
146
|
assertValidAgentId(id);
|
|
128
|
-
const existing = await
|
|
147
|
+
const existing = await getAgentOverlay(id);
|
|
129
148
|
if (!existing) {
|
|
130
149
|
throw new Error(`Agent ${id} does not exist.`);
|
|
131
150
|
}
|
|
@@ -136,3 +155,38 @@ agentsCmd
|
|
|
136
155
|
handleError('delete agent', err);
|
|
137
156
|
}
|
|
138
157
|
});
|
|
158
|
+
|
|
159
|
+
agentsCmd
|
|
160
|
+
.command('refresh <id>')
|
|
161
|
+
.description("Refresh the agent's tracked template files against the installed clawmini")
|
|
162
|
+
.option('--accept', 'Overwrite files that have diverged from the recorded SHA')
|
|
163
|
+
.option('--dry-run', 'Print the per-file plan without writing anything')
|
|
164
|
+
.action(async (id: string, options: { accept?: boolean; dryRun?: boolean }) => {
|
|
165
|
+
try {
|
|
166
|
+
assertValidAgentId(id);
|
|
167
|
+
const overlay = await getAgentOverlay(id);
|
|
168
|
+
if (!overlay) {
|
|
169
|
+
throw new Error(`Agent ${id} does not exist.`);
|
|
170
|
+
}
|
|
171
|
+
const refreshOpts = {
|
|
172
|
+
...(options.accept === undefined ? {} : { accept: options.accept }),
|
|
173
|
+
...(options.dryRun === undefined ? {} : { dryRun: options.dryRun }),
|
|
174
|
+
};
|
|
175
|
+
if (overlay.extends) {
|
|
176
|
+
const plan = await refreshAgentTemplate(id, overlay, process.cwd(), refreshOpts);
|
|
177
|
+
if (plan) {
|
|
178
|
+
for (const line of formatPlanActions(plan)) console.log(line);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const resolved = await getAgent(id);
|
|
183
|
+
if (resolved) {
|
|
184
|
+
const skillsPlan = await refreshAgentSkills(id, resolved, process.cwd(), refreshOpts);
|
|
185
|
+
if (skillsPlan) {
|
|
186
|
+
for (const line of formatPlanActions(skillsPlan)) console.log(line);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
} catch (err) {
|
|
190
|
+
handleError('refresh agent', err);
|
|
191
|
+
}
|
|
192
|
+
});
|
package/src/cli/commands/down.ts
CHANGED
|
@@ -1,18 +1,70 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { getDaemonClient } from '../client.js';
|
|
3
3
|
import { getSocketPath } from '../../shared/workspace.js';
|
|
4
|
+
import { readSupervisorPid, removeSupervisorPid } from '../supervisor-pid.js';
|
|
4
5
|
import fs from 'node:fs';
|
|
5
6
|
|
|
7
|
+
function isErrnoCode(err: unknown, code: string): boolean {
|
|
8
|
+
return err instanceof Error && (err as NodeJS.ErrnoException).code === code;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async function stopSupervisor(pid: number): Promise<void> {
|
|
12
|
+
process.stdout.write(`Stopping clawmini supervisor (pid ${pid})`);
|
|
13
|
+
try {
|
|
14
|
+
process.kill(pid, 'SIGTERM');
|
|
15
|
+
} catch (err) {
|
|
16
|
+
if (isErrnoCode(err, 'ESRCH')) {
|
|
17
|
+
process.stdout.write('\nSupervisor already exited.\n');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
throw new Error(
|
|
21
|
+
`Failed to signal supervisor: ${err instanceof Error ? err.message : String(err)}`,
|
|
22
|
+
{ cause: err }
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Poll until the process is gone or we time out. Must exceed the
|
|
27
|
+
// supervisor's internal phase-2 timeout (60s for the daemon) so we don't
|
|
28
|
+
// bail while it's still draining `down` hooks.
|
|
29
|
+
const TIMEOUT_MS = 90_000;
|
|
30
|
+
const deadline = Date.now() + TIMEOUT_MS;
|
|
31
|
+
while (Date.now() < deadline) {
|
|
32
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
33
|
+
process.stdout.write('.');
|
|
34
|
+
try {
|
|
35
|
+
process.kill(pid, 0);
|
|
36
|
+
} catch {
|
|
37
|
+
process.stdout.write('\nSuccessfully shut down clawmini supervisor.\n');
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
throw new Error(`Supervisor did not exit within ${TIMEOUT_MS / 1000} seconds.`);
|
|
42
|
+
}
|
|
43
|
+
|
|
6
44
|
export const downCmd = new Command('down')
|
|
7
|
-
.description('Stop the local clawmini daemon
|
|
45
|
+
.description('Stop the local clawmini supervisor or daemon')
|
|
8
46
|
.action(async () => {
|
|
47
|
+
const supPid = readSupervisorPid();
|
|
48
|
+
if (supPid) {
|
|
49
|
+
try {
|
|
50
|
+
await stopSupervisor(supPid);
|
|
51
|
+
return;
|
|
52
|
+
} catch (err) {
|
|
53
|
+
console.error('\n', err instanceof Error ? err.message : String(err));
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// No live supervisor — drop any stale pid file so future commands
|
|
59
|
+
// aren't confused by it.
|
|
60
|
+
removeSupervisorPid();
|
|
61
|
+
|
|
9
62
|
try {
|
|
10
63
|
const client = await getDaemonClient({ autoStart: false });
|
|
11
64
|
process.stdout.write('Shutting down clawmini daemon...');
|
|
12
65
|
await client.shutdown.mutate();
|
|
13
66
|
|
|
14
67
|
const socketPath = getSocketPath();
|
|
15
|
-
// Wait for the socket file to be removed by the daemon's exit handler
|
|
16
68
|
while (fs.existsSync(socketPath)) {
|
|
17
69
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
18
70
|
process.stdout.write('.');
|
|
@@ -8,9 +8,15 @@ environmentsCmd
|
|
|
8
8
|
.command('enable <name>')
|
|
9
9
|
.description('Enable an environment for a path in the workspace')
|
|
10
10
|
.option('-p, --path <subpath>', 'Path to apply the environment to', './')
|
|
11
|
-
.
|
|
11
|
+
.option('--fork', 'Clone the built-in template directory instead of writing a thin overlay')
|
|
12
|
+
.action(async (name: string, options: { path: string; fork?: boolean }) => {
|
|
12
13
|
try {
|
|
13
|
-
await enableEnvironment(
|
|
14
|
+
await enableEnvironment(
|
|
15
|
+
name,
|
|
16
|
+
options.path,
|
|
17
|
+
process.cwd(),
|
|
18
|
+
options.fork ? { fork: true } : {}
|
|
19
|
+
);
|
|
14
20
|
} catch (err) {
|
|
15
21
|
handleError('enable environment', err);
|
|
16
22
|
}
|
package/src/cli/commands/init.ts
CHANGED
|
@@ -1,11 +1,29 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
4
5
|
import { isValidAgentId, enableEnvironment } from '../../shared/workspace.js';
|
|
5
6
|
import { setDefaultChatId } from '../../shared/chats.js';
|
|
6
7
|
import { type Agent } from '../../shared/config.js';
|
|
7
8
|
import { createAgentWithChat } from '../../shared/agent-utils.js';
|
|
8
9
|
import { handleError } from '../utils.js';
|
|
10
|
+
import { installBuiltinPolicies } from '../builtin-policies.js';
|
|
11
|
+
|
|
12
|
+
function readBackupGitignoreTemplate(): string | null {
|
|
13
|
+
let currentDir = path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
while (
|
|
15
|
+
currentDir !== path.parse(currentDir).root &&
|
|
16
|
+
!fs.existsSync(path.join(currentDir, 'package.json'))
|
|
17
|
+
) {
|
|
18
|
+
currentDir = path.dirname(currentDir);
|
|
19
|
+
}
|
|
20
|
+
const templatePath = path.join(currentDir, 'docs', 'backups', 'clawmini.gitignore');
|
|
21
|
+
try {
|
|
22
|
+
return fs.readFileSync(templatePath, 'utf8');
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
9
27
|
|
|
10
28
|
export const initCmd = new Command('init')
|
|
11
29
|
.description('Initialize a new .clawmini settings folder')
|
|
@@ -47,6 +65,19 @@ export const initCmd = new Command('init')
|
|
|
47
65
|
fs.writeFileSync(settingsPath, JSON.stringify(defaultSettings, null, 2));
|
|
48
66
|
console.log('Initialized .clawmini/settings.json');
|
|
49
67
|
|
|
68
|
+
const gitignoreTemplate = readBackupGitignoreTemplate();
|
|
69
|
+
if (gitignoreTemplate) {
|
|
70
|
+
const gitignorePath = path.join(dirPath, '.gitignore');
|
|
71
|
+
if (!fs.existsSync(gitignorePath)) {
|
|
72
|
+
fs.writeFileSync(gitignorePath, gitignoreTemplate);
|
|
73
|
+
console.log('Initialized .clawmini/.gitignore');
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
console.warn('Warning: backup .gitignore template not found; skipping .clawmini/.gitignore');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
await installBuiltinPolicies(dirPath);
|
|
80
|
+
|
|
50
81
|
if (options.agent) {
|
|
51
82
|
try {
|
|
52
83
|
const agentId = options.agent;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
import { getClawminiDir } from '../../shared/workspace.js';
|
|
6
|
+
|
|
7
|
+
const SERVICE_ALIASES: Record<string, string> = {
|
|
8
|
+
daemon: 'daemon',
|
|
9
|
+
web: 'web',
|
|
10
|
+
discord: 'adapter-discord',
|
|
11
|
+
'adapter-discord': 'adapter-discord',
|
|
12
|
+
'google-chat': 'adapter-google-chat',
|
|
13
|
+
'adapter-google-chat': 'adapter-google-chat',
|
|
14
|
+
supervisor: 'supervisor',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
function displayNameFor(logBase: string): string {
|
|
18
|
+
if (logBase.startsWith('adapter-')) return logBase.slice('adapter-'.length);
|
|
19
|
+
return logBase;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function tailString(content: string, n: number): string[] {
|
|
23
|
+
const lines = content.split('\n');
|
|
24
|
+
if (lines.length > 0 && lines[lines.length - 1] === '') lines.pop();
|
|
25
|
+
return lines.slice(-n);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface LogsOptions {
|
|
29
|
+
follow?: boolean;
|
|
30
|
+
service?: string;
|
|
31
|
+
lines?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const logsCmd = new Command('logs')
|
|
35
|
+
.description('View logs from clawmini services (daemon, web, adapters)')
|
|
36
|
+
.option('-f, --follow', 'Follow the logs as new lines are written')
|
|
37
|
+
.option(
|
|
38
|
+
'-s, --service <name>',
|
|
39
|
+
'Restrict to one service: daemon, web, discord, google-chat, supervisor'
|
|
40
|
+
)
|
|
41
|
+
.option('-n, --lines <count>', 'Number of lines to show from the tail of each file', '50')
|
|
42
|
+
.action(async (options: LogsOptions) => {
|
|
43
|
+
const logDir = path.join(getClawminiDir(), 'logs');
|
|
44
|
+
if (!fs.existsSync(logDir)) {
|
|
45
|
+
console.error(`No log directory at ${logDir}. Start the supervisor with 'clawmini serve'.`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let targets: string[];
|
|
50
|
+
if (options.service) {
|
|
51
|
+
const resolved = SERVICE_ALIASES[options.service];
|
|
52
|
+
if (!resolved) {
|
|
53
|
+
console.error(
|
|
54
|
+
`Unknown service '${options.service}'. Valid: ${Object.keys(SERVICE_ALIASES).join(', ')}`
|
|
55
|
+
);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
targets = [`${resolved}.log`];
|
|
59
|
+
} else {
|
|
60
|
+
targets = fs.readdirSync(logDir).filter((f) => f.endsWith('.log'));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const linesCount = Math.max(0, parseInt(options.lines ?? '50', 10) || 50);
|
|
64
|
+
|
|
65
|
+
for (const file of targets) {
|
|
66
|
+
const full = path.join(logDir, file);
|
|
67
|
+
if (!fs.existsSync(full)) continue;
|
|
68
|
+
const prefix = `[${displayNameFor(file.replace(/\.log$/, ''))}] `;
|
|
69
|
+
const content = fs.readFileSync(full, 'utf-8');
|
|
70
|
+
for (const line of tailString(content, linesCount)) {
|
|
71
|
+
process.stdout.write(prefix + line + '\n');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!options.follow) return;
|
|
76
|
+
|
|
77
|
+
const watched: Array<{ file: string; full: string; prefix: string; position: number }> = [];
|
|
78
|
+
for (const file of targets) {
|
|
79
|
+
const full = path.join(logDir, file);
|
|
80
|
+
const prefix = `[${displayNameFor(file.replace(/\.log$/, ''))}] `;
|
|
81
|
+
const position = fs.existsSync(full) ? fs.statSync(full).size : 0;
|
|
82
|
+
watched.push({ file, full, prefix, position });
|
|
83
|
+
fs.watchFile(full, { interval: 500 }, (curr) => {
|
|
84
|
+
const entry = watched.find((w) => w.full === full);
|
|
85
|
+
if (!entry) return;
|
|
86
|
+
if (curr.size < entry.position) {
|
|
87
|
+
entry.position = 0;
|
|
88
|
+
}
|
|
89
|
+
if (curr.size > entry.position) {
|
|
90
|
+
const fd = fs.openSync(full, 'r');
|
|
91
|
+
const buf = Buffer.alloc(curr.size - entry.position);
|
|
92
|
+
fs.readSync(fd, buf, 0, buf.length, entry.position);
|
|
93
|
+
fs.closeSync(fd);
|
|
94
|
+
entry.position = curr.size;
|
|
95
|
+
|
|
96
|
+
const text = buf.toString();
|
|
97
|
+
const lines = text.split('\n');
|
|
98
|
+
if (lines.length > 0 && lines[lines.length - 1] === '') lines.pop();
|
|
99
|
+
for (const line of lines) {
|
|
100
|
+
process.stdout.write(entry.prefix + line + '\n');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const stop = () => {
|
|
107
|
+
for (const entry of watched) fs.unwatchFile(entry.full);
|
|
108
|
+
process.exit(0);
|
|
109
|
+
};
|
|
110
|
+
process.on('SIGINT', stop);
|
|
111
|
+
process.on('SIGTERM', stop);
|
|
112
|
+
|
|
113
|
+
await new Promise<void>(() => {
|
|
114
|
+
/* intentionally unresolved; signals drive termination */
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -3,10 +3,10 @@ import fs from 'node:fs';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { handleError } from '../utils.js';
|
|
5
5
|
import { resolveCompiledScript } from '../../shared/lite.js';
|
|
6
|
-
import type
|
|
6
|
+
import { BUILTIN_POLICIES, type PolicyConfig } from '../../shared/policies.js';
|
|
7
7
|
import { getClawminiDir } from '../../shared/workspace.js';
|
|
8
8
|
|
|
9
|
-
const SUPPORTED_POLICIES = ['
|
|
9
|
+
const SUPPORTED_POLICIES = ['manage-policies'];
|
|
10
10
|
|
|
11
11
|
export const policiesCmd = new Command('policies').description('Manage sandbox policies');
|
|
12
12
|
|
|
@@ -44,10 +44,12 @@ policiesCmd
|
|
|
44
44
|
policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
const builtin = BUILTIN_POLICIES[name];
|
|
47
48
|
policies.policies[name] = {
|
|
48
|
-
description:
|
|
49
|
+
description: builtin?.description ?? `Built-in policy ${name}`,
|
|
49
50
|
command: `./.clawmini/policy-scripts/${name}.js`,
|
|
50
|
-
allowHelp: true,
|
|
51
|
+
allowHelp: builtin?.allowHelp ?? true,
|
|
52
|
+
...(builtin?.autoApprove !== undefined ? { autoApprove: builtin.autoApprove } : {}),
|
|
51
53
|
};
|
|
52
54
|
|
|
53
55
|
fs.writeFileSync(policiesPath, JSON.stringify(policies, null, 2));
|