clawmini 0.0.1
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/.gemini/settings.json +46 -0
- package/.prettierrc +7 -0
- package/GEMINI.md +11 -0
- package/README.md +137 -0
- package/dist/adapter-discord/index.d.mts +5 -0
- package/dist/adapter-discord/index.d.mts.map +1 -0
- package/dist/adapter-discord/index.mjs +456 -0
- package/dist/adapter-discord/index.mjs.map +1 -0
- package/dist/chats-DKgTeU7i.mjs +91 -0
- package/dist/chats-DKgTeU7i.mjs.map +1 -0
- package/dist/chats-Zd_HXDHx.mjs +29 -0
- package/dist/chats-Zd_HXDHx.mjs.map +1 -0
- package/dist/cli/index.d.mts +1 -0
- package/dist/cli/index.mjs +850 -0
- package/dist/cli/index.mjs.map +1 -0
- package/dist/cli/lite.d.mts +1 -0
- package/dist/cli/lite.mjs +4434 -0
- package/dist/cli/lite.mjs.map +1 -0
- package/dist/daemon/index.d.mts +5 -0
- package/dist/daemon/index.d.mts.map +1 -0
- package/dist/daemon/index.mjs +1222 -0
- package/dist/daemon/index.mjs.map +1 -0
- package/dist/fetch-BjZVyU3Z.mjs +37 -0
- package/dist/fetch-BjZVyU3Z.mjs.map +1 -0
- package/dist/fs-B5wW0oaH.mjs +14 -0
- package/dist/fs-B5wW0oaH.mjs.map +1 -0
- package/dist/lite-Dl7WXyaH.mjs +80 -0
- package/dist/lite-Dl7WXyaH.mjs.map +1 -0
- package/dist/rolldown-runtime-95iHPtFO.mjs +18 -0
- package/dist/web/_app/env.js +1 -0
- package/dist/web/_app/immutable/assets/0.GI4C4dpV.css +1 -0
- package/dist/web/_app/immutable/chunks/B5abRDXp.js +1 -0
- package/dist/web/_app/immutable/chunks/B8yYFADm.js +1 -0
- package/dist/web/_app/immutable/chunks/BPy8HLo7.js +5 -0
- package/dist/web/_app/immutable/chunks/Bi0jeV7Q.js +1 -0
- package/dist/web/_app/immutable/chunks/BmUXQ3wy.js +2 -0
- package/dist/web/_app/immutable/chunks/C3k55nDF.js +1 -0
- package/dist/web/_app/immutable/chunks/COekwvP2.js +1 -0
- package/dist/web/_app/immutable/chunks/CSvS_NwK.js +1 -0
- package/dist/web/_app/immutable/chunks/CpaGRn9L.js +1 -0
- package/dist/web/_app/immutable/chunks/CyNaE55B.js +1 -0
- package/dist/web/_app/immutable/chunks/DG5RZBw-.js +2 -0
- package/dist/web/_app/immutable/chunks/Dc-UOHw9.js +1 -0
- package/dist/web/_app/immutable/chunks/DcrmIfTj.js +1 -0
- package/dist/web/_app/immutable/chunks/ZkLyk0mE.js +1 -0
- package/dist/web/_app/immutable/entry/app.B-vZe7PN.js +2 -0
- package/dist/web/_app/immutable/entry/start.oP1AgKhs.js +1 -0
- package/dist/web/_app/immutable/nodes/0.B5WFN0zw.js +1 -0
- package/dist/web/_app/immutable/nodes/1.D1wtJb2k.js +1 -0
- package/dist/web/_app/immutable/nodes/2.CK3CLC0f.js +1 -0
- package/dist/web/_app/immutable/nodes/3.BB5wCoBf.js +4 -0
- package/dist/web/_app/immutable/nodes/4.Dr2jvAXK.js +1 -0
- package/dist/web/_app/immutable/nodes/5.BJl7oM3b.js +1 -0
- package/dist/web/_app/version.json +1 -0
- package/dist/web/index.html +37 -0
- package/dist/web/robots.txt +3 -0
- package/dist/workspace-CSgfo_2J.mjs +383 -0
- package/dist/workspace-CSgfo_2J.mjs.map +1 -0
- package/docs/01_chats/development_log.md +36 -0
- package/docs/01_chats/notes.md +27 -0
- package/docs/01_chats/prd.md +47 -0
- package/docs/01_chats/questions.md +19 -0
- package/docs/01_chats/tickets.md +67 -0
- package/docs/02_sessions/development_log.md +79 -0
- package/docs/02_sessions/notes.md +40 -0
- package/docs/02_sessions/prd.md +75 -0
- package/docs/02_sessions/questions.md +7 -0
- package/docs/02_sessions/tickets.md +68 -0
- package/docs/03_web_interface/development_log.md +60 -0
- package/docs/03_web_interface/notes.md +29 -0
- package/docs/03_web_interface/prd.md +42 -0
- package/docs/03_web_interface/questions.md +8 -0
- package/docs/03_web_interface/tickets.md +59 -0
- package/docs/04_agents/development_log.md +54 -0
- package/docs/04_agents/notes.md +45 -0
- package/docs/04_agents/prd.md +47 -0
- package/docs/04_agents/questions.md +13 -0
- package/docs/04_agents/tickets.md +107 -0
- package/docs/05_routers/development_log.md +13 -0
- package/docs/05_routers/notes.md +40 -0
- package/docs/05_routers/prd.md +55 -0
- package/docs/05_routers/questions.md +21 -0
- package/docs/05_routers/tickets.md +109 -0
- package/docs/06_agent_templates/development_log.md +38 -0
- package/docs/06_agent_templates/notes.md +25 -0
- package/docs/06_agent_templates/prd.md +34 -0
- package/docs/06_agent_templates/questions.md +11 -0
- package/docs/06_agent_templates/tickets.md +49 -0
- package/docs/06_cron/development_log.md +51 -0
- package/docs/06_cron/notes.md +14 -0
- package/docs/06_cron/prd.md +92 -0
- package/docs/06_cron/questions.md +15 -0
- package/docs/06_cron/tickets.md +75 -0
- package/docs/07_web_chat_ux/development_log.md +30 -0
- package/docs/07_web_chat_ux/notes.md +25 -0
- package/docs/07_web_chat_ux/prd.md +46 -0
- package/docs/07_web_chat_ux/questions.md +7 -0
- package/docs/07_web_chat_ux/tickets.md +48 -0
- package/docs/08_agent_api/development_log.md +52 -0
- package/docs/08_agent_api/notes.md +31 -0
- package/docs/08_agent_api/prd.md +56 -0
- package/docs/08_agent_api/questions.md +14 -0
- package/docs/08_agent_api/tickets.md +104 -0
- package/docs/09_agent_fallbacks/development_log.md +52 -0
- package/docs/09_agent_fallbacks/notes.md +40 -0
- package/docs/09_agent_fallbacks/prd.md +55 -0
- package/docs/09_agent_fallbacks/questions.md +10 -0
- package/docs/09_agent_fallbacks/tickets.md +88 -0
- package/docs/09_discord_adapter/development_log.md +95 -0
- package/docs/09_discord_adapter/notes.md +18 -0
- package/docs/09_discord_adapter/prd.md +57 -0
- package/docs/09_discord_adapter/questions.md +16 -0
- package/docs/09_discord_adapter/tickets.md +116 -0
- package/docs/10_file_attachments/development_log.md +55 -0
- package/docs/10_file_attachments/notes.md +59 -0
- package/docs/10_file_attachments/prd.md +73 -0
- package/docs/10_file_attachments/questions.md +15 -0
- package/docs/10_file_attachments/tickets.md +88 -0
- package/docs/11_message_verbosity/development_log.md +43 -0
- package/docs/11_message_verbosity/notes.md +26 -0
- package/docs/11_message_verbosity/prd.md +44 -0
- package/docs/11_message_verbosity/questions.md +8 -0
- package/docs/11_message_verbosity/tickets.md +33 -0
- package/docs/12_environments/development_log.md +43 -0
- package/docs/12_environments/notes.md +45 -0
- package/docs/12_environments/prd.md +113 -0
- package/docs/12_environments/questions.md +17 -0
- package/docs/12_environments/tickets.md +87 -0
- package/docs/12_setup_flow_improvements/development_log.md +40 -0
- package/docs/12_setup_flow_improvements/notes.md +34 -0
- package/docs/12_setup_flow_improvements/prd.md +35 -0
- package/docs/12_setup_flow_improvements/questions.md +8 -0
- package/docs/12_setup_flow_improvements/tickets.md +122 -0
- package/docs/13_discord_typing_indicators/development_log.md +38 -0
- package/docs/13_discord_typing_indicators/notes.md +18 -0
- package/docs/13_discord_typing_indicators/prd.md +41 -0
- package/docs/13_discord_typing_indicators/questions.md +6 -0
- package/docs/13_discord_typing_indicators/tickets.md +60 -0
- package/docs/14_interruptions/development_log.md +50 -0
- package/docs/14_interruptions/notes.md +38 -0
- package/docs/14_interruptions/prd.md +46 -0
- package/docs/14_interruptions/questions.md +12 -0
- package/docs/14_interruptions/tickets.md +69 -0
- package/docs/15_sandbox_policies/development_log.md +95 -0
- package/docs/15_sandbox_policies/notes.md +33 -0
- package/docs/15_sandbox_policies/prd.md +163 -0
- package/docs/15_sandbox_policies/questions.md +10 -0
- package/docs/15_sandbox_policies/tickets.md +196 -0
- package/docs/CHECKS.md +9 -0
- package/docs/guides/discord_adapter_setup.md +69 -0
- package/docs/guides/sandbox_policies.md +76 -0
- package/eslint.config.js +47 -0
- package/napkin.md +21 -0
- package/package.json +50 -0
- package/scripts/create_worktree.sh +49 -0
- package/scripts/get_pr_comments.sh +36 -0
- package/src/adapter-discord/client.test.ts +65 -0
- package/src/adapter-discord/client.ts +41 -0
- package/src/adapter-discord/config.test.ts +156 -0
- package/src/adapter-discord/config.ts +61 -0
- package/src/adapter-discord/forwarder.test.ts +493 -0
- package/src/adapter-discord/forwarder.ts +246 -0
- package/src/adapter-discord/index.test.ts +399 -0
- package/src/adapter-discord/index.ts +147 -0
- package/src/adapter-discord/state.test.ts +65 -0
- package/src/adapter-discord/state.ts +44 -0
- package/src/cli/client.ts +46 -0
- package/src/cli/commands/agents.ts +138 -0
- package/src/cli/commands/chats.ts +79 -0
- package/src/cli/commands/down.ts +32 -0
- package/src/cli/commands/environments.ts +39 -0
- package/src/cli/commands/export-lite.ts +62 -0
- package/src/cli/commands/init.ts +79 -0
- package/src/cli/commands/jobs.ts +141 -0
- package/src/cli/commands/messages.ts +103 -0
- package/src/cli/commands/up.ts +26 -0
- package/src/cli/commands/web-api/agents.ts +138 -0
- package/src/cli/commands/web-api/chats.ts +213 -0
- package/src/cli/commands/web-api/utils.ts +27 -0
- package/src/cli/commands/web.ts +105 -0
- package/src/cli/e2e/adapter-discord.test.ts +76 -0
- package/src/cli/e2e/agents.test.ts +140 -0
- package/src/cli/e2e/basic.test.ts +43 -0
- package/src/cli/e2e/cron.test.ts +132 -0
- package/src/cli/e2e/daemon.test.ts +293 -0
- package/src/cli/e2e/environments.test.ts +66 -0
- package/src/cli/e2e/export-lite-func.test.ts +155 -0
- package/src/cli/e2e/export-lite.test.ts +51 -0
- package/src/cli/e2e/fallbacks.test.ts +169 -0
- package/src/cli/e2e/global-setup.ts +15 -0
- package/src/cli/e2e/init.test.ts +70 -0
- package/src/cli/e2e/messages.test.ts +294 -0
- package/src/cli/e2e/requests.test.ts +165 -0
- package/src/cli/e2e/utils.ts +66 -0
- package/src/cli/index.test.ts +7 -0
- package/src/cli/index.ts +29 -0
- package/src/cli/lite.ts +247 -0
- package/src/cli/utils.ts +4 -0
- package/src/daemon/auth.test.ts +50 -0
- package/src/daemon/auth.ts +69 -0
- package/src/daemon/chats.ts +26 -0
- package/src/daemon/cron.test.ts +28 -0
- package/src/daemon/cron.ts +159 -0
- package/src/daemon/events.ts +15 -0
- package/src/daemon/index.ts +212 -0
- package/src/daemon/message-agent.test.ts +132 -0
- package/src/daemon/message-extraction.test.ts +166 -0
- package/src/daemon/message-fallbacks.test.ts +313 -0
- package/src/daemon/message-interruption.test.ts +125 -0
- package/src/daemon/message-queue.test.ts +143 -0
- package/src/daemon/message-router.test.ts +106 -0
- package/src/daemon/message-session.test.ts +127 -0
- package/src/daemon/message-test-utils.ts +41 -0
- package/src/daemon/message-typing.test.ts +93 -0
- package/src/daemon/message-verbosity.test.ts +127 -0
- package/src/daemon/message.ts +600 -0
- package/src/daemon/observation.test.ts +118 -0
- package/src/daemon/policy-request-service.test.ts +87 -0
- package/src/daemon/policy-request-service.ts +62 -0
- package/src/daemon/policy-utils.test.ts +138 -0
- package/src/daemon/policy-utils.ts +152 -0
- package/src/daemon/queue.test.ts +89 -0
- package/src/daemon/queue.ts +87 -0
- package/src/daemon/request-store.test.ts +103 -0
- package/src/daemon/request-store.ts +96 -0
- package/src/daemon/router-policy-request.test.ts +99 -0
- package/src/daemon/router.test.ts +380 -0
- package/src/daemon/router.ts +510 -0
- package/src/daemon/routers/slash-command.test.ts +145 -0
- package/src/daemon/routers/slash-command.ts +58 -0
- package/src/daemon/routers/slash-interrupt.test.ts +30 -0
- package/src/daemon/routers/slash-interrupt.ts +7 -0
- package/src/daemon/routers/slash-new.test.ts +59 -0
- package/src/daemon/routers/slash-new.ts +14 -0
- package/src/daemon/routers/slash-policies.test.ts +167 -0
- package/src/daemon/routers/slash-policies.ts +131 -0
- package/src/daemon/routers/slash-stop.test.ts +30 -0
- package/src/daemon/routers/slash-stop.ts +3 -0
- package/src/daemon/routers/types.ts +10 -0
- package/src/daemon/routers/utils.ts +22 -0
- package/src/daemon/routers.test.ts +141 -0
- package/src/daemon/routers.ts +115 -0
- package/src/daemon/utils/spawn.ts +61 -0
- package/src/shared/agent-utils.ts +30 -0
- package/src/shared/chats.test.ts +112 -0
- package/src/shared/chats.ts +164 -0
- package/src/shared/config.test.ts +90 -0
- package/src/shared/config.ts +100 -0
- package/src/shared/event-source.ts +121 -0
- package/src/shared/fetch.ts +45 -0
- package/src/shared/lite.ts +129 -0
- package/src/shared/policies.ts +24 -0
- package/src/shared/utils/env.ts +27 -0
- package/src/shared/utils/fs.ts +13 -0
- package/src/shared/workspace.test.ts +345 -0
- package/src/shared/workspace.ts +500 -0
- package/templates/environments/cladding/env.json +7 -0
- package/templates/environments/macos/env.json +8 -0
- package/templates/environments/macos/sandbox.sb +21 -0
- package/templates/environments/macos-proxy/allowlist.txt +1 -0
- package/templates/environments/macos-proxy/env.json +14 -0
- package/templates/environments/macos-proxy/proxy.mjs +86 -0
- package/templates/environments/macos-proxy/sandbox.sb +34 -0
- package/templates/gemini/settings.json +11 -0
- package/templates/gemini-claw/.gemini/hooks/clawmini-logging.sh +17 -0
- package/templates/gemini-claw/.gemini/settings.json +24 -0
- package/templates/gemini-claw/.gemini/skills/clawmini-jobs/SKILL.md +40 -0
- package/templates/gemini-claw/.gemini/system.md +98 -0
- package/templates/gemini-claw/BOOTSTRAP.md +54 -0
- package/templates/gemini-claw/GEMINI.md +107 -0
- package/templates/gemini-claw/HEARTBEAT.md +3 -0
- package/templates/gemini-claw/MEMORY.md +2 -0
- package/templates/gemini-claw/SOUL.md +42 -0
- package/templates/gemini-claw/TOOLS.md +38 -0
- package/templates/gemini-claw/USER.md +15 -0
- package/templates/gemini-claw/memory/.gitkeep +0 -0
- package/templates/gemini-claw/settings.json +24 -0
- package/templates/opencode/settings.json +11 -0
- package/tsconfig.json +42 -0
- package/tsdown.config.ts +19 -0
- package/vitest.config.ts +9 -0
- package/web/.svelte-kit/ambient.d.ts +382 -0
- package/web/.svelte-kit/generated/client/app.js +35 -0
- package/web/.svelte-kit/generated/client/matchers.js +1 -0
- package/web/.svelte-kit/generated/client/nodes/0.js +3 -0
- package/web/.svelte-kit/generated/client/nodes/1.js +1 -0
- package/web/.svelte-kit/generated/client/nodes/2.js +1 -0
- package/web/.svelte-kit/generated/client/nodes/3.js +1 -0
- package/web/.svelte-kit/generated/client/nodes/4.js +3 -0
- package/web/.svelte-kit/generated/client/nodes/5.js +3 -0
- package/web/.svelte-kit/generated/client-optimized/app.js +35 -0
- package/web/.svelte-kit/generated/client-optimized/matchers.js +1 -0
- package/web/.svelte-kit/generated/client-optimized/nodes/0.js +3 -0
- package/web/.svelte-kit/generated/client-optimized/nodes/1.js +1 -0
- package/web/.svelte-kit/generated/client-optimized/nodes/2.js +1 -0
- package/web/.svelte-kit/generated/client-optimized/nodes/3.js +1 -0
- package/web/.svelte-kit/generated/client-optimized/nodes/4.js +3 -0
- package/web/.svelte-kit/generated/client-optimized/nodes/5.js +3 -0
- package/web/.svelte-kit/generated/root.js +3 -0
- package/web/.svelte-kit/generated/root.svelte +68 -0
- package/web/.svelte-kit/generated/server/internal.js +53 -0
- package/web/.svelte-kit/non-ambient.d.ts +46 -0
- package/web/.svelte-kit/output/client/.vite/manifest.json +251 -0
- package/web/.svelte-kit/output/client/_app/immutable/assets/0.GI4C4dpV.css +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/B5abRDXp.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/B8yYFADm.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BPy8HLo7.js +5 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/Bi0jeV7Q.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BmUXQ3wy.js +2 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/C3k55nDF.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/COekwvP2.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/CSvS_NwK.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/CpaGRn9L.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/CyNaE55B.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DG5RZBw-.js +2 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/Dc-UOHw9.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DcrmIfTj.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/ZkLyk0mE.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/entry/app.B-vZe7PN.js +2 -0
- package/web/.svelte-kit/output/client/_app/immutable/entry/start.oP1AgKhs.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/0.B5WFN0zw.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/1.D1wtJb2k.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/2.CK3CLC0f.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/3.BB5wCoBf.js +4 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/4.Dr2jvAXK.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/5.BJl7oM3b.js +1 -0
- package/web/.svelte-kit/output/client/_app/version.json +1 -0
- package/web/.svelte-kit/output/client/robots.txt +3 -0
- package/web/.svelte-kit/output/prerendered/dependencies/_app/env.js +1 -0
- package/web/.svelte-kit/output/server/.vite/manifest.json +215 -0
- package/web/.svelte-kit/output/server/_app/immutable/assets/_layout.GI4C4dpV.css +1 -0
- package/web/.svelte-kit/output/server/chunks/Icon.js +153 -0
- package/web/.svelte-kit/output/server/chunks/bot.js +2753 -0
- package/web/.svelte-kit/output/server/chunks/client.js +47 -0
- package/web/.svelte-kit/output/server/chunks/environment.js +34 -0
- package/web/.svelte-kit/output/server/chunks/exports.js +231 -0
- package/web/.svelte-kit/output/server/chunks/false.js +4 -0
- package/web/.svelte-kit/output/server/chunks/index-server.js +20 -0
- package/web/.svelte-kit/output/server/chunks/index.js +24 -0
- package/web/.svelte-kit/output/server/chunks/internal.js +133 -0
- package/web/.svelte-kit/output/server/chunks/plus.js +81 -0
- package/web/.svelte-kit/output/server/chunks/root.js +4076 -0
- package/web/.svelte-kit/output/server/chunks/shared.js +789 -0
- package/web/.svelte-kit/output/server/chunks/utils.js +43 -0
- package/web/.svelte-kit/output/server/entries/fallbacks/error.svelte.js +11 -0
- package/web/.svelte-kit/output/server/entries/pages/_layout.svelte.js +3944 -0
- package/web/.svelte-kit/output/server/entries/pages/_layout.ts.js +28 -0
- package/web/.svelte-kit/output/server/entries/pages/_page.svelte.js +7 -0
- package/web/.svelte-kit/output/server/entries/pages/agents/_page.svelte.js +379 -0
- package/web/.svelte-kit/output/server/entries/pages/chats/_id_/_page.svelte.js +292 -0
- package/web/.svelte-kit/output/server/entries/pages/chats/_id_/_page.ts.js +17 -0
- package/web/.svelte-kit/output/server/entries/pages/chats/_id_/settings/_page.svelte.js +259 -0
- package/web/.svelte-kit/output/server/entries/pages/chats/_id_/settings/_page.ts.js +17 -0
- package/web/.svelte-kit/output/server/index.js +3748 -0
- package/web/.svelte-kit/output/server/internal.js +14 -0
- package/web/.svelte-kit/output/server/manifest-full.js +63 -0
- package/web/.svelte-kit/output/server/manifest.js +63 -0
- package/web/.svelte-kit/output/server/nodes/0.js +13 -0
- package/web/.svelte-kit/output/server/nodes/1.js +8 -0
- package/web/.svelte-kit/output/server/nodes/2.js +8 -0
- package/web/.svelte-kit/output/server/nodes/3.js +8 -0
- package/web/.svelte-kit/output/server/nodes/4.js +13 -0
- package/web/.svelte-kit/output/server/nodes/5.js +13 -0
- package/web/.svelte-kit/output/server/remote-entry.js +557 -0
- package/web/.svelte-kit/tsconfig.json +67 -0
- package/web/.svelte-kit/types/route_meta_data.json +17 -0
- package/web/.svelte-kit/types/src/routes/$types.d.ts +26 -0
- package/web/.svelte-kit/types/src/routes/agents/$types.d.ts +18 -0
- package/web/.svelte-kit/types/src/routes/chats/[id]/$types.d.ts +21 -0
- package/web/.svelte-kit/types/src/routes/chats/[id]/proxy+page.ts +20 -0
- package/web/.svelte-kit/types/src/routes/chats/[id]/settings/$types.d.ts +21 -0
- package/web/.svelte-kit/types/src/routes/chats/[id]/settings/proxy+page.ts +19 -0
- package/web/.svelte-kit/types/src/routes/proxy+layout.ts +35 -0
- package/web/README.md +42 -0
- package/web/components.json +16 -0
- package/web/package.json +41 -0
- package/web/src/app.css +121 -0
- package/web/src/app.d.ts +13 -0
- package/web/src/app.html +11 -0
- package/web/src/demo.spec.ts +7 -0
- package/web/src/lib/app-state.svelte.ts +3 -0
- package/web/src/lib/assets/favicon.svg +1 -0
- package/web/src/lib/components/app/app-sidebar-test-wrapper.svelte +10 -0
- package/web/src/lib/components/app/app-sidebar.svelte +171 -0
- package/web/src/lib/components/app/app-sidebar.svelte.spec.ts +13 -0
- package/web/src/lib/components/ui/button/button.svelte +82 -0
- package/web/src/lib/components/ui/button/index.ts +17 -0
- package/web/src/lib/components/ui/dialog/dialog-close.svelte +7 -0
- package/web/src/lib/components/ui/dialog/dialog-content.svelte +45 -0
- package/web/src/lib/components/ui/dialog/dialog-description.svelte +17 -0
- package/web/src/lib/components/ui/dialog/dialog-footer.svelte +20 -0
- package/web/src/lib/components/ui/dialog/dialog-header.svelte +20 -0
- package/web/src/lib/components/ui/dialog/dialog-overlay.svelte +20 -0
- package/web/src/lib/components/ui/dialog/dialog-portal.svelte +7 -0
- package/web/src/lib/components/ui/dialog/dialog-title.svelte +17 -0
- package/web/src/lib/components/ui/dialog/dialog-trigger.svelte +7 -0
- package/web/src/lib/components/ui/dialog/dialog.svelte +7 -0
- package/web/src/lib/components/ui/dialog/index.ts +34 -0
- package/web/src/lib/components/ui/input/index.ts +7 -0
- package/web/src/lib/components/ui/input/input.svelte +52 -0
- package/web/src/lib/components/ui/separator/index.ts +7 -0
- package/web/src/lib/components/ui/separator/separator.svelte +21 -0
- package/web/src/lib/components/ui/sheet/index.ts +34 -0
- package/web/src/lib/components/ui/sheet/sheet-close.svelte +7 -0
- package/web/src/lib/components/ui/sheet/sheet-content.svelte +60 -0
- package/web/src/lib/components/ui/sheet/sheet-description.svelte +17 -0
- package/web/src/lib/components/ui/sheet/sheet-footer.svelte +20 -0
- package/web/src/lib/components/ui/sheet/sheet-header.svelte +20 -0
- package/web/src/lib/components/ui/sheet/sheet-overlay.svelte +20 -0
- package/web/src/lib/components/ui/sheet/sheet-portal.svelte +7 -0
- package/web/src/lib/components/ui/sheet/sheet-title.svelte +17 -0
- package/web/src/lib/components/ui/sheet/sheet-trigger.svelte +7 -0
- package/web/src/lib/components/ui/sheet/sheet.svelte +7 -0
- package/web/src/lib/components/ui/sidebar/constants.ts +6 -0
- package/web/src/lib/components/ui/sidebar/context.svelte.ts +79 -0
- package/web/src/lib/components/ui/sidebar/index.ts +75 -0
- package/web/src/lib/components/ui/sidebar/sidebar-content.svelte +24 -0
- package/web/src/lib/components/ui/sidebar/sidebar-footer.svelte +21 -0
- package/web/src/lib/components/ui/sidebar/sidebar-group-action.svelte +36 -0
- package/web/src/lib/components/ui/sidebar/sidebar-group-content.svelte +21 -0
- package/web/src/lib/components/ui/sidebar/sidebar-group-label.svelte +34 -0
- package/web/src/lib/components/ui/sidebar/sidebar-group.svelte +21 -0
- package/web/src/lib/components/ui/sidebar/sidebar-header.svelte +21 -0
- package/web/src/lib/components/ui/sidebar/sidebar-input.svelte +21 -0
- package/web/src/lib/components/ui/sidebar/sidebar-inset.svelte +24 -0
- package/web/src/lib/components/ui/sidebar/sidebar-menu-action.svelte +43 -0
- package/web/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte +29 -0
- package/web/src/lib/components/ui/sidebar/sidebar-menu-button.svelte +103 -0
- package/web/src/lib/components/ui/sidebar/sidebar-menu-item.svelte +21 -0
- package/web/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte +36 -0
- package/web/src/lib/components/ui/sidebar/sidebar-menu-sub-button.svelte +43 -0
- package/web/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte +21 -0
- package/web/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte +25 -0
- package/web/src/lib/components/ui/sidebar/sidebar-menu.svelte +21 -0
- package/web/src/lib/components/ui/sidebar/sidebar-provider.svelte +53 -0
- package/web/src/lib/components/ui/sidebar/sidebar-rail.svelte +36 -0
- package/web/src/lib/components/ui/sidebar/sidebar-separator.svelte +19 -0
- package/web/src/lib/components/ui/sidebar/sidebar-trigger.svelte +35 -0
- package/web/src/lib/components/ui/sidebar/sidebar.svelte +104 -0
- package/web/src/lib/components/ui/skeleton/index.ts +7 -0
- package/web/src/lib/components/ui/skeleton/skeleton.svelte +17 -0
- package/web/src/lib/components/ui/switch/index.ts +7 -0
- package/web/src/lib/components/ui/switch/switch.svelte +29 -0
- package/web/src/lib/components/ui/textarea/index.ts +7 -0
- package/web/src/lib/components/ui/textarea/textarea.svelte +23 -0
- package/web/src/lib/components/ui/tooltip/index.ts +19 -0
- package/web/src/lib/components/ui/tooltip/tooltip-content.svelte +52 -0
- package/web/src/lib/components/ui/tooltip/tooltip-portal.svelte +7 -0
- package/web/src/lib/components/ui/tooltip/tooltip-provider.svelte +7 -0
- package/web/src/lib/components/ui/tooltip/tooltip-trigger.svelte +7 -0
- package/web/src/lib/components/ui/tooltip/tooltip.svelte +7 -0
- package/web/src/lib/hooks/is-mobile.svelte.ts +9 -0
- package/web/src/lib/index.ts +1 -0
- package/web/src/lib/types.ts +23 -0
- package/web/src/lib/utils.ts +13 -0
- package/web/src/routes/+layout.svelte +67 -0
- package/web/src/routes/+layout.ts +34 -0
- package/web/src/routes/+page.svelte +7 -0
- package/web/src/routes/agents/+page.svelte +206 -0
- package/web/src/routes/chats/[id]/+page.svelte +406 -0
- package/web/src/routes/chats/[id]/+page.ts +19 -0
- package/web/src/routes/chats/[id]/page.svelte.spec.ts +102 -0
- package/web/src/routes/chats/[id]/settings/+page.svelte +165 -0
- package/web/src/routes/chats/[id]/settings/+page.ts +18 -0
- package/web/src/routes/page.svelte.spec.ts +13 -0
- package/web/static/robots.txt +3 -0
- package/web/svelte.config.js +21 -0
- package/web/tsconfig.json +20 -0
- package/web/vite.config.ts +41 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-95iHPtFO.mjs";
|
|
2
|
+
import { n as pathIsInsideDir } from "./fs-B5wW0oaH.mjs";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { execSync } from "node:child_process";
|
|
6
|
+
import fs$1 from "node:fs/promises";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
|
|
10
|
+
//#region src/shared/config.ts
|
|
11
|
+
const FallbackSchema = z.looseObject({
|
|
12
|
+
commands: z.looseObject({
|
|
13
|
+
new: z.string().optional(),
|
|
14
|
+
append: z.string().optional(),
|
|
15
|
+
getSessionId: z.string().optional(),
|
|
16
|
+
getMessageContent: z.string().optional()
|
|
17
|
+
}).optional(),
|
|
18
|
+
env: z.record(z.string(), z.union([z.string(), z.boolean()])).optional(),
|
|
19
|
+
retries: z.number().int().min(0).default(1),
|
|
20
|
+
delayMs: z.number().int().min(0).default(1e3)
|
|
21
|
+
});
|
|
22
|
+
const AgentSchema = z.looseObject({
|
|
23
|
+
commands: z.looseObject({
|
|
24
|
+
new: z.string().optional(),
|
|
25
|
+
append: z.string().optional(),
|
|
26
|
+
getSessionId: z.string().optional(),
|
|
27
|
+
getMessageContent: z.string().optional()
|
|
28
|
+
}).optional(),
|
|
29
|
+
env: z.record(z.string(), z.union([z.string(), z.boolean()])).optional(),
|
|
30
|
+
directory: z.string().optional(),
|
|
31
|
+
fallbacks: z.array(FallbackSchema).optional(),
|
|
32
|
+
files: z.string().default("./attachments").optional()
|
|
33
|
+
});
|
|
34
|
+
const CronJobSchema = z.looseObject({
|
|
35
|
+
id: z.string().min(1),
|
|
36
|
+
createdAt: z.string().optional(),
|
|
37
|
+
message: z.string().default(""),
|
|
38
|
+
reply: z.string().optional(),
|
|
39
|
+
agentId: z.string().optional(),
|
|
40
|
+
env: z.record(z.string(), z.union([z.string(), z.boolean()])).optional(),
|
|
41
|
+
session: z.looseObject({ type: z.string() }).optional(),
|
|
42
|
+
schedule: z.union([
|
|
43
|
+
z.looseObject({ cron: z.string() }),
|
|
44
|
+
z.looseObject({ every: z.string() }),
|
|
45
|
+
z.looseObject({ at: z.string() })
|
|
46
|
+
])
|
|
47
|
+
});
|
|
48
|
+
const ChatSettingsSchema = z.looseObject({
|
|
49
|
+
defaultAgent: z.string().optional(),
|
|
50
|
+
sessions: z.record(z.string(), z.string()).optional(),
|
|
51
|
+
routers: z.array(z.string()).optional(),
|
|
52
|
+
jobs: z.array(CronJobSchema).optional()
|
|
53
|
+
});
|
|
54
|
+
const AgentSessionSettingsSchema = z.looseObject({ env: z.record(z.string(), z.union([z.string(), z.boolean()])).optional() });
|
|
55
|
+
const EnvironmentSchema = z.looseObject({
|
|
56
|
+
init: z.string().optional(),
|
|
57
|
+
up: z.string().optional(),
|
|
58
|
+
down: z.string().optional(),
|
|
59
|
+
prefix: z.string().optional(),
|
|
60
|
+
envFormat: z.string().optional(),
|
|
61
|
+
exportLiteTo: z.string().optional(),
|
|
62
|
+
env: z.record(z.string(), z.union([z.string(), z.boolean()])).optional()
|
|
63
|
+
});
|
|
64
|
+
const SettingsSchema = z.looseObject({
|
|
65
|
+
chats: z.looseObject({ defaultId: z.string().optional() }).optional(),
|
|
66
|
+
defaultAgent: AgentSchema.optional(),
|
|
67
|
+
environments: z.record(z.string(), z.string()).optional(),
|
|
68
|
+
routers: z.array(z.string()).optional(),
|
|
69
|
+
files: z.string().default("./attachments").optional(),
|
|
70
|
+
api: z.union([z.boolean(), z.looseObject({
|
|
71
|
+
host: z.string().optional(),
|
|
72
|
+
port: z.number().optional(),
|
|
73
|
+
proxy_host: z.string().optional()
|
|
74
|
+
})]).optional()
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/shared/workspace.ts
|
|
79
|
+
var workspace_exports = /* @__PURE__ */ __exportAll({
|
|
80
|
+
applyTemplateToAgent: () => applyTemplateToAgent,
|
|
81
|
+
copyEnvironmentTemplate: () => copyEnvironmentTemplate,
|
|
82
|
+
copyTemplate: () => copyTemplate,
|
|
83
|
+
copyTemplateBase: () => copyTemplateBase,
|
|
84
|
+
deleteAgent: () => deleteAgent,
|
|
85
|
+
enableEnvironment: () => enableEnvironment,
|
|
86
|
+
ensureAgentWorkDir: () => ensureAgentWorkDir,
|
|
87
|
+
getActiveEnvironmentInfo: () => getActiveEnvironmentInfo,
|
|
88
|
+
getAgent: () => getAgent,
|
|
89
|
+
getAgentDir: () => getAgentDir,
|
|
90
|
+
getAgentSessionSettingsPath: () => getAgentSessionSettingsPath,
|
|
91
|
+
getAgentSettingsPath: () => getAgentSettingsPath,
|
|
92
|
+
getChatSettingsPath: () => getChatSettingsPath,
|
|
93
|
+
getClawminiDir: () => getClawminiDir,
|
|
94
|
+
getEnvironmentPath: () => getEnvironmentPath,
|
|
95
|
+
getSettingsPath: () => getSettingsPath,
|
|
96
|
+
getSocketPath: () => getSocketPath,
|
|
97
|
+
getWorkspaceRoot: () => getWorkspaceRoot,
|
|
98
|
+
isValidAgentId: () => isValidAgentId,
|
|
99
|
+
listAgents: () => listAgents,
|
|
100
|
+
readAgentSessionSettings: () => readAgentSessionSettings,
|
|
101
|
+
readChatSettings: () => readChatSettings,
|
|
102
|
+
readEnvironment: () => readEnvironment,
|
|
103
|
+
readSettings: () => readSettings,
|
|
104
|
+
resolveAgentWorkDir: () => resolveAgentWorkDir,
|
|
105
|
+
resolveEnvironmentTemplatePath: () => resolveEnvironmentTemplatePath,
|
|
106
|
+
resolveTemplatePath: () => resolveTemplatePath,
|
|
107
|
+
resolveTemplatePathBase: () => resolveTemplatePathBase,
|
|
108
|
+
writeAgentSessionSettings: () => writeAgentSessionSettings,
|
|
109
|
+
writeAgentSettings: () => writeAgentSettings,
|
|
110
|
+
writeChatSettings: () => writeChatSettings,
|
|
111
|
+
writeSettings: () => writeSettings
|
|
112
|
+
});
|
|
113
|
+
function getWorkspaceRoot(startDir = process.cwd()) {
|
|
114
|
+
let curr = startDir;
|
|
115
|
+
while (curr !== path.parse(curr).root) {
|
|
116
|
+
if (fs.existsSync(path.join(curr, ".clawmini"))) return curr;
|
|
117
|
+
if (fs.existsSync(path.join(curr, "package.json")) || fs.existsSync(path.join(curr, ".git"))) return curr;
|
|
118
|
+
curr = path.dirname(curr);
|
|
119
|
+
}
|
|
120
|
+
return startDir;
|
|
121
|
+
}
|
|
122
|
+
function resolveAgentWorkDir(agentId, customDir, startDir = process.cwd()) {
|
|
123
|
+
const workspaceRoot = getWorkspaceRoot(startDir);
|
|
124
|
+
const dirPath = customDir ? path.resolve(workspaceRoot, customDir) : path.resolve(workspaceRoot, agentId);
|
|
125
|
+
if (!pathIsInsideDir(dirPath, workspaceRoot, { allowSameDir: true })) throw new Error("Invalid agent directory: resolves outside the workspace.");
|
|
126
|
+
return dirPath;
|
|
127
|
+
}
|
|
128
|
+
async function ensureAgentWorkDir(agentId, customDir, startDir = process.cwd()) {
|
|
129
|
+
const dirPath = resolveAgentWorkDir(agentId, customDir, startDir);
|
|
130
|
+
if (!fs.existsSync(dirPath)) {
|
|
131
|
+
await fs$1.mkdir(dirPath, { recursive: true });
|
|
132
|
+
console.log(`Created agent working directory at ${dirPath}`);
|
|
133
|
+
}
|
|
134
|
+
return dirPath;
|
|
135
|
+
}
|
|
136
|
+
function getClawminiDir(startDir = process.cwd()) {
|
|
137
|
+
return path.join(getWorkspaceRoot(startDir), ".clawmini");
|
|
138
|
+
}
|
|
139
|
+
function getSocketPath(startDir = process.cwd()) {
|
|
140
|
+
return path.join(getClawminiDir(startDir), "server.sock");
|
|
141
|
+
}
|
|
142
|
+
function getSettingsPath(startDir = process.cwd()) {
|
|
143
|
+
return path.join(getClawminiDir(startDir), "settings.json");
|
|
144
|
+
}
|
|
145
|
+
function getChatSettingsPath(chatId, startDir = process.cwd()) {
|
|
146
|
+
return path.join(getClawminiDir(startDir), "chats", chatId, "settings.json");
|
|
147
|
+
}
|
|
148
|
+
function isValidAgentId(agentId) {
|
|
149
|
+
if (!agentId || agentId.length === 0) return false;
|
|
150
|
+
return /^[a-zA-Z0-9_]+(?:-[a-zA-Z0-9_]+)*$/.test(agentId);
|
|
151
|
+
}
|
|
152
|
+
function getAgentDir(agentId, startDir = process.cwd()) {
|
|
153
|
+
if (!isValidAgentId(agentId)) throw new Error(`Invalid agent ID: ${agentId}`);
|
|
154
|
+
return path.join(getClawminiDir(startDir), "agents", agentId);
|
|
155
|
+
}
|
|
156
|
+
function getAgentSettingsPath(agentId, startDir = process.cwd()) {
|
|
157
|
+
return path.join(getAgentDir(agentId, startDir), "settings.json");
|
|
158
|
+
}
|
|
159
|
+
function getAgentSessionSettingsPath(agentId, sessionId, startDir = process.cwd()) {
|
|
160
|
+
if (!isValidAgentId(agentId)) throw new Error(`Invalid agent ID: ${agentId}`);
|
|
161
|
+
return path.join(getClawminiDir(startDir), "agents", agentId, "sessions", sessionId, "settings.json");
|
|
162
|
+
}
|
|
163
|
+
async function readJsonFile(filePath) {
|
|
164
|
+
try {
|
|
165
|
+
const data = await fs$1.readFile(filePath, "utf-8");
|
|
166
|
+
return JSON.parse(data);
|
|
167
|
+
} catch {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async function writeJsonFile(filePath, data) {
|
|
172
|
+
const dir = path.dirname(filePath);
|
|
173
|
+
await fs$1.mkdir(dir, { recursive: true });
|
|
174
|
+
await fs$1.writeFile(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
175
|
+
}
|
|
176
|
+
async function readChatSettings(chatId, startDir = process.cwd()) {
|
|
177
|
+
const data = await readJsonFile(getChatSettingsPath(chatId, startDir));
|
|
178
|
+
if (!data) return null;
|
|
179
|
+
const parsed = ChatSettingsSchema.safeParse(data);
|
|
180
|
+
return parsed.success ? parsed.data : null;
|
|
181
|
+
}
|
|
182
|
+
async function writeChatSettings(chatId, data, startDir = process.cwd()) {
|
|
183
|
+
await writeJsonFile(getChatSettingsPath(chatId, startDir), data);
|
|
184
|
+
}
|
|
185
|
+
async function readAgentSessionSettings(agentId, sessionId, startDir = process.cwd()) {
|
|
186
|
+
const data = await readJsonFile(getAgentSessionSettingsPath(agentId, sessionId, startDir));
|
|
187
|
+
if (!data) return null;
|
|
188
|
+
const parsed = AgentSessionSettingsSchema.safeParse(data);
|
|
189
|
+
return parsed.success ? parsed.data : null;
|
|
190
|
+
}
|
|
191
|
+
async function writeAgentSessionSettings(agentId, sessionId, data, startDir = process.cwd()) {
|
|
192
|
+
await writeJsonFile(getAgentSessionSettingsPath(agentId, sessionId, startDir), data);
|
|
193
|
+
}
|
|
194
|
+
async function getAgent(agentId, startDir = process.cwd()) {
|
|
195
|
+
const filePath = getAgentSettingsPath(agentId, startDir);
|
|
196
|
+
let dataStr;
|
|
197
|
+
try {
|
|
198
|
+
dataStr = await fs$1.readFile(filePath, "utf-8");
|
|
199
|
+
} catch (err) {
|
|
200
|
+
if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") return null;
|
|
201
|
+
throw err;
|
|
202
|
+
}
|
|
203
|
+
let data;
|
|
204
|
+
try {
|
|
205
|
+
data = JSON.parse(dataStr);
|
|
206
|
+
} catch (parseErr) {
|
|
207
|
+
const message = parseErr instanceof Error ? parseErr.message : String(parseErr);
|
|
208
|
+
throw new Error(`Invalid JSON in ${filePath}: ${message}`, { cause: parseErr });
|
|
209
|
+
}
|
|
210
|
+
const parsed = AgentSchema.safeParse(data);
|
|
211
|
+
if (!parsed.success) throw new Error(`Invalid schema in ${filePath}: ${parsed.error.message}`);
|
|
212
|
+
return parsed.data;
|
|
213
|
+
}
|
|
214
|
+
async function writeAgentSettings(agentId, data, startDir = process.cwd()) {
|
|
215
|
+
await ensureAgentWorkDir(agentId, data.directory, startDir);
|
|
216
|
+
await writeJsonFile(getAgentSettingsPath(agentId, startDir), data);
|
|
217
|
+
}
|
|
218
|
+
async function listAgents(startDir = process.cwd()) {
|
|
219
|
+
const agentsDir = path.join(getClawminiDir(startDir), "agents");
|
|
220
|
+
try {
|
|
221
|
+
const entries = await fs$1.readdir(agentsDir, { withFileTypes: true });
|
|
222
|
+
const agentIds = [];
|
|
223
|
+
for (const entry of entries) if (entry.isDirectory()) {
|
|
224
|
+
const settingsPath = path.join(agentsDir, entry.name, "settings.json");
|
|
225
|
+
try {
|
|
226
|
+
await fs$1.access(settingsPath);
|
|
227
|
+
agentIds.push(entry.name);
|
|
228
|
+
} catch {}
|
|
229
|
+
}
|
|
230
|
+
return agentIds;
|
|
231
|
+
} catch {
|
|
232
|
+
return [];
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
async function deleteAgent(agentId, startDir = process.cwd()) {
|
|
236
|
+
const dir = getAgentDir(agentId, startDir);
|
|
237
|
+
const agentsDir = path.join(getClawminiDir(startDir), "agents");
|
|
238
|
+
if (!pathIsInsideDir(dir, agentsDir)) throw new Error(`Security Error: Cannot delete agent directory outside of ${agentsDir}`);
|
|
239
|
+
try {
|
|
240
|
+
await fs$1.rm(dir, {
|
|
241
|
+
recursive: true,
|
|
242
|
+
force: true
|
|
243
|
+
});
|
|
244
|
+
} catch {}
|
|
245
|
+
}
|
|
246
|
+
async function isDirectory(dirPath) {
|
|
247
|
+
try {
|
|
248
|
+
return (await fs$1.stat(dirPath)).isDirectory();
|
|
249
|
+
} catch {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
async function resolveTemplatePathBase(templateName, startDir = process.cwd()) {
|
|
254
|
+
const workspaceRoot = getWorkspaceRoot(startDir);
|
|
255
|
+
const localTemplatePath = path.join(workspaceRoot, ".clawmini", "templates", templateName);
|
|
256
|
+
if (await isDirectory(localTemplatePath)) return localTemplatePath;
|
|
257
|
+
let currentDir = path.dirname(fileURLToPath(import.meta.url));
|
|
258
|
+
while (currentDir !== path.parse(currentDir).root && !fs.existsSync(path.join(currentDir, "package.json"))) currentDir = path.dirname(currentDir);
|
|
259
|
+
const searchPath = path.join(currentDir, "templates", templateName);
|
|
260
|
+
if (await isDirectory(searchPath)) return searchPath;
|
|
261
|
+
throw new Error(`Template not found: ${templateName} (searched local: ${localTemplatePath}, built-in: ${searchPath})`);
|
|
262
|
+
}
|
|
263
|
+
async function resolveTemplatePath(templateName, startDir = process.cwd()) {
|
|
264
|
+
if (templateName === "environments" || templateName.startsWith("environments/")) throw new Error(`Template not found: ${templateName}`);
|
|
265
|
+
return resolveTemplatePathBase(templateName, startDir);
|
|
266
|
+
}
|
|
267
|
+
async function resolveEnvironmentTemplatePath(templateName, startDir = process.cwd()) {
|
|
268
|
+
return resolveTemplatePathBase(path.join("environments", templateName), startDir);
|
|
269
|
+
}
|
|
270
|
+
async function copyTemplateBase(templatePath, targetDir, allowMissingDir = false) {
|
|
271
|
+
try {
|
|
272
|
+
if ((await fs$1.readdir(targetDir)).length > 0) throw new Error(`Target directory is not empty: ${targetDir}`);
|
|
273
|
+
} catch (err) {
|
|
274
|
+
if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") if (allowMissingDir) await fs$1.mkdir(targetDir, { recursive: true });
|
|
275
|
+
else throw new Error(`Target directory does not exist: ${targetDir}`, { cause: err });
|
|
276
|
+
else throw err;
|
|
277
|
+
}
|
|
278
|
+
await fs$1.cp(templatePath, targetDir, { recursive: true });
|
|
279
|
+
}
|
|
280
|
+
async function copyTemplate(templateName, targetDir, startDir = process.cwd()) {
|
|
281
|
+
await copyTemplateBase(await resolveTemplatePath(templateName, startDir), targetDir, false);
|
|
282
|
+
}
|
|
283
|
+
async function copyEnvironmentTemplate(templateName, targetDir, startDir = process.cwd()) {
|
|
284
|
+
await copyTemplateBase(await resolveEnvironmentTemplatePath(templateName, startDir), targetDir, true);
|
|
285
|
+
}
|
|
286
|
+
async function applyTemplateToAgent(agentId, templateName, overrides, startDir = process.cwd()) {
|
|
287
|
+
const agentWorkDir = resolveAgentWorkDir(agentId, overrides.directory, startDir);
|
|
288
|
+
await copyTemplate(templateName, agentWorkDir, startDir);
|
|
289
|
+
const settingsPath = path.join(agentWorkDir, "settings.json");
|
|
290
|
+
try {
|
|
291
|
+
const rawSettings = await fs$1.readFile(settingsPath, "utf-8");
|
|
292
|
+
const parsedSettings = JSON.parse(rawSettings);
|
|
293
|
+
const validation = AgentSchema.safeParse(parsedSettings);
|
|
294
|
+
if (validation.success) {
|
|
295
|
+
const templateData = validation.data;
|
|
296
|
+
if (templateData.directory) {
|
|
297
|
+
console.warn(`Warning: Ignoring 'directory' field from template settings.json. Using default or provided directory.`);
|
|
298
|
+
delete templateData.directory;
|
|
299
|
+
}
|
|
300
|
+
const mergedEnv = {
|
|
301
|
+
...templateData.env || {},
|
|
302
|
+
...overrides.env || {}
|
|
303
|
+
};
|
|
304
|
+
const mergedData = {
|
|
305
|
+
...templateData,
|
|
306
|
+
...overrides
|
|
307
|
+
};
|
|
308
|
+
if (Object.keys(mergedEnv).length > 0) mergedData.env = mergedEnv;
|
|
309
|
+
await writeAgentSettings(agentId, mergedData, startDir);
|
|
310
|
+
}
|
|
311
|
+
} catch {} finally {
|
|
312
|
+
try {
|
|
313
|
+
await fs$1.rm(settingsPath);
|
|
314
|
+
} catch {}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
async function readSettings(startDir = process.cwd()) {
|
|
318
|
+
const data = await readJsonFile(getSettingsPath(startDir));
|
|
319
|
+
if (!data) return null;
|
|
320
|
+
const parsed = SettingsSchema.safeParse(data);
|
|
321
|
+
return parsed.success ? parsed.data : null;
|
|
322
|
+
}
|
|
323
|
+
async function writeSettings(data, startDir = process.cwd()) {
|
|
324
|
+
await writeJsonFile(getSettingsPath(startDir), data);
|
|
325
|
+
}
|
|
326
|
+
function getEnvironmentPath(name, startDir = process.cwd()) {
|
|
327
|
+
return path.join(getClawminiDir(startDir), "environments", name);
|
|
328
|
+
}
|
|
329
|
+
async function readEnvironment(name, startDir = process.cwd()) {
|
|
330
|
+
const data = await readJsonFile(path.join(getEnvironmentPath(name, startDir), "env.json"));
|
|
331
|
+
if (!data) return null;
|
|
332
|
+
const parsed = EnvironmentSchema.safeParse(data);
|
|
333
|
+
return parsed.success ? parsed.data : null;
|
|
334
|
+
}
|
|
335
|
+
async function getActiveEnvironmentInfo(targetPath, startDir = process.cwd()) {
|
|
336
|
+
const settings = await readSettings(startDir);
|
|
337
|
+
if (!settings?.environments) return null;
|
|
338
|
+
const workspaceRoot = getWorkspaceRoot(startDir);
|
|
339
|
+
const resolvedTarget = path.resolve(workspaceRoot, targetPath);
|
|
340
|
+
let bestMatch = null;
|
|
341
|
+
let maxDepth = -1;
|
|
342
|
+
for (const [envPath, envName] of Object.entries(settings.environments)) {
|
|
343
|
+
const resolvedEnvPath = path.resolve(workspaceRoot, envPath);
|
|
344
|
+
if (pathIsInsideDir(resolvedTarget, resolvedEnvPath, { allowSameDir: true })) {
|
|
345
|
+
const depth = resolvedEnvPath.split(path.sep).length;
|
|
346
|
+
if (depth > maxDepth) {
|
|
347
|
+
maxDepth = depth;
|
|
348
|
+
bestMatch = {
|
|
349
|
+
name: envName,
|
|
350
|
+
targetPath: resolvedEnvPath
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return bestMatch;
|
|
356
|
+
}
|
|
357
|
+
async function enableEnvironment(name, targetPath = "./", startDir = process.cwd()) {
|
|
358
|
+
const targetDir = getEnvironmentPath(name, startDir);
|
|
359
|
+
if (!fs.existsSync(targetDir)) {
|
|
360
|
+
await copyEnvironmentTemplate(name, targetDir, startDir);
|
|
361
|
+
console.log(`Copied environment template '${name}'.`);
|
|
362
|
+
} else console.log(`Environment template '${name}' already exists in workspace.`);
|
|
363
|
+
const settings = await readSettings(startDir) || { chats: { defaultId: "" } };
|
|
364
|
+
const environments = settings.environments || {};
|
|
365
|
+
environments[targetPath] = name;
|
|
366
|
+
settings.environments = environments;
|
|
367
|
+
await writeSettings(settings, startDir);
|
|
368
|
+
console.log(`Enabled environment '${name}' for path '${targetPath}'.`);
|
|
369
|
+
const envConfig = await readEnvironment(name, startDir);
|
|
370
|
+
if (envConfig?.init) {
|
|
371
|
+
const workspaceRoot = getWorkspaceRoot(startDir);
|
|
372
|
+
const affectedDir = path.resolve(workspaceRoot, targetPath);
|
|
373
|
+
console.log(`Executing init command for environment '${name}': ${envConfig.init}`);
|
|
374
|
+
execSync(envConfig.init, {
|
|
375
|
+
cwd: affectedDir,
|
|
376
|
+
stdio: "inherit"
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
//#endregion
|
|
382
|
+
export { SettingsSchema as C, CronJobSchema as S, workspace_exports as _, getAgent as a, writeChatSettings as b, getSettingsPath as c, isValidAgentId as d, listAgents as f, readSettings as g, readEnvironment as h, getActiveEnvironmentInfo as i, getSocketPath as l, readChatSettings as m, deleteAgent as n, getClawminiDir as o, readAgentSessionSettings as p, enableEnvironment as r, getEnvironmentPath as s, applyTemplateToAgent as t, getWorkspaceRoot as u, writeAgentSessionSettings as v, writeSettings as x, writeAgentSettings as y };
|
|
383
|
+
//# sourceMappingURL=workspace-CSgfo_2J.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-CSgfo_2J.mjs","names":["fsPromises"],"sources":["../src/shared/config.ts","../src/shared/workspace.ts"],"sourcesContent":["import { z } from 'zod';\n\nexport const FallbackSchema = z.looseObject({\n commands: z\n .looseObject({\n new: z.string().optional(),\n append: z.string().optional(),\n getSessionId: z.string().optional(),\n getMessageContent: z.string().optional(),\n })\n .optional(),\n env: z.record(z.string(), z.union([z.string(), z.boolean()])).optional(),\n retries: z.number().int().min(0).default(1),\n delayMs: z.number().int().min(0).default(1000),\n});\n\nexport const AgentSchema = z.looseObject({\n commands: z\n .looseObject({\n new: z.string().optional(),\n append: z.string().optional(),\n getSessionId: z.string().optional(),\n getMessageContent: z.string().optional(),\n })\n .optional(),\n env: z.record(z.string(), z.union([z.string(), z.boolean()])).optional(),\n directory: z.string().optional(),\n fallbacks: z.array(FallbackSchema).optional(),\n files: z.string().default('./attachments').optional(),\n});\n\nexport type Agent = z.infer<typeof AgentSchema>;\n\nexport const CronJobSchema = z.looseObject({\n id: z.string().min(1),\n createdAt: z.string().optional(),\n message: z.string().default(''),\n reply: z.string().optional(),\n agentId: z.string().optional(),\n env: z.record(z.string(), z.union([z.string(), z.boolean()])).optional(),\n session: z.looseObject({ type: z.string() }).optional(),\n schedule: z.union([\n z.looseObject({ cron: z.string() }),\n z.looseObject({ every: z.string() }),\n z.looseObject({ at: z.string() }),\n ]),\n});\n\nexport type CronJob = z.infer<typeof CronJobSchema>;\n\nexport const ChatSettingsSchema = z.looseObject({\n defaultAgent: z.string().optional(),\n sessions: z.record(z.string(), z.string()).optional(),\n routers: z.array(z.string()).optional(),\n jobs: z.array(CronJobSchema).optional(),\n});\n\nexport type ChatSettings = z.infer<typeof ChatSettingsSchema>;\n\nexport const AgentSessionSettingsSchema = z.looseObject({\n env: z.record(z.string(), z.union([z.string(), z.boolean()])).optional(),\n});\n\nexport type AgentSessionSettings = z.infer<typeof AgentSessionSettingsSchema>;\n\nexport const EnvironmentSchema = z.looseObject({\n init: z.string().optional(),\n up: z.string().optional(),\n down: z.string().optional(),\n prefix: z.string().optional(),\n envFormat: z.string().optional(),\n exportLiteTo: z.string().optional(),\n env: z.record(z.string(), z.union([z.string(), z.boolean()])).optional(),\n});\n\nexport type Environment = z.infer<typeof EnvironmentSchema>;\n\nexport const SettingsSchema = z.looseObject({\n chats: z\n .looseObject({\n defaultId: z.string().optional(),\n })\n .optional(),\n defaultAgent: AgentSchema.optional(),\n environments: z.record(z.string(), z.string()).optional(),\n routers: z.array(z.string()).optional(),\n files: z.string().default('./attachments').optional(),\n api: z\n .union([\n z.boolean(),\n z.looseObject({\n host: z.string().optional(),\n port: z.number().optional(),\n proxy_host: z.string().optional(),\n }),\n ])\n .optional(),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","/* eslint-disable max-lines */\nimport { execSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport fsPromises from 'node:fs/promises';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n type Agent,\n AgentSchema,\n type ChatSettings,\n ChatSettingsSchema,\n type AgentSessionSettings,\n AgentSessionSettingsSchema,\n type Environment,\n EnvironmentSchema,\n type Settings,\n SettingsSchema,\n} from './config.js';\nimport { pathIsInsideDir } from './utils/fs.js';\n\nexport function getWorkspaceRoot(startDir = process.cwd()): string {\n let curr = startDir;\n while (curr !== path.parse(curr).root) {\n if (fs.existsSync(path.join(curr, '.clawmini'))) {\n return curr;\n }\n if (fs.existsSync(path.join(curr, 'package.json')) || fs.existsSync(path.join(curr, '.git'))) {\n return curr;\n }\n curr = path.dirname(curr);\n }\n return startDir;\n}\n\nexport function resolveAgentWorkDir(\n agentId: string,\n customDir?: string,\n startDir = process.cwd()\n): string {\n const workspaceRoot = getWorkspaceRoot(startDir);\n const dirPath = customDir\n ? path.resolve(workspaceRoot, customDir)\n : path.resolve(workspaceRoot, agentId);\n\n if (!pathIsInsideDir(dirPath, workspaceRoot, { allowSameDir: true })) {\n throw new Error('Invalid agent directory: resolves outside the workspace.');\n }\n\n return dirPath;\n}\n\nexport async function ensureAgentWorkDir(\n agentId: string,\n customDir?: string,\n startDir = process.cwd()\n): Promise<string> {\n const dirPath = resolveAgentWorkDir(agentId, customDir, startDir);\n\n if (!fs.existsSync(dirPath)) {\n await fsPromises.mkdir(dirPath, { recursive: true });\n console.log(`Created agent working directory at ${dirPath}`);\n }\n return dirPath;\n}\n\nexport function getClawminiDir(startDir = process.cwd()): string {\n return path.join(getWorkspaceRoot(startDir), '.clawmini');\n}\n\nexport function getSocketPath(startDir = process.cwd()): string {\n return path.join(getClawminiDir(startDir), 'server.sock');\n}\n\nexport function getSettingsPath(startDir = process.cwd()): string {\n return path.join(getClawminiDir(startDir), 'settings.json');\n}\n\nexport function getChatSettingsPath(chatId: string, startDir = process.cwd()): string {\n return path.join(getClawminiDir(startDir), 'chats', chatId, 'settings.json');\n}\n\nexport function isValidAgentId(agentId: string): boolean {\n if (!agentId || agentId.length === 0) return false;\n return /^[a-zA-Z0-9_]+(?:-[a-zA-Z0-9_]+)*$/.test(agentId);\n}\n\nexport function getAgentDir(agentId: string, startDir = process.cwd()): string {\n if (!isValidAgentId(agentId)) {\n throw new Error(`Invalid agent ID: ${agentId}`);\n }\n return path.join(getClawminiDir(startDir), 'agents', agentId);\n}\n\nexport function getAgentSettingsPath(agentId: string, startDir = process.cwd()): string {\n return path.join(getAgentDir(agentId, startDir), 'settings.json');\n}\n\nexport function getAgentSessionSettingsPath(\n agentId: string,\n sessionId: string,\n startDir = process.cwd()\n): string {\n if (!isValidAgentId(agentId)) {\n throw new Error(`Invalid agent ID: ${agentId}`);\n }\n return path.join(\n getClawminiDir(startDir),\n 'agents',\n agentId,\n 'sessions',\n sessionId,\n 'settings.json'\n );\n}\n\nasync function readJsonFile(filePath: string): Promise<Record<string, unknown> | null> {\n try {\n const data = await fsPromises.readFile(filePath, 'utf-8');\n return JSON.parse(data) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\nasync function writeJsonFile(filePath: string, data: Record<string, unknown>): Promise<void> {\n const dir = path.dirname(filePath);\n await fsPromises.mkdir(dir, { recursive: true });\n await fsPromises.writeFile(filePath, JSON.stringify(data, null, 2), 'utf-8');\n}\n\nexport async function readChatSettings(\n chatId: string,\n startDir = process.cwd()\n): Promise<ChatSettings | null> {\n const data = await readJsonFile(getChatSettingsPath(chatId, startDir));\n if (!data) return null;\n const parsed = ChatSettingsSchema.safeParse(data);\n return parsed.success ? parsed.data : null;\n}\n\nexport async function writeChatSettings(\n chatId: string,\n data: ChatSettings,\n startDir = process.cwd()\n): Promise<void> {\n await writeJsonFile(getChatSettingsPath(chatId, startDir), data as Record<string, unknown>);\n}\n\nexport async function readAgentSessionSettings(\n agentId: string,\n sessionId: string,\n startDir = process.cwd()\n): Promise<AgentSessionSettings | null> {\n const data = await readJsonFile(getAgentSessionSettingsPath(agentId, sessionId, startDir));\n if (!data) return null;\n const parsed = AgentSessionSettingsSchema.safeParse(data);\n return parsed.success ? parsed.data : null;\n}\n\nexport async function writeAgentSessionSettings(\n agentId: string,\n sessionId: string,\n data: AgentSessionSettings,\n startDir = process.cwd()\n): Promise<void> {\n await writeJsonFile(\n getAgentSessionSettingsPath(agentId, sessionId, startDir),\n data as Record<string, unknown>\n );\n}\n\nexport async function getAgent(agentId: string, startDir = process.cwd()): Promise<Agent | null> {\n const filePath = getAgentSettingsPath(agentId, startDir);\n let dataStr: string;\n try {\n dataStr = await fsPromises.readFile(filePath, 'utf-8');\n } catch (err: unknown) {\n if (err && typeof err === 'object' && 'code' in err && err.code === 'ENOENT') return null;\n throw err;\n }\n\n let data: unknown;\n try {\n data = JSON.parse(dataStr);\n } catch (parseErr: unknown) {\n const message = parseErr instanceof Error ? parseErr.message : String(parseErr);\n throw new Error(`Invalid JSON in ${filePath}: ${message}`, { cause: parseErr });\n }\n\n const parsed = AgentSchema.safeParse(data);\n if (!parsed.success) {\n throw new Error(`Invalid schema in ${filePath}: ${parsed.error.message}`);\n }\n return parsed.data;\n}\n\nexport async function writeAgentSettings(\n agentId: string,\n data: Agent,\n startDir = process.cwd()\n): Promise<void> {\n await ensureAgentWorkDir(agentId, data.directory, startDir);\n await writeJsonFile(getAgentSettingsPath(agentId, startDir), data as Record<string, unknown>);\n}\n\nexport async function listAgents(startDir = process.cwd()): Promise<string[]> {\n const agentsDir = path.join(getClawminiDir(startDir), 'agents');\n try {\n const entries = await fsPromises.readdir(agentsDir, { withFileTypes: true });\n const agentIds = [];\n for (const entry of entries) {\n if (entry.isDirectory()) {\n const settingsPath = path.join(agentsDir, entry.name, 'settings.json');\n try {\n await fsPromises.access(settingsPath);\n agentIds.push(entry.name);\n } catch {\n // No settings.json, probably just a sessions dir for a non-existent agent or default agent\n }\n }\n }\n return agentIds;\n } catch {\n return [];\n }\n}\n\nexport async function deleteAgent(agentId: string, startDir = process.cwd()): Promise<void> {\n const dir = getAgentDir(agentId, startDir);\n const agentsDir = path.join(getClawminiDir(startDir), 'agents');\n\n if (!pathIsInsideDir(dir, agentsDir)) {\n throw new Error(`Security Error: Cannot delete agent directory outside of ${agentsDir}`);\n }\n\n try {\n await fsPromises.rm(dir, { recursive: true, force: true });\n } catch {\n // Ignore if not found\n }\n}\n\nasync function isDirectory(dirPath: string): Promise<boolean> {\n try {\n const stat = await fsPromises.stat(dirPath);\n return stat.isDirectory();\n } catch {\n return false;\n }\n}\n\nexport async function resolveTemplatePathBase(\n templateName: string,\n startDir = process.cwd()\n): Promise<string> {\n const workspaceRoot = getWorkspaceRoot(startDir);\n const localTemplatePath = path.join(workspaceRoot, '.clawmini', 'templates', templateName);\n\n if (await isDirectory(localTemplatePath)) {\n return localTemplatePath;\n }\n\n // Fallback to built-in templates\n // Find the clawmini package root by looking for package.json\n let currentDir = path.dirname(fileURLToPath(import.meta.url));\n while (\n currentDir !== path.parse(currentDir).root &&\n !fs.existsSync(path.join(currentDir, 'package.json'))\n ) {\n currentDir = path.dirname(currentDir);\n }\n\n const searchPath = path.join(currentDir, 'templates', templateName);\n\n if (await isDirectory(searchPath)) {\n return searchPath;\n }\n\n throw new Error(\n `Template not found: ${templateName} (searched local: ${localTemplatePath}, built-in: ${searchPath})`\n );\n}\n\nexport async function resolveTemplatePath(\n templateName: string,\n startDir = process.cwd()\n): Promise<string> {\n if (templateName === 'environments' || templateName.startsWith('environments/')) {\n throw new Error(`Template not found: ${templateName}`);\n }\n return resolveTemplatePathBase(templateName, startDir);\n}\n\nexport async function resolveEnvironmentTemplatePath(\n templateName: string,\n startDir = process.cwd()\n): Promise<string> {\n return resolveTemplatePathBase(path.join('environments', templateName), startDir);\n}\n\nexport async function copyTemplateBase(\n templatePath: string,\n targetDir: string,\n allowMissingDir: boolean = false\n): Promise<void> {\n // Check if target directory exists and is not empty\n try {\n const entries = await fsPromises.readdir(targetDir);\n if (entries.length > 0) {\n throw new Error(`Target directory is not empty: ${targetDir}`);\n }\n } catch (err: unknown) {\n if (err && typeof err === 'object' && 'code' in err && err.code === 'ENOENT') {\n if (allowMissingDir) {\n await fsPromises.mkdir(targetDir, { recursive: true });\n } else {\n throw new Error(`Target directory does not exist: ${targetDir}`, { cause: err });\n }\n } else {\n throw err;\n }\n }\n\n // Recursively copy\n await fsPromises.cp(templatePath, targetDir, { recursive: true });\n}\n\nexport async function copyTemplate(\n templateName: string,\n targetDir: string,\n startDir = process.cwd()\n): Promise<void> {\n const templatePath = await resolveTemplatePath(templateName, startDir);\n await copyTemplateBase(templatePath, targetDir, false);\n}\n\nexport async function copyEnvironmentTemplate(\n templateName: string,\n targetDir: string,\n startDir = process.cwd()\n): Promise<void> {\n const templatePath = await resolveEnvironmentTemplatePath(templateName, startDir);\n await copyTemplateBase(templatePath, targetDir, true);\n}\n\nexport async function applyTemplateToAgent(\n agentId: string,\n templateName: string,\n overrides: Agent,\n startDir = process.cwd()\n): Promise<void> {\n const agentWorkDir = resolveAgentWorkDir(agentId, overrides.directory, startDir);\n await copyTemplate(templateName, agentWorkDir, startDir);\n\n const settingsPath = path.join(agentWorkDir, 'settings.json');\n try {\n const rawSettings = await fsPromises.readFile(settingsPath, 'utf-8');\n const parsedSettings = JSON.parse(rawSettings);\n const validation = AgentSchema.safeParse(parsedSettings);\n\n if (validation.success) {\n const templateData = validation.data;\n if (templateData.directory) {\n console.warn(\n `Warning: Ignoring 'directory' field from template settings.json. Using default or provided directory.`\n );\n delete templateData.directory;\n }\n\n // Merge: overrides take precedence over templateData\n const mergedEnv = { ...(templateData.env || {}), ...(overrides.env || {}) };\n const mergedData: Agent = { ...templateData, ...overrides };\n if (Object.keys(mergedEnv).length > 0) {\n mergedData.env = mergedEnv;\n }\n\n await writeAgentSettings(agentId, mergedData, startDir);\n }\n } catch {\n // Ignore parsing or file not found errors\n } finally {\n try {\n await fsPromises.rm(settingsPath);\n } catch {\n // Ignore if it doesn't exist\n }\n }\n}\n\nexport async function readSettings(startDir = process.cwd()): Promise<Settings | null> {\n const data = await readJsonFile(getSettingsPath(startDir));\n if (!data) return null;\n const parsed = SettingsSchema.safeParse(data);\n return parsed.success ? parsed.data : null;\n}\n\nexport async function writeSettings(data: Settings, startDir = process.cwd()): Promise<void> {\n await writeJsonFile(getSettingsPath(startDir), data as Record<string, unknown>);\n}\n\nexport function getEnvironmentPath(name: string, startDir = process.cwd()): string {\n return path.join(getClawminiDir(startDir), 'environments', name);\n}\n\nexport async function readEnvironment(\n name: string,\n startDir = process.cwd()\n): Promise<Environment | null> {\n const data = await readJsonFile(path.join(getEnvironmentPath(name, startDir), 'env.json'));\n if (!data) return null;\n const parsed = EnvironmentSchema.safeParse(data);\n return parsed.success ? parsed.data : null;\n}\n\nexport async function getActiveEnvironmentInfo(\n targetPath: string,\n startDir = process.cwd()\n): Promise<{ name: string; targetPath: string } | null> {\n const settings = await readSettings(startDir);\n if (!settings?.environments) return null;\n\n const workspaceRoot = getWorkspaceRoot(startDir);\n const resolvedTarget = path.resolve(workspaceRoot, targetPath);\n\n let bestMatch: { name: string; targetPath: string } | null = null;\n let maxDepth = -1;\n\n for (const [envPath, envName] of Object.entries(settings.environments)) {\n const resolvedEnvPath = path.resolve(workspaceRoot, envPath);\n\n if (pathIsInsideDir(resolvedTarget, resolvedEnvPath, { allowSameDir: true })) {\n const depth = resolvedEnvPath.split(path.sep).length;\n if (depth > maxDepth) {\n maxDepth = depth;\n bestMatch = { name: envName, targetPath: resolvedEnvPath };\n }\n }\n }\n\n return bestMatch;\n}\n\nexport async function getActiveEnvironmentName(\n targetPath: string,\n startDir = process.cwd()\n): Promise<string | null> {\n const info = await getActiveEnvironmentInfo(targetPath, startDir);\n return info ? info.name : null;\n}\n\nexport async function enableEnvironment(\n name: string,\n targetPath: string = './',\n startDir = process.cwd()\n): Promise<void> {\n const targetDir = getEnvironmentPath(name, startDir);\n\n // Copy template to targetDir if it does not already exist\n if (!fs.existsSync(targetDir)) {\n await copyEnvironmentTemplate(name, targetDir, startDir);\n console.log(`Copied environment template '${name}'.`);\n } else {\n console.log(`Environment template '${name}' already exists in workspace.`);\n }\n\n const settings = (await readSettings(startDir)) || { chats: { defaultId: '' } };\n const environments = settings.environments || {};\n\n environments[targetPath] = name;\n settings.environments = environments;\n\n await writeSettings(settings, startDir);\n console.log(`Enabled environment '${name}' for path '${targetPath}'.`);\n\n // Execute init command if present\n const envConfig = await readEnvironment(name, startDir);\n if (envConfig?.init) {\n // Get the target directory for the environment\n const workspaceRoot = getWorkspaceRoot(startDir);\n const affectedDir = path.resolve(workspaceRoot, targetPath);\n console.log(`Executing init command for environment '${name}': ${envConfig.init}`);\n execSync(envConfig.init, { cwd: affectedDir, stdio: 'inherit' });\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Development Log
|
|
2
|
+
|
|
3
|
+
## Step 1: Core Chat Data Types & Storage Utilities
|
|
4
|
+
|
|
5
|
+
- Implemented `src/shared/chats.ts` with `UserMessage`, `CommandLogMessage`, and file utilities.
|
|
6
|
+
- Implemented unit tests in `src/shared/chats.test.ts`.
|
|
7
|
+
|
|
8
|
+
## Step 2: Daemon Concurrency & Execution Logic
|
|
9
|
+
|
|
10
|
+
- Added `handleUserMessage` in `src/daemon/queue.ts` which uses a queue-per-directory concurrency control.
|
|
11
|
+
- Updated `sendMessage` in `src/daemon/router.ts` to call `handleUserMessage` with the default chat if none is provided.
|
|
12
|
+
- Added test coverage in `src/daemon/queue.test.ts` for directory-based execution sequences and failure logging without halting.
|
|
13
|
+
|
|
14
|
+
## Step 3: CLI Chat Management Commands
|
|
15
|
+
|
|
16
|
+
- Created `src/cli/commands/chats.ts` with `list`, `add`, `delete`, and `set-default` commands using commander.
|
|
17
|
+
- Registered the new `chatsCmd` in `src/cli/index.ts`.
|
|
18
|
+
- Implemented comprehensive E2E tests in `src/cli/e2e.test.ts` for chat creation, listing, setting default, and deletion in a sandbox environment.
|
|
19
|
+
- Fixed a typescript `any` issue in `src/shared/chats.ts`.
|
|
20
|
+
- Validated logic by running all types, lints, and vitest passes successfully.
|
|
21
|
+
|
|
22
|
+
## Step 4: CLI Messaging and History Commands
|
|
23
|
+
|
|
24
|
+
- Updated `messages send` command in `src/cli/commands/messages.ts` to support routing via `--chat <id>`.
|
|
25
|
+
- Implemented `messages tail` command to view history of a given chat id, with support for human-readable output and `--json` raw JSONL output.
|
|
26
|
+
- Fixed TypeScript warnings (unexpected any) in `src/daemon/queue.ts` and `src/daemon/queue.test.ts`.
|
|
27
|
+
- Wrote full E2E test coverage in `src/cli/e2e.test.ts` for sending to specific chats and verifying historical output via tail.
|
|
28
|
+
|
|
29
|
+
## Step 5: Background Messaging
|
|
30
|
+
|
|
31
|
+
- Updated `src/daemon/queue.ts` to accept a `noWait` argument, so the daemon immediately returns without awaiting task completion when set.
|
|
32
|
+
- Updated the TRPC mutation in `src/daemon/router.ts` to accept a boolean `noWait` flag and forward it.
|
|
33
|
+
- Updated `src/cli/commands/messages.ts` with a `--no-wait` flag using `commander`.
|
|
34
|
+
- Verified behavior with an added E2E test in `src/cli/e2e.test.ts` that runs commands effectively without halting the client process.
|
|
35
|
+
- Fixed a bug where `[USER]` messages were logged before previous queue items finished executing. `[USER]` messages are now appended inside the queue task to guarantee atomic execution ordering.
|
|
36
|
+
- Added E2E test to verify correct atomic ordering of `[USER]` and `[LOG]` outputs with the `--no-wait` flag.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Notes for Chats Feature
|
|
2
|
+
|
|
3
|
+
## Current State of the Codebase
|
|
4
|
+
- **Initialization:** `clawmini init` sets up a workspace by creating a `.clawmini` directory and a `settings.json` file. The settings file currently has a minimal structure: `{ "defaultAgent": { "commands": { "new": "echo $CLAW_CLI_MESSAGE" }, "env": {} } }`.
|
|
5
|
+
- **Client:** The CLI provides `clawmini messages send <message>`. This command communicates with a local background daemon via tRPC over a UNIX domain socket.
|
|
6
|
+
- **Daemon Router:** The daemon receives the message, reads `settings.json`, and spawns the command configured in `defaultAgent.commands.new`. The message is passed securely via the `CLAW_CLI_MESSAGE` environment variable.
|
|
7
|
+
- **Missing functionality:** There is no notion of multiple separate chats, no local storage of history, and commands are simply spawned immediately without any directory-based concurrency control.
|
|
8
|
+
|
|
9
|
+
## Feature Requirements
|
|
10
|
+
1. **Chat Management:**
|
|
11
|
+
- `clawmini chats` with subcommands: `list`, `add <id>`, `delete <id>`.
|
|
12
|
+
- Users create IDs for chats.
|
|
13
|
+
- Set a default chat ID automatically, user can update it with `set-default`.
|
|
14
|
+
2. **Message Sending & Viewing:**
|
|
15
|
+
- `clawmini messages send <message> [--chat <id>]`
|
|
16
|
+
- `clawmini messages tail [-n NUM] [--json] [--chat <id>]`
|
|
17
|
+
- Use a default chat if `--chat` is not specified.
|
|
18
|
+
- `tail` should have a nicely formatted text output by default, and a `--json` flag for raw data.
|
|
19
|
+
3. **Storage:**
|
|
20
|
+
- Chat history saved in `.clawmini/chats/<id>/chat.jsonl`.
|
|
21
|
+
- Each line is a JSON object representing a message (either sent by user or the response from the command).
|
|
22
|
+
- Schema ideas:
|
|
23
|
+
- User Input: `{ "role": "user", "content": "..." }`
|
|
24
|
+
- Command Output: `{ "role": "log", "content": "<stdout>", "stderr": "<stderr>", "timestamp": "...", "command": "...", "cwd": "..." }`
|
|
25
|
+
4. **Daemon Internals:**
|
|
26
|
+
- A new core function: `handleUserMessage(chat-id, message, settings) -> Promise<{command, directory, envvars}>`.
|
|
27
|
+
- Directory-based concurrency constraint: `handleUserMessage` must wait to resolve until any previously running command in the *same directory* has finished.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Product Requirements Document: Chats Feature
|
|
2
|
+
|
|
3
|
+
## 1. Vision
|
|
4
|
+
To enhance the `clawmini` CLI by introducing persistent, multi-session chat capabilities. Users will be able to manage multiple distinct conversation threads (chats) natively within the CLI, persist command histories locally, and execute background tasks safely with directory-based concurrency controls.
|
|
5
|
+
|
|
6
|
+
## 2. Product/Market Background
|
|
7
|
+
Currently, the `clawmini` tool is stateless from the perspective of user interactions. Users can send messages to a background daemon, which blindly executes a configured command. There's no way to maintain context, separate different workflows, or review the history of interactions. By introducing the concept of "chats," `clawmini` evolves into a stateful, interactive tool capable of managing complex, iterative tasks seamlessly.
|
|
8
|
+
|
|
9
|
+
## 3. Use Cases
|
|
10
|
+
1. **Context Separation:** A developer wants to maintain separate interaction histories for different tasks (e.g., "debugging-auth" vs. "writing-tests"). They can create different chats for each task.
|
|
11
|
+
2. **History Review:** A user wants to review what commands were generated and executed, along with their outputs, from a previous session. They can use the `messages tail` command to see the history.
|
|
12
|
+
3. **Safe Concurrency:** A user sends multiple messages rapidly that trigger commands in the same project directory. The system automatically queues these commands to prevent race conditions or file lock issues, ensuring they run sequentially. If a command fails, subsequent commands still run, and failure logs are preserved in the history.
|
|
13
|
+
4. **Resilience and Auditing:** If a background daemon restarts or a user closes their terminal, the state is preserved in local files, allowing workflows to resume or be audited later.
|
|
14
|
+
|
|
15
|
+
## 4. Requirements
|
|
16
|
+
|
|
17
|
+
### 4.1. Chat Management
|
|
18
|
+
* **`clawmini chats list`**: Displays all existing chats.
|
|
19
|
+
* **`clawmini chats add <id>`**: Creates a new chat with the specified identifier.
|
|
20
|
+
* **`clawmini chats delete <id>`**: Removes the chat and its associated history.
|
|
21
|
+
* **`clawmini chats set-default <id>`**: Updates the globally configured default chat for the workspace.
|
|
22
|
+
* **Default Chat**: The system should automatically initialize a default chat (e.g., `default`) when no chats exist.
|
|
23
|
+
|
|
24
|
+
### 4.2. Messaging and History
|
|
25
|
+
* **`clawmini messages send <message> [--chat <id>]`**: Sends a message to a specific chat. If `--chat` is omitted, it targets the default chat.
|
|
26
|
+
* **`clawmini messages tail [-n NUM] [--json] [--chat <id>]`**: Displays the most recent messages in a chat.
|
|
27
|
+
* `-n NUM`: Limits the output to the last NUM messages (default: all or a sensible limit like 20).
|
|
28
|
+
* `--json`: Outputs the raw JSONL data instead of human-readable formatted text.
|
|
29
|
+
* **Storage**:
|
|
30
|
+
* Chat logs must be stored in `.clawmini/chats/<id>/chat.jsonl`.
|
|
31
|
+
* Messages are appended as individual JSON objects.
|
|
32
|
+
* **Schema**:
|
|
33
|
+
* **User Message**: `{ "role": "user", "content": "<message>", "timestamp": "<ISO8601>" }`
|
|
34
|
+
* **Command Output**: `{ "role": "log", "content": "<stdout>", "stderr": "<stderr>", "timestamp": "<ISO8601>", "command": "<executed-cmd>", "cwd": "<working-dir>", "exitCode": <number> }`
|
|
35
|
+
|
|
36
|
+
### 4.3. Daemon Internals & Concurrency Control
|
|
37
|
+
* **`handleUserMessage(chatId, message, settings)`**: A new core function in the daemon router responsible for processing incoming messages.
|
|
38
|
+
* **Execution Strategy**:
|
|
39
|
+
* The daemon must resolve the command, working directory, and environment variables based on the message and settings.
|
|
40
|
+
* The daemon must maintain a queue or lock mechanism based on the target execution directory (`cwd`).
|
|
41
|
+
* A command must not start executing until all prior commands queued for the same directory have finished (either succeeded or failed).
|
|
42
|
+
* **Error Handling**: If a command exits with a non-zero code, it does *not* halt the queue for that directory. The next command in the queue proceeds. The failure must be recorded in the `chat.jsonl` log as a `{ role: "log" }` entry containing the exit code and stderr.
|
|
43
|
+
|
|
44
|
+
## 5. Security, Privacy, and Accessibility Concerns
|
|
45
|
+
* **Security**: Command execution must continue to safely pass user input via environment variables (`CLAW_CLI_MESSAGE`) or other safe mechanisms to prevent shell injection attacks.
|
|
46
|
+
* **Privacy**: Chat histories are stored completely locally in `.clawmini/chats/` within the user's workspace. No data is sent to external servers by default (unless explicitly configured by the user in `settings.json` command hooks).
|
|
47
|
+
* **Accessibility**: The CLI output (specifically `messages tail`) should be formatted with clear visual distinctions (e.g., colors or prefixes) between user inputs and system logs to improve readability for terminal users.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Questions and Answers
|
|
2
|
+
|
|
3
|
+
## Question 1
|
|
4
|
+
|
|
5
|
+
**Q:** When the `--chat <id>` flag is omitted, how is the default chat determined? Should we always fall back to a built-in ID like `default` that is created automatically, or should it be the most recently used/created chat?
|
|
6
|
+
|
|
7
|
+
**A:** Let's have a default ID that is created automatically, but the user can change with `set-default`.
|
|
8
|
+
|
|
9
|
+
## Question 2
|
|
10
|
+
|
|
11
|
+
**Q:** Should the user's input `message` and the command's response both be stored in `.clawmini/chats/<id>/chat.jsonl`? If so, what should the basic schema look like for those JSON objects? For instance, `{ "role": "user", "content": "..." }` and `{ "role": "system", "content": "stdout/stderr of command" }`?
|
|
12
|
+
|
|
13
|
+
**A:** Yes, exactly. let's call outputs from the command `{role: "log", content: <stdout>, stderr: <stderr>}`. Also think about other relevant info we should store like timestamps, the chat-id + command + directory chosen, etc.
|
|
14
|
+
|
|
15
|
+
## Question 3
|
|
16
|
+
|
|
17
|
+
**Q:** For the directory-based concurrency limit (waiting for previous commands in the same directory to finish), what should happen if a previous command fails (exits with a non-zero code)? Should the next command in the queue still execute, or should it be aborted/rejected?
|
|
18
|
+
|
|
19
|
+
**A:** It should still execute. If a command fails, a `{role: log}` message should be added to the queue with notes about the failure.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Implementation Tickets: Chats Feature
|
|
2
|
+
|
|
3
|
+
## Step 1: Core Chat Data Types & Storage Utilities
|
|
4
|
+
**Description**: Define the core data structures for chats and messages, and implement the file-system utilities to read/write chat history in JSONL format.
|
|
5
|
+
**Tasks**:
|
|
6
|
+
- Define TypeScript interfaces for `UserMessage` and `CommandLogMessage` based on the PRD schema.
|
|
7
|
+
- Implement storage utilities (e.g., in `src/shared/chats.ts`) for managing `.clawmini/chats/<id>/chat.jsonl`.
|
|
8
|
+
- Functions needed: `createChat(id)`, `listChats()`, `deleteChat(id)`, `appendMessage(id, message)`, `getMessages(id, limit)`.
|
|
9
|
+
- Implement global default chat tracking (e.g., in `settings.json` or a separate state file).
|
|
10
|
+
**Verification**:
|
|
11
|
+
- Write unit tests verifying JSONL reading/writing (appending correctly, parsing correctly).
|
|
12
|
+
- Write unit tests for directory creation and deletion for chats.
|
|
13
|
+
- Run type-checking (`tsc`) and linter.
|
|
14
|
+
**Status**: complete
|
|
15
|
+
|
|
16
|
+
## Step 2: Daemon Concurrency & Execution Logic
|
|
17
|
+
**Description**: Update the background daemon to handle messages targeted at specific chats and introduce directory-based concurrency control.
|
|
18
|
+
**Tasks**:
|
|
19
|
+
- Implement `handleUserMessage(chatId, message, settings)` in the daemon.
|
|
20
|
+
- Create a directory-based queue mechanism. Ensure commands targeting the same `cwd` run sequentially, while commands for different directories can run concurrently.
|
|
21
|
+
- Capture command stdout, stderr, and exit code, and append them as a `log` message to the corresponding chat's `chat.jsonl`.
|
|
22
|
+
- Do not halt the queue on non-zero exit codes.
|
|
23
|
+
**Verification**:
|
|
24
|
+
- Write unit tests for the execution queue, mocking command execution to verify sequential vs concurrent execution based on `cwd`.
|
|
25
|
+
- Verify failure logs (exitCode, stderr) are written correctly even if a command fails.
|
|
26
|
+
- Run type-checking (`tsc`) and linter.
|
|
27
|
+
**Status**: complete
|
|
28
|
+
|
|
29
|
+
## Step 3: CLI Chat Management Commands
|
|
30
|
+
**Description**: Implement the `clawmini chats` CLI commands for users to manage their chat sessions.
|
|
31
|
+
**Tasks**:
|
|
32
|
+
- Implement `clawmini chats list` to display existing chats.
|
|
33
|
+
- Implement `clawmini chats add <id>` to initialize a new chat.
|
|
34
|
+
- Implement `clawmini chats delete <id>` to remove a chat.
|
|
35
|
+
- Implement `clawmini chats set-default <id>` to update the workspace's default chat.
|
|
36
|
+
**Verification**:
|
|
37
|
+
- Write E2E tests using `node:child_process.spawn` pointing to `dist/cli/index.mjs`.
|
|
38
|
+
- Run commands in an isolated sandbox temporary directory (`cwd: e2eDir`).
|
|
39
|
+
- Verify the CLI output and the creation/deletion of `.clawmini/chats/<id>` directories.
|
|
40
|
+
- Explicitly tear down E2E tests (e.g., `pkill -f "dist/daemon/index.mjs"`) in the `afterAll` hook.
|
|
41
|
+
- Run type-checking (`tsc`) and linter.
|
|
42
|
+
**Status**: complete
|
|
43
|
+
|
|
44
|
+
## Step 4: CLI Messaging and History Commands
|
|
45
|
+
**Description**: Update the existing `send` command to support chats and implement the `tail` command for viewing history.
|
|
46
|
+
**Tasks**:
|
|
47
|
+
- Update `clawmini messages send <message> [--chat <id>]` to route the message to the specified chat (or the default one).
|
|
48
|
+
- Implement `clawmini messages tail [-n NUM] [--json] [--chat <id>]`.
|
|
49
|
+
- Format `tail` output for human readability by default, and output raw JSONL if `--json` is passed.
|
|
50
|
+
**Verification**:
|
|
51
|
+
- Write E2E tests sending messages to a specific chat and verifying they are properly stored.
|
|
52
|
+
- Write E2E tests for `tail` command, verifying both formatted text output and `--json` raw output.
|
|
53
|
+
- Write E2E tests verifying the `--chat` flag correctly overrides the default chat.
|
|
54
|
+
- Run tests in an isolated sandbox directory, with proper daemon teardown in `afterAll`.
|
|
55
|
+
- Run type-checking (`tsc`) and linter.
|
|
56
|
+
**Status**: complete
|
|
57
|
+
|
|
58
|
+
## Step 5: Background Messaging
|
|
59
|
+
**Description**: Support returning immediately from `messages send` using a `--no-wait` flag. By default, it should wait for the response to finish. The server should queue the message and send it when ready.
|
|
60
|
+
**Tasks**:
|
|
61
|
+
- Update `messages send` to accept `--no-wait`.
|
|
62
|
+
- Update `sendMessage` trpc mutation to accept an optional `noWait` boolean.
|
|
63
|
+
- Update `handleUserMessage` in `src/daemon/queue.ts` to skip awaiting the task execution if `noWait` is true.
|
|
64
|
+
**Verification**:
|
|
65
|
+
- Verify tests.
|
|
66
|
+
- Add E2E test case for `--no-wait` flag.
|
|
67
|
+
**Status**: complete
|