clawmini 0.0.3 → 0.0.5
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/README.md +19 -0
- package/dist/adapter-discord/index.d.mts.map +1 -1
- package/dist/adapter-discord/index.mjs +398 -193
- package/dist/adapter-discord/index.mjs.map +1 -1
- package/dist/adapter-google-chat/index.d.mts +5 -0
- package/dist/adapter-google-chat/index.d.mts.map +1 -0
- package/dist/adapter-google-chat/index.mjs +1077 -0
- package/dist/adapter-google-chat/index.mjs.map +1 -0
- package/dist/cli/index.mjs +107 -14
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lite.mjs +175 -16
- package/dist/cli/lite.mjs.map +1 -1
- package/dist/cli/propose-policy.d.mts +1 -0
- package/dist/cli/propose-policy.mjs +7159 -0
- package/dist/cli/propose-policy.mjs.map +1 -0
- package/dist/daemon/index.d.mts.map +1 -1
- package/dist/daemon/index.mjs +1427 -513
- package/dist/daemon/index.mjs.map +1 -1
- package/dist/{lite-oSYSvaOr.mjs → lite-CBxOT1y5.mjs} +101 -24
- package/dist/lite-CBxOT1y5.mjs.map +1 -0
- package/dist/routing-D8rTxtaV.mjs +245 -0
- package/dist/routing-D8rTxtaV.mjs.map +1 -0
- package/dist/web/_app/immutable/assets/0.C-4eziNy.css +1 -0
- package/dist/web/_app/immutable/assets/4.Cc_xwLNl.css +1 -0
- package/dist/web/_app/immutable/chunks/B6YN0Nuq.js +1 -0
- package/dist/web/_app/immutable/chunks/{Dc-UOHw9.js → BmRlVmv6.js} +1 -1
- package/{web/.svelte-kit/output/client/_app/immutable/chunks/8YNcRyEk.js → dist/web/_app/immutable/chunks/C20lZMGz.js} +1 -1
- package/dist/web/_app/immutable/chunks/C9lbZ-kT.js +1 -0
- package/dist/web/_app/immutable/chunks/CK9JZLaG.js +2 -0
- package/dist/web/_app/immutable/chunks/CME08kGM.js +1 -0
- package/dist/web/_app/immutable/chunks/{BPy8HLo7.js → Ck-be5J2.js} +1 -1
- package/dist/web/_app/immutable/chunks/Ck3rYNON.js +1 -0
- package/dist/web/_app/immutable/chunks/DMtIqaiV.js +2 -0
- package/dist/web/_app/immutable/chunks/{B8yYFADm.js → DhD271EB.js} +1 -1
- package/dist/web/_app/immutable/chunks/{DcrmIfTj.js → DpuLqk8d.js} +1 -1
- package/dist/web/_app/immutable/chunks/{ZkLyk0mE.js → Drm9vgeP.js} +1 -1
- package/dist/web/_app/immutable/chunks/DsIToJCP.js +1 -0
- package/dist/web/_app/immutable/chunks/{CyNaE55B.js → Zeh-C-mx.js} +1 -1
- package/{web/.svelte-kit/output/client/_app/immutable/entry/app.DO5eYwVz.js → dist/web/_app/immutable/entry/app.BgB5VkRU.js} +2 -2
- package/dist/web/_app/immutable/entry/start.DuxJo6av.js +1 -0
- package/dist/web/_app/immutable/nodes/0.C9oFZP9h.js +1 -0
- package/dist/web/_app/immutable/nodes/1.BON2Wk6k.js +1 -0
- package/dist/web/_app/immutable/nodes/{2.CK3CLC0f.js → 2.BnwnD1Ki.js} +1 -1
- package/dist/web/_app/immutable/nodes/{3.ncP0xLO6.js → 3.CIs4tjjw.js} +1 -1
- package/dist/web/_app/immutable/nodes/4.DLarELN4.js +60 -0
- package/dist/web/_app/immutable/nodes/{5.BpJUN6QH.js → 5.CE_QKy_3.js} +1 -1
- package/dist/web/_app/version.json +1 -1
- package/dist/web/index.html +12 -12
- package/dist/{workspace-DjoNjhW0.mjs → workspace-BJmJBfKi.mjs} +103 -11
- package/dist/workspace-BJmJBfKi.mjs.map +1 -0
- package/docs/14_google_chat_adapter/development_log.md +40 -0
- package/docs/14_google_chat_adapter/notes.md +28 -0
- package/docs/14_google_chat_adapter/prd.md +35 -0
- package/docs/14_google_chat_adapter/questions.md +9 -0
- package/docs/14_google_chat_adapter/tickets.md +117 -0
- package/docs/15_sandbox_policies/tickets.md +33 -0
- package/docs/16_session_timeout/development_log.md +20 -0
- package/docs/16_session_timeout/notes.md +44 -0
- package/docs/16_session_timeout/prd.md +106 -0
- package/docs/16_session_timeout/questions.md +10 -0
- package/docs/16_session_timeout/tickets.md +64 -0
- package/docs/17_auto_approve_policy/development_log.md +29 -0
- package/docs/17_auto_approve_policy/notes.md +25 -0
- package/docs/17_auto_approve_policy/prd.md +34 -0
- package/docs/17_auto_approve_policy/questions.md +10 -0
- package/docs/17_auto_approve_policy/tickets.md +11 -0
- package/docs/18_clawmini_skills/development_log.md +36 -0
- package/docs/18_clawmini_skills/notes.md +8 -0
- package/docs/18_clawmini_skills/prd.md +45 -0
- package/docs/18_clawmini_skills/questions.md +10 -0
- package/docs/18_clawmini_skills/tickets.md +55 -0
- package/docs/19_subagents/development_log.md +69 -0
- package/docs/19_subagents/notes.md +18 -0
- package/docs/19_subagents/prd.md +156 -0
- package/docs/19_subagents/questions.md +13 -0
- package/docs/19_subagents/tickets.md +113 -0
- package/docs/20_chat_logs_cleanup/development_log.md +50 -0
- package/docs/20_chat_logs_cleanup/notes.md +43 -0
- package/docs/20_chat_logs_cleanup/prd.md +232 -0
- package/docs/20_chat_logs_cleanup/questions.md +2 -0
- package/docs/20_chat_logs_cleanup/tickets.md +98 -0
- package/docs/20_webui_markdown/development_log.md +36 -0
- package/docs/20_webui_markdown/notes.md +23 -0
- package/docs/20_webui_markdown/prd.md +49 -0
- package/docs/20_webui_markdown/questions.md +10 -0
- package/docs/20_webui_markdown/tickets.md +55 -0
- package/docs/21_adapter_filtering/development_log.md +29 -0
- package/docs/21_adapter_filtering/notes.md +25 -0
- package/docs/21_adapter_filtering/prd.md +44 -0
- package/docs/21_adapter_filtering/questions.md +12 -0
- package/docs/21_adapter_filtering/tickets.md +38 -0
- package/docs/21_built_in_routers/development_log.md +17 -0
- package/docs/21_built_in_routers/notes.md +27 -0
- package/docs/21_built_in_routers/prd.md +34 -0
- package/docs/21_built_in_routers/questions.md +4 -0
- package/docs/21_built_in_routers/tickets.md +25 -0
- package/docs/21_fancy_policies/development_log.md +38 -0
- package/docs/21_fancy_policies/notes.md +27 -0
- package/docs/21_fancy_policies/prd.md +58 -0
- package/docs/21_fancy_policies/questions.md +6 -0
- package/docs/21_fancy_policies/tickets.md +48 -0
- package/docs/22_adapter_multi_chat/development_log.md +76 -0
- package/docs/22_adapter_multi_chat/notes.md +42 -0
- package/docs/22_adapter_multi_chat/prd.md +76 -0
- package/docs/22_adapter_multi_chat/questions.md +16 -0
- package/docs/22_adapter_multi_chat/tickets.md +164 -0
- package/docs/23_custom_token_env/development_log.md +31 -0
- package/docs/23_custom_token_env/notes.md +16 -0
- package/docs/23_custom_token_env/prd.md +42 -0
- package/docs/23_custom_token_env/questions.md +8 -0
- package/docs/23_custom_token_env/tickets.md +54 -0
- package/docs/guides/discord_adapter_setup.md +15 -2
- package/docs/guides/google_chat_adapter_setup.md +145 -0
- package/napkin.md +5 -0
- package/package.json +7 -2
- package/src/adapter-discord/config.test.ts +27 -8
- package/src/adapter-discord/config.ts +6 -8
- package/src/adapter-discord/forwarder.test.ts +307 -114
- package/src/adapter-discord/forwarder.ts +260 -75
- package/src/adapter-discord/index.test.ts +278 -0
- package/src/adapter-discord/index.ts +160 -30
- package/src/adapter-discord/interactions.test.ts +96 -0
- package/src/adapter-discord/interactions.ts +156 -0
- package/src/adapter-discord/state.test.ts +9 -8
- package/src/adapter-discord/state.ts +51 -8
- package/src/adapter-google-chat/auth.test.ts +87 -0
- package/src/adapter-google-chat/auth.ts +132 -0
- package/src/adapter-google-chat/cards.ts +71 -0
- package/src/adapter-google-chat/client.test.ts +561 -0
- package/src/adapter-google-chat/client.ts +430 -0
- package/src/adapter-google-chat/config.test.ts +187 -0
- package/src/adapter-google-chat/config.ts +82 -0
- package/src/adapter-google-chat/cron.test.ts +143 -0
- package/src/adapter-google-chat/cron.ts +81 -0
- package/src/adapter-google-chat/forwarder.test.ts +537 -0
- package/src/adapter-google-chat/forwarder.ts +349 -0
- package/src/adapter-google-chat/index.test.ts +62 -0
- package/src/adapter-google-chat/index.ts +61 -0
- package/src/adapter-google-chat/state.test.ts +96 -0
- package/src/adapter-google-chat/state.ts +85 -0
- package/src/adapter-google-chat/subscriptions.ts +124 -0
- package/src/adapter-google-chat/upload.ts +88 -0
- package/src/adapter-google-chat/utils.test.ts +111 -0
- package/src/adapter-google-chat/utils.ts +133 -0
- package/src/cli/commands/init.ts +0 -7
- package/src/cli/commands/messages.ts +18 -3
- package/src/cli/commands/policies.ts +70 -0
- package/src/cli/commands/skills.ts +71 -0
- package/src/cli/commands/web-api/chats.ts +5 -1
- package/src/cli/e2e/basic.test.ts +1 -1
- package/src/cli/e2e/cron.test.ts +1 -1
- package/src/cli/e2e/daemon.test.ts +132 -4
- package/src/cli/e2e/export-lite-func.test.ts +54 -31
- package/src/cli/e2e/fallbacks.test.ts +8 -6
- package/src/cli/e2e/init.test.ts +7 -0
- package/src/cli/e2e/messages.test.ts +90 -55
- package/src/cli/e2e/propose-policy.test.ts +203 -0
- package/src/cli/e2e/requests.test.ts +15 -0
- package/src/cli/e2e/session-timeout.test.ts +192 -0
- package/src/cli/e2e/skills.test.ts +55 -0
- package/src/cli/e2e/slash-new.test.ts +93 -0
- package/src/cli/e2e/subagents.test.ts +106 -0
- package/src/cli/index.ts +4 -0
- package/src/cli/lite.ts +51 -11
- package/src/cli/propose-policy.ts +91 -0
- package/src/cli/subagent-commands.ts +215 -0
- package/src/daemon/agent/agent-context.ts +89 -0
- package/src/daemon/agent/agent-extractors.ts +68 -0
- package/src/daemon/agent/agent-runner.ts +153 -0
- package/src/daemon/agent/agent-session.ts +261 -0
- package/src/daemon/agent/chat-logger.test.ts +158 -0
- package/src/daemon/agent/chat-logger.ts +188 -0
- package/src/daemon/agent/task-scheduler.test.ts +202 -0
- package/src/daemon/agent/task-scheduler.ts +276 -0
- package/src/daemon/agent/types.ts +84 -0
- package/src/daemon/agent/utils.ts +7 -0
- package/src/daemon/api/agent-router.ts +166 -18
- package/src/daemon/api/index.test.ts +50 -18
- package/src/daemon/api/policy-request.test.ts +39 -2
- package/src/daemon/api/subagent-router.test.ts +108 -0
- package/src/daemon/api/subagent-router.ts +296 -0
- package/src/daemon/api/subagent-utils.test.ts +56 -0
- package/src/daemon/api/subagent-utils.ts +130 -0
- package/src/daemon/api/user-router.ts +30 -13
- package/src/daemon/auth.ts +1 -0
- package/src/daemon/chats.ts +6 -0
- package/src/daemon/cron.test.ts +66 -1
- package/src/daemon/cron.ts +35 -8
- package/src/daemon/index.ts +23 -0
- package/src/daemon/message-agent.test.ts +11 -25
- package/src/daemon/message-extraction.test.ts +10 -27
- package/src/daemon/message-fallbacks.test.ts +13 -35
- package/src/daemon/message-interruption.test.ts +70 -53
- package/src/daemon/message-jobs.test.ts +138 -0
- package/src/daemon/message-queue.test.ts +30 -43
- package/src/daemon/message-router.test.ts +12 -11
- package/src/daemon/message-session.test.ts +41 -28
- package/src/daemon/message-typing.test.ts +19 -6
- package/src/daemon/message.ts +103 -515
- package/src/daemon/policy-request-service.ts +8 -3
- package/src/daemon/policy-utils.ts +19 -1
- package/src/daemon/queue.ts +16 -0
- package/src/daemon/request-store.test.ts +4 -0
- package/src/daemon/routers/session-timeout.test.ts +122 -0
- package/src/daemon/routers/session-timeout.ts +71 -0
- package/src/daemon/routers/slash-new.ts +3 -1
- package/src/daemon/routers/slash-policies.test.ts +26 -13
- package/src/daemon/routers/slash-policies.ts +39 -29
- package/src/daemon/routers/types.ts +8 -0
- package/src/daemon/routers.ts +64 -2
- package/src/daemon/utils/spawn.ts +6 -8
- package/src/shared/adapters/commands.test.ts +155 -0
- package/src/shared/adapters/commands.ts +125 -0
- package/src/shared/adapters/filtering.test.ts +111 -0
- package/src/shared/adapters/filtering.ts +57 -0
- package/src/shared/adapters/routing.test.ts +144 -0
- package/src/shared/adapters/routing.ts +109 -0
- package/src/shared/agent-utils.ts +10 -0
- package/src/shared/chats.test.ts +145 -3
- package/src/shared/chats.ts +215 -18
- package/src/shared/config.ts +67 -15
- package/src/shared/lite.ts +22 -18
- package/src/shared/policies.ts +7 -0
- package/src/shared/workspace.test.ts +45 -1
- package/src/shared/workspace.ts +119 -6
- package/templates/debug/settings.json +5 -2
- package/templates/environments/cladding/env.json +2 -2
- package/templates/gemini/.gemini/hooks/check-subagents.mjs +23 -0
- package/templates/gemini/.gemini/hooks/clawmini-logging.sh +17 -0
- package/templates/gemini/.gemini/hooks/insert-pending.sh +9 -0
- package/templates/gemini/.gemini/settings.json +50 -0
- package/templates/gemini/settings.json +22 -8
- package/templates/gemini-claw/.gemini/base-system.md +100 -0
- package/templates/gemini-claw/.gemini/hooks/check-subagents.mjs +23 -0
- package/templates/gemini-claw/.gemini/hooks/clawmini-logging.sh +1 -1
- package/templates/gemini-claw/.gemini/settings.json +13 -0
- package/templates/gemini-claw/.gemini/subagent-system.md +7 -0
- package/templates/gemini-claw/.gemini/system.md +3 -99
- package/templates/gemini-claw/settings.json +27 -22
- package/templates/skills/clawmini-requests/SKILL.md +92 -0
- package/templates/skills/clawmini-subagents/SKILL.md +79 -0
- package/templates/skills/skill-creator/SKILL.md +60 -0
- package/tsdown.config.ts +10 -1
- package/web/.svelte-kit/generated/server/internal.js +2 -1
- package/web/.svelte-kit/non-ambient.d.ts +2 -0
- package/web/.svelte-kit/output/client/.vite/manifest.json +141 -138
- package/web/.svelte-kit/output/client/_app/immutable/assets/0.C-4eziNy.css +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/assets/4.Cc_xwLNl.css +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/B6YN0Nuq.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{Dc-UOHw9.js → BmRlVmv6.js} +1 -1
- package/{dist/web/_app/immutable/chunks/8YNcRyEk.js → web/.svelte-kit/output/client/_app/immutable/chunks/C20lZMGz.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/C9lbZ-kT.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/CK9JZLaG.js +2 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/CME08kGM.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{BPy8HLo7.js → Ck-be5J2.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/Ck3rYNON.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DMtIqaiV.js +2 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{B8yYFADm.js → DhD271EB.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{DcrmIfTj.js → DpuLqk8d.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{ZkLyk0mE.js → Drm9vgeP.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DsIToJCP.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{CyNaE55B.js → Zeh-C-mx.js} +1 -1
- package/{dist/web/_app/immutable/entry/app.DO5eYwVz.js → web/.svelte-kit/output/client/_app/immutable/entry/app.BgB5VkRU.js} +2 -2
- package/web/.svelte-kit/output/client/_app/immutable/entry/start.DuxJo6av.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/0.C9oFZP9h.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/1.BON2Wk6k.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{2.CK3CLC0f.js → 2.BnwnD1Ki.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{3.ncP0xLO6.js → 3.CIs4tjjw.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/4.DLarELN4.js +60 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{5.BpJUN6QH.js → 5.CE_QKy_3.js} +1 -1
- package/web/.svelte-kit/output/client/_app/version.json +1 -1
- package/web/.svelte-kit/output/server/.vite/manifest.json +12 -3
- package/web/.svelte-kit/output/server/_app/immutable/assets/_layout.C-4eziNy.css +1 -0
- package/web/.svelte-kit/output/server/_app/immutable/assets/_page.Cc_xwLNl.css +1 -0
- package/web/.svelte-kit/output/server/chunks/app-state.svelte.js +5 -0
- package/web/.svelte-kit/output/server/chunks/bot.js +4 -4
- package/web/.svelte-kit/output/server/chunks/client.js +2 -1
- package/web/.svelte-kit/output/server/chunks/exports.js +0 -1
- package/web/.svelte-kit/output/server/chunks/internal.js +2 -1
- package/web/.svelte-kit/output/server/chunks/root.js +482 -392
- package/web/.svelte-kit/output/server/entries/pages/_layout.svelte.js +57 -7
- package/web/.svelte-kit/output/server/entries/pages/chats/_id_/_page.svelte.js +234 -9
- package/web/.svelte-kit/output/server/index.js +82 -10
- 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 +2 -2
- 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 +2 -2
- package/web/.svelte-kit/output/server/nodes/5.js +1 -1
- package/web/.svelte-kit/types/src/routes/$types.d.ts +1 -2
- package/web/.svelte-kit/types/src/routes/agents/$types.d.ts +1 -2
- package/web/.svelte-kit/types/src/routes/chats/[id]/$types.d.ts +1 -2
- package/web/.svelte-kit/types/src/routes/chats/[id]/settings/$types.d.ts +1 -2
- package/web/package.json +8 -0
- package/web/src/lib/app-state.svelte.ts +5 -1
- package/web/src/lib/components/app/markdown-renderer.svelte +56 -0
- package/web/src/lib/components/app/markdown-renderer.svelte.spec.ts +44 -0
- package/web/src/lib/components/app/message-content.svelte +16 -0
- package/web/src/lib/types.ts +67 -3
- package/web/src/routes/+layout.svelte +31 -1
- package/web/src/routes/chats/[id]/+page.svelte +167 -18
- package/web/src/routes/chats/[id]/page.svelte.spec.ts +58 -7
- package/dist/lite-oSYSvaOr.mjs.map +0 -1
- package/dist/web/_app/immutable/assets/0.GI4C4dpV.css +0 -1
- package/dist/web/_app/immutable/chunks/B5abRDXp.js +0 -1
- package/dist/web/_app/immutable/chunks/Bi0jeV7Q.js +0 -1
- package/dist/web/_app/immutable/chunks/BmUXQ3wy.js +0 -2
- package/dist/web/_app/immutable/chunks/C3k55nDF.js +0 -1
- package/dist/web/_app/immutable/chunks/CpaGRn9L.js +0 -1
- package/dist/web/_app/immutable/chunks/DG5RZBw-.js +0 -2
- package/dist/web/_app/immutable/chunks/DQoygso7.js +0 -1
- package/dist/web/_app/immutable/entry/start.D48mVn1m.js +0 -1
- package/dist/web/_app/immutable/nodes/0.B-0CcADM.js +0 -1
- package/dist/web/_app/immutable/nodes/1.FixKgvRO.js +0 -1
- package/dist/web/_app/immutable/nodes/4.CQYJEgv8.js +0 -1
- package/dist/workspace-DjoNjhW0.mjs.map +0 -1
- package/src/daemon/message-verbosity.test.ts +0 -127
- package/web/.svelte-kit/output/client/_app/immutable/assets/0.GI4C4dpV.css +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/B5abRDXp.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/Bi0jeV7Q.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BmUXQ3wy.js +0 -2
- package/web/.svelte-kit/output/client/_app/immutable/chunks/C3k55nDF.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/CpaGRn9L.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DG5RZBw-.js +0 -2
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DQoygso7.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/entry/start.D48mVn1m.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/0.B-0CcADM.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/1.FixKgvRO.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/4.CQYJEgv8.js +0 -1
- package/web/.svelte-kit/output/server/_app/immutable/assets/_layout.GI4C4dpV.css +0 -1
- /package/templates/{gemini-claw/.gemini/skills → skills}/clawmini-jobs/SKILL.md +0 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { google } from 'googleapis';
|
|
2
|
+
import type { GoogleChatConfig } from './config.js';
|
|
3
|
+
import { getAuthClient, getUserAuthClient } from './auth.js';
|
|
4
|
+
import { updateGoogleChatState, type GoogleChatState } from './state.js';
|
|
5
|
+
|
|
6
|
+
export async function handleAddedToSpace(
|
|
7
|
+
spaceName: string,
|
|
8
|
+
externalContextId: string,
|
|
9
|
+
spaceType: string | undefined,
|
|
10
|
+
targetChatId: string | null | undefined,
|
|
11
|
+
mappedChatId: string | null | undefined,
|
|
12
|
+
config: GoogleChatConfig
|
|
13
|
+
) {
|
|
14
|
+
if (spaceType !== 'DIRECT_MESSAGE') {
|
|
15
|
+
try {
|
|
16
|
+
const userAuthClient = await getUserAuthClient(config);
|
|
17
|
+
const tokenResponse = await userAuthClient.getAccessToken();
|
|
18
|
+
const token = tokenResponse.token;
|
|
19
|
+
|
|
20
|
+
if (token) {
|
|
21
|
+
const res = await fetch('https://workspaceevents.googleapis.com/v1/subscriptions', {
|
|
22
|
+
method: 'POST',
|
|
23
|
+
headers: {
|
|
24
|
+
Authorization: `Bearer ${token}`,
|
|
25
|
+
'Content-Type': 'application/json',
|
|
26
|
+
},
|
|
27
|
+
body: JSON.stringify({
|
|
28
|
+
targetResource: `//chat.googleapis.com/${spaceName}`,
|
|
29
|
+
eventTypes: ['google.workspace.chat.message.v1.created'],
|
|
30
|
+
payloadOptions: { includeResource: true },
|
|
31
|
+
notificationEndpoint: {
|
|
32
|
+
pubsubTopic: `projects/${config.projectId}/topics/${config.topicName}`,
|
|
33
|
+
},
|
|
34
|
+
}),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (res.ok) {
|
|
38
|
+
const subData = (await res.json()) as { name: string; expireTime: string };
|
|
39
|
+
await updateGoogleChatState((latestState) => {
|
|
40
|
+
const currentMap = latestState.channelChatMap || {};
|
|
41
|
+
return {
|
|
42
|
+
channelChatMap: {
|
|
43
|
+
...currentMap,
|
|
44
|
+
[externalContextId]: {
|
|
45
|
+
...(currentMap[externalContextId] || {}),
|
|
46
|
+
subscriptionId: subData.name,
|
|
47
|
+
expirationDate: subData.expireTime,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
console.log(`Created subscription ${subData.name} for space ${externalContextId}`);
|
|
53
|
+
} else {
|
|
54
|
+
const errText = await res.text();
|
|
55
|
+
console.error(`Failed to create subscription for space ${externalContextId}:`, errText);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
} catch (err) {
|
|
59
|
+
console.error('Error setting up subscription on ADDED_TO_SPACE:', err);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (targetChatId && mappedChatId) {
|
|
64
|
+
try {
|
|
65
|
+
const authClient = await getAuthClient();
|
|
66
|
+
const chatApi = google.chat({ version: 'v1', auth: authClient });
|
|
67
|
+
await chatApi.spaces.messages.create({
|
|
68
|
+
parent: externalContextId,
|
|
69
|
+
requestBody: {
|
|
70
|
+
text: `Hello! I am currently mapped to chat \`${targetChatId}\`.`,
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
} catch (err) {
|
|
74
|
+
console.error('Failed to send greeting on ADDED_TO_SPACE:', err);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export async function handleRemovedFromSpace(
|
|
80
|
+
externalContextId: string,
|
|
81
|
+
currentState: GoogleChatState,
|
|
82
|
+
config: GoogleChatConfig
|
|
83
|
+
) {
|
|
84
|
+
const subId = currentState.channelChatMap?.[externalContextId]?.subscriptionId;
|
|
85
|
+
if (subId) {
|
|
86
|
+
try {
|
|
87
|
+
const userAuthClient = await getUserAuthClient(config);
|
|
88
|
+
const tokenResponse = await userAuthClient.getAccessToken();
|
|
89
|
+
const token = tokenResponse.token;
|
|
90
|
+
|
|
91
|
+
if (token) {
|
|
92
|
+
const res = await fetch(`https://workspaceevents.googleapis.com/v1/${subId}`, {
|
|
93
|
+
method: 'DELETE',
|
|
94
|
+
headers: {
|
|
95
|
+
Authorization: `Bearer ${token}`,
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
if (res.ok) {
|
|
100
|
+
console.log(`Deleted subscription ${subId}`);
|
|
101
|
+
} else {
|
|
102
|
+
const errText = await res.text();
|
|
103
|
+
console.error(`Failed to delete subscription ${subId}:`, errText);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
} catch (err) {
|
|
107
|
+
console.error('Error tearing down subscription on REMOVED_FROM_SPACE:', err);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
await updateGoogleChatState((latestState) => {
|
|
112
|
+
const map = { ...(latestState.channelChatMap || {}) };
|
|
113
|
+
const entry = map[externalContextId];
|
|
114
|
+
if (entry) {
|
|
115
|
+
if (!entry.chatId) {
|
|
116
|
+
delete map[externalContextId];
|
|
117
|
+
} else {
|
|
118
|
+
delete entry.subscriptionId;
|
|
119
|
+
delete entry.expirationDate;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return { channelChatMap: map };
|
|
123
|
+
});
|
|
124
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { google } from 'googleapis';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import mime from 'mime-types';
|
|
5
|
+
import type { GoogleChatConfig } from './config.js';
|
|
6
|
+
import { getUserAuthClient } from './auth.js';
|
|
7
|
+
import { getWorkspaceRoot } from '../shared/workspace.js';
|
|
8
|
+
|
|
9
|
+
export async function uploadFilesToDrive(
|
|
10
|
+
files: string[],
|
|
11
|
+
config: GoogleChatConfig
|
|
12
|
+
): Promise<string[]> {
|
|
13
|
+
const driveClient = await getUserAuthClient(config);
|
|
14
|
+
const driveApi = google.drive({ version: 'v3', auth: driveClient });
|
|
15
|
+
const workspaceRoot = getWorkspaceRoot(process.cwd());
|
|
16
|
+
|
|
17
|
+
let folderId: string | undefined;
|
|
18
|
+
try {
|
|
19
|
+
const queryRes = await driveApi.files.list({
|
|
20
|
+
q: "mimeType='application/vnd.google-apps.folder' and name='Clawmini Uploads' and trashed=false",
|
|
21
|
+
fields: 'files(id)',
|
|
22
|
+
});
|
|
23
|
+
if (queryRes.data.files && queryRes.data.files.length > 0) {
|
|
24
|
+
folderId = queryRes.data.files[0]!.id!;
|
|
25
|
+
} else {
|
|
26
|
+
const folderRes = await driveApi.files.create({
|
|
27
|
+
requestBody: {
|
|
28
|
+
name: 'Clawmini Uploads',
|
|
29
|
+
mimeType: 'application/vnd.google-apps.folder',
|
|
30
|
+
},
|
|
31
|
+
fields: 'id',
|
|
32
|
+
});
|
|
33
|
+
if (folderRes.data.id) {
|
|
34
|
+
folderId = folderRes.data.id;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
} catch (err) {
|
|
38
|
+
console.error('Failed to create or find Clawmini Uploads folder', err);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const uploadPromises = files.map(async (fileRelPath) => {
|
|
42
|
+
const filePath = path.resolve(workspaceRoot, fileRelPath);
|
|
43
|
+
if (!fs.existsSync(filePath)) return null;
|
|
44
|
+
|
|
45
|
+
const fileName = path.basename(filePath);
|
|
46
|
+
const mimeType = mime.lookup(filePath) || 'application/octet-stream';
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const driveRes = await driveApi.files.create({
|
|
50
|
+
requestBody: {
|
|
51
|
+
name: fileName,
|
|
52
|
+
...(folderId ? { parents: [folderId] } : {}),
|
|
53
|
+
},
|
|
54
|
+
media: { mimeType, body: fs.createReadStream(filePath) },
|
|
55
|
+
fields: 'id, webViewLink',
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
if (driveRes.data.id && driveRes.data.webViewLink) {
|
|
59
|
+
const fileId = driveRes.data.id;
|
|
60
|
+
try {
|
|
61
|
+
await Promise.all(
|
|
62
|
+
config.authorizedUsers.map((email) =>
|
|
63
|
+
driveApi.permissions.create({
|
|
64
|
+
fileId,
|
|
65
|
+
requestBody: {
|
|
66
|
+
type: 'user',
|
|
67
|
+
role: 'reader',
|
|
68
|
+
emailAddress: email,
|
|
69
|
+
},
|
|
70
|
+
sendNotificationEmail: false,
|
|
71
|
+
})
|
|
72
|
+
)
|
|
73
|
+
);
|
|
74
|
+
} catch (err) {
|
|
75
|
+
console.error(`Failed to grant permissions for ${fileName}`, err);
|
|
76
|
+
}
|
|
77
|
+
return driveRes.data.webViewLink;
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
} catch (err) {
|
|
81
|
+
console.error(`Failed to upload file ${fileName} to Google Drive`, err);
|
|
82
|
+
return `*(Failed to upload to Drive: ${fileName})*`;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const uploadResults = await Promise.all(uploadPromises);
|
|
87
|
+
return uploadResults.filter((r) => r !== null) as string[];
|
|
88
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { downloadAttachment, resetAuthClient } from './utils.js';
|
|
3
|
+
import { google } from 'googleapis';
|
|
4
|
+
import { EventEmitter } from 'node:events';
|
|
5
|
+
|
|
6
|
+
vi.mock('googleapis', () => {
|
|
7
|
+
return {
|
|
8
|
+
google: {
|
|
9
|
+
auth: {
|
|
10
|
+
getClient: vi.fn(),
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe('downloadAttachment', () => {
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
vi.resetAllMocks();
|
|
19
|
+
resetAuthClient();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should successfully download an attachment within the size limit', async () => {
|
|
23
|
+
const mockStream = new EventEmitter();
|
|
24
|
+
// @ts-expect-error Mocking the stream destroy
|
|
25
|
+
mockStream.destroy = vi.fn();
|
|
26
|
+
|
|
27
|
+
const mockRequest = vi.fn().mockResolvedValue({
|
|
28
|
+
data: mockStream,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// @ts-expect-error Mocking the client
|
|
32
|
+
vi.mocked(google.auth.getClient).mockResolvedValue({
|
|
33
|
+
request: mockRequest,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const promise = downloadAttachment('spaces/123/messages/456/attachments/789');
|
|
37
|
+
|
|
38
|
+
await new Promise(setImmediate);
|
|
39
|
+
|
|
40
|
+
// Simulate stream data and end
|
|
41
|
+
mockStream.emit('data', Buffer.alloc(1024));
|
|
42
|
+
mockStream.emit('end');
|
|
43
|
+
|
|
44
|
+
const buffer = await promise;
|
|
45
|
+
|
|
46
|
+
expect(google.auth.getClient).toHaveBeenCalledWith({
|
|
47
|
+
scopes: ['https://www.googleapis.com/auth/chat.bot'],
|
|
48
|
+
});
|
|
49
|
+
expect(mockRequest).toHaveBeenCalledWith({
|
|
50
|
+
url: 'https://chat.googleapis.com/v1/media/spaces/123/messages/456/attachments/789?alt=media',
|
|
51
|
+
method: 'GET',
|
|
52
|
+
responseType: 'stream',
|
|
53
|
+
});
|
|
54
|
+
expect(buffer).toBeInstanceOf(Buffer);
|
|
55
|
+
expect(buffer.length).toBe(1024);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should throw an error if the attachment exceeds the maximum size', async () => {
|
|
59
|
+
const maxSizeBytes = 25 * 1024 * 1024;
|
|
60
|
+
const mockStream = new EventEmitter();
|
|
61
|
+
// @ts-expect-error Mocking the stream destroy
|
|
62
|
+
mockStream.destroy = vi.fn();
|
|
63
|
+
|
|
64
|
+
const mockRequest = vi.fn().mockResolvedValue({
|
|
65
|
+
data: mockStream,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// @ts-expect-error Mocking the client
|
|
69
|
+
vi.mocked(google.auth.getClient).mockResolvedValue({
|
|
70
|
+
request: mockRequest,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const promise = downloadAttachment('spaces/123/messages/456/attachments/789');
|
|
74
|
+
|
|
75
|
+
await new Promise(setImmediate);
|
|
76
|
+
|
|
77
|
+
// Simulate stream data exceeding the limit
|
|
78
|
+
mockStream.emit('data', Buffer.alloc(maxSizeBytes + 1));
|
|
79
|
+
|
|
80
|
+
await expect(promise).rejects.toThrow('Attachment exceeds maximum size');
|
|
81
|
+
// @ts-expect-error Mocking the stream destroy
|
|
82
|
+
expect(mockStream.destroy).toHaveBeenCalled();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should throw an error if the attachment exceeds a custom maximum size', async () => {
|
|
86
|
+
const maxSizeBytes = 10 * 1024 * 1024;
|
|
87
|
+
const mockStream = new EventEmitter();
|
|
88
|
+
// @ts-expect-error Mocking the stream destroy
|
|
89
|
+
mockStream.destroy = vi.fn();
|
|
90
|
+
|
|
91
|
+
const mockRequest = vi.fn().mockResolvedValue({
|
|
92
|
+
data: mockStream,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// @ts-expect-error Mocking the client
|
|
96
|
+
vi.mocked(google.auth.getClient).mockResolvedValue({
|
|
97
|
+
request: mockRequest,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const promise = downloadAttachment('spaces/123/messages/456/attachments/789', 10);
|
|
101
|
+
|
|
102
|
+
await new Promise(setImmediate);
|
|
103
|
+
|
|
104
|
+
// Simulate stream data exceeding the limit
|
|
105
|
+
mockStream.emit('data', Buffer.alloc(maxSizeBytes + 1));
|
|
106
|
+
|
|
107
|
+
await expect(promise).rejects.toThrow('Attachment exceeds maximum size');
|
|
108
|
+
// @ts-expect-error Mocking the stream destroy
|
|
109
|
+
expect(mockStream.destroy).toHaveBeenCalled();
|
|
110
|
+
});
|
|
111
|
+
});
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { google } from 'googleapis';
|
|
2
|
+
import type { Readable } from 'node:stream';
|
|
3
|
+
import type { ChatMessage } from '../shared/chats.js';
|
|
4
|
+
|
|
5
|
+
let authClient: Awaited<ReturnType<typeof google.auth.getClient>> | null = null;
|
|
6
|
+
|
|
7
|
+
export function resetAuthClient(): void {
|
|
8
|
+
authClient = null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function buildPolicyCard(logMessage: ChatMessage) {
|
|
12
|
+
const policyId = ('requestId' in logMessage && logMessage.requestId) || logMessage.id;
|
|
13
|
+
return [
|
|
14
|
+
{
|
|
15
|
+
cardId: logMessage.id,
|
|
16
|
+
card: {
|
|
17
|
+
header: {
|
|
18
|
+
title: 'Action Required: Policy Approval',
|
|
19
|
+
subtitle: 'A request needs your review.',
|
|
20
|
+
},
|
|
21
|
+
sections: [
|
|
22
|
+
{
|
|
23
|
+
widgets: [
|
|
24
|
+
{
|
|
25
|
+
textParagraph: {
|
|
26
|
+
text: logMessage.content || 'Please review this request.',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
buttonList: {
|
|
31
|
+
buttons: [
|
|
32
|
+
{
|
|
33
|
+
text: 'Approve',
|
|
34
|
+
color: {
|
|
35
|
+
red: 0,
|
|
36
|
+
green: 0.5,
|
|
37
|
+
blue: 0,
|
|
38
|
+
alpha: 1,
|
|
39
|
+
},
|
|
40
|
+
onClick: {
|
|
41
|
+
action: {
|
|
42
|
+
function: 'approve',
|
|
43
|
+
parameters: [{ key: 'policyId', value: policyId }],
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
text: 'Reject',
|
|
49
|
+
color: {
|
|
50
|
+
red: 0.8,
|
|
51
|
+
green: 0,
|
|
52
|
+
blue: 0,
|
|
53
|
+
alpha: 1,
|
|
54
|
+
},
|
|
55
|
+
onClick: {
|
|
56
|
+
action: {
|
|
57
|
+
function: 'reject',
|
|
58
|
+
parameters: [{ key: 'policyId', value: policyId }],
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function chunkString(str: string, size: number): string[] {
|
|
74
|
+
const chunks: string[] = [];
|
|
75
|
+
const chars = Array.from(str);
|
|
76
|
+
for (let i = 0; i < chars.length; i += size) {
|
|
77
|
+
chunks.push(chars.slice(i, i + size).join(''));
|
|
78
|
+
}
|
|
79
|
+
return chunks;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Downloads a file attachment securely using Application Default Credentials (ADC).
|
|
84
|
+
* @param resourceName The resourceName of the attachment data to download.
|
|
85
|
+
* @param maxAttachmentSizeMB The maximum allowed attachment size in MB (defaults to 25).
|
|
86
|
+
* @returns A Buffer containing the file data.
|
|
87
|
+
*/
|
|
88
|
+
export async function downloadAttachment(
|
|
89
|
+
resourceName: string,
|
|
90
|
+
maxAttachmentSizeMB: number = 25
|
|
91
|
+
): Promise<Buffer> {
|
|
92
|
+
// Use ADC to authenticate
|
|
93
|
+
if (!authClient) {
|
|
94
|
+
authClient = await google.auth.getClient({
|
|
95
|
+
scopes: ['https://www.googleapis.com/auth/chat.bot'],
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
const client = authClient;
|
|
99
|
+
|
|
100
|
+
const url = `https://chat.googleapis.com/v1/media/${resourceName}?alt=media`;
|
|
101
|
+
|
|
102
|
+
const response = await client.request<Readable>({
|
|
103
|
+
url,
|
|
104
|
+
method: 'GET',
|
|
105
|
+
responseType: 'stream',
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
return new Promise((resolve, reject) => {
|
|
109
|
+
const chunks: Buffer[] = [];
|
|
110
|
+
let totalBytes = 0;
|
|
111
|
+
const maxSizeBytes = maxAttachmentSizeMB * 1024 * 1024;
|
|
112
|
+
|
|
113
|
+
response.data.on('data', (chunk: Buffer) => {
|
|
114
|
+
totalBytes += chunk.length;
|
|
115
|
+
if (totalBytes > maxSizeBytes) {
|
|
116
|
+
response.data.destroy();
|
|
117
|
+
reject(
|
|
118
|
+
new Error(`Attachment exceeds maximum size of ${maxSizeBytes} bytes: ${totalBytes} bytes`)
|
|
119
|
+
);
|
|
120
|
+
} else {
|
|
121
|
+
chunks.push(chunk);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
response.data.on('end', () => {
|
|
126
|
+
resolve(Buffer.concat(chunks));
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
response.data.on('error', (err: Error) => {
|
|
130
|
+
reject(err);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
}
|
package/src/cli/commands/init.ts
CHANGED
|
@@ -37,13 +37,6 @@ export const initCmd = new Command('init')
|
|
|
37
37
|
},
|
|
38
38
|
env: {},
|
|
39
39
|
},
|
|
40
|
-
routers: [
|
|
41
|
-
'@clawmini/slash-new',
|
|
42
|
-
'@clawmini/slash-stop',
|
|
43
|
-
'@clawmini/slash-interrupt',
|
|
44
|
-
'@clawmini/slash-policies',
|
|
45
|
-
'@clawmini/slash-command',
|
|
46
|
-
],
|
|
47
40
|
api: true,
|
|
48
41
|
};
|
|
49
42
|
|
|
@@ -76,15 +76,30 @@ messagesCmd
|
|
|
76
76
|
.action(async (options) => {
|
|
77
77
|
try {
|
|
78
78
|
const chatId = options.chat ?? (await getDefaultChatId());
|
|
79
|
-
const messages = await getMessages(
|
|
79
|
+
const messages = await getMessages(
|
|
80
|
+
chatId,
|
|
81
|
+
options.lines,
|
|
82
|
+
undefined,
|
|
83
|
+
(msg) => !msg.subagentId
|
|
84
|
+
);
|
|
80
85
|
|
|
81
86
|
if (options.json) {
|
|
82
87
|
messages.forEach((msg) => console.log(JSON.stringify(msg)));
|
|
83
88
|
} else {
|
|
84
89
|
messages.forEach((msg) => {
|
|
85
|
-
if (msg.role === 'user') {
|
|
90
|
+
if (msg.role === 'user' || msg.displayRole === 'user') {
|
|
86
91
|
console.log(`[USER] ${msg.content}`);
|
|
87
|
-
} else if (msg.role === '
|
|
92
|
+
} else if (msg.role === 'agent' || msg.displayRole === 'agent') {
|
|
93
|
+
console.log(`[AGENT] ${msg.content.trim()}`);
|
|
94
|
+
} else if (msg.role === 'policy') {
|
|
95
|
+
console.log(`[POLICY] ${msg.commandName} ${msg.args.join(' ')}`);
|
|
96
|
+
} else if (msg.role === 'tool') {
|
|
97
|
+
console.log(`[TOOL] ${msg.name}`);
|
|
98
|
+
} else if (msg.role === 'system') {
|
|
99
|
+
if (msg.content) {
|
|
100
|
+
console.log(`[LOG] ${msg.content.trim()}`);
|
|
101
|
+
}
|
|
102
|
+
} else if (msg.role === 'command' || msg.role === 'legacy_log') {
|
|
88
103
|
if (msg.content) {
|
|
89
104
|
console.log(`[LOG] ${msg.content.trim()}`);
|
|
90
105
|
} else if (msg.stderr) {
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { handleError } from '../utils.js';
|
|
5
|
+
import { resolveCompiledScript } from '../../shared/lite.js';
|
|
6
|
+
import type { PolicyConfig } from '../../shared/policies.js';
|
|
7
|
+
import { getClawminiDir } from '../../shared/workspace.js';
|
|
8
|
+
|
|
9
|
+
const SUPPORTED_POLICIES = ['propose-policy'];
|
|
10
|
+
|
|
11
|
+
export const policiesCmd = new Command('policies').description('Manage sandbox policies');
|
|
12
|
+
|
|
13
|
+
policiesCmd
|
|
14
|
+
.command('add <name>')
|
|
15
|
+
.description('Add a new policy')
|
|
16
|
+
.action(async (name: string) => {
|
|
17
|
+
if (!SUPPORTED_POLICIES.includes(name)) {
|
|
18
|
+
handleError(
|
|
19
|
+
'add policy',
|
|
20
|
+
new Error(
|
|
21
|
+
`Unsupported policy: "${name}". Supported policies: ${SUPPORTED_POLICIES.join(', ')}`
|
|
22
|
+
)
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const dirPath = getClawminiDir();
|
|
27
|
+
const policyScriptsDir = path.join(dirPath, 'policy-scripts');
|
|
28
|
+
const policiesPath = path.join(dirPath, 'policies.json');
|
|
29
|
+
|
|
30
|
+
if (!fs.existsSync(dirPath)) {
|
|
31
|
+
handleError(
|
|
32
|
+
'add policy',
|
|
33
|
+
new Error('.clawmini directory not found. Please run "clawmini init" first.')
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!fs.existsSync(policyScriptsDir)) {
|
|
38
|
+
fs.mkdirSync(policyScriptsDir, { recursive: true });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Update or create policies.json
|
|
42
|
+
let policies: PolicyConfig = { policies: {} };
|
|
43
|
+
if (fs.existsSync(policiesPath)) {
|
|
44
|
+
policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
policies.policies[name] = {
|
|
48
|
+
description: 'Propose a new policy to create',
|
|
49
|
+
command: `./.clawmini/policy-scripts/${name}.js`,
|
|
50
|
+
allowHelp: true,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
fs.writeFileSync(policiesPath, JSON.stringify(policies, null, 2));
|
|
54
|
+
console.log(`Registered ${name} in .clawmini/policies.json`);
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const foundPath = await resolveCompiledScript(name, import.meta.url);
|
|
58
|
+
let scriptContent = fs.readFileSync(foundPath, 'utf8');
|
|
59
|
+
|
|
60
|
+
if (!scriptContent.startsWith('#!')) {
|
|
61
|
+
scriptContent = '#!/usr/bin/env node\n' + scriptContent;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const destPath = path.join(policyScriptsDir, `${name}.js`);
|
|
65
|
+
fs.writeFileSync(destPath, scriptContent, { mode: 0o755 });
|
|
66
|
+
console.log(`Copied ${name} script to ${destPath}`);
|
|
67
|
+
} catch (err) {
|
|
68
|
+
handleError('add policy', err);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { promises as fsPromises, Dirent } from 'fs';
|
|
3
|
+
import {
|
|
4
|
+
resolveSkillsTemplatePath,
|
|
5
|
+
copyAgentSkills,
|
|
6
|
+
copyAgentSkill,
|
|
7
|
+
} from '../../shared/workspace.js';
|
|
8
|
+
import { handleError } from '../utils.js';
|
|
9
|
+
|
|
10
|
+
export const skillsCmd = new Command('skills').description('Manage template skills');
|
|
11
|
+
|
|
12
|
+
skillsCmd
|
|
13
|
+
.command('list')
|
|
14
|
+
.description('List available template skills')
|
|
15
|
+
.action(async () => {
|
|
16
|
+
try {
|
|
17
|
+
let skillsDir: string;
|
|
18
|
+
try {
|
|
19
|
+
skillsDir = await resolveSkillsTemplatePath();
|
|
20
|
+
} catch (err: unknown) {
|
|
21
|
+
if (err instanceof Error && err.message.includes('Template not found: skills')) {
|
|
22
|
+
console.error('No skills found. The templates/skills directory does not exist.');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
throw err;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let entries: Dirent[];
|
|
29
|
+
try {
|
|
30
|
+
entries = await fsPromises.readdir(skillsDir, { withFileTypes: true });
|
|
31
|
+
} catch (err: unknown) {
|
|
32
|
+
if (err && typeof err === 'object' && 'code' in err && err.code === 'ENOENT') {
|
|
33
|
+
console.error('No skills found.');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const skills = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
40
|
+
|
|
41
|
+
if (skills.length === 0) {
|
|
42
|
+
console.error('No skills found.');
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
for (const skill of skills) {
|
|
46
|
+
console.log(`- ${skill}`);
|
|
47
|
+
}
|
|
48
|
+
} catch (err) {
|
|
49
|
+
handleError('list skills', err);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
skillsCmd
|
|
54
|
+
.command('add [skill-name]')
|
|
55
|
+
.description('Add a skill to an agent, overwriting the target skill directory if it exists')
|
|
56
|
+
.option('-a, --agent <agentId>', 'Agent ID (defaults to "default")')
|
|
57
|
+
.action(async (skillName: string | undefined, options: { agent?: string }) => {
|
|
58
|
+
try {
|
|
59
|
+
const agentId = options.agent || 'default';
|
|
60
|
+
|
|
61
|
+
if (skillName) {
|
|
62
|
+
await copyAgentSkill(agentId, skillName, process.cwd(), true);
|
|
63
|
+
console.log(`Successfully added skill '${skillName}' to agent '${agentId}'.`);
|
|
64
|
+
} else {
|
|
65
|
+
await copyAgentSkills(agentId, process.cwd(), true);
|
|
66
|
+
console.log(`Successfully added all skills to agent '${agentId}'.`);
|
|
67
|
+
}
|
|
68
|
+
} catch (err) {
|
|
69
|
+
handleError('add skill', err);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
@@ -52,8 +52,12 @@ export async function handleApiChats(
|
|
|
52
52
|
try {
|
|
53
53
|
const url = new URL(req.url || '', `http://${req.headers.host}`);
|
|
54
54
|
const since = url.searchParams.get('since');
|
|
55
|
+
const before = url.searchParams.get('before');
|
|
56
|
+
const limitParam = url.searchParams.get('limit');
|
|
57
|
+
const parsedLimit = limitParam ? parseInt(limitParam, 10) : undefined;
|
|
58
|
+
const limit = Number.isNaN(parsedLimit) ? undefined : parsedLimit;
|
|
55
59
|
|
|
56
|
-
let messages = await getMessages(chatId);
|
|
60
|
+
let messages = await getMessages(chatId, limit, undefined, undefined, before || undefined);
|
|
57
61
|
|
|
58
62
|
if (since) {
|
|
59
63
|
const sinceIndex = messages.findIndex((m) => m.id === since);
|
|
@@ -39,5 +39,5 @@ describe('E2E Basic Tests', () => {
|
|
|
39
39
|
const { stdout: stdoutDelete } = await runCli(['chats', 'delete', 'test-chat']);
|
|
40
40
|
expect(stdoutDelete).toContain('Chat test-chat deleted successfully.');
|
|
41
41
|
expect(fs.existsSync(path.join(chatsDir, 'test-chat'))).toBe(false);
|
|
42
|
-
});
|
|
42
|
+
}, 15000);
|
|
43
43
|
});
|
package/src/cli/e2e/cron.test.ts
CHANGED
|
@@ -65,7 +65,7 @@ describe('E2E Cron Tests', () => {
|
|
|
65
65
|
const { stdout: stdoutList3 } = await runCli(['jobs', 'list']);
|
|
66
66
|
expect(stdoutList3).not.toContain('test-job-1');
|
|
67
67
|
expect(stdoutList3).toContain('- test-job-2 (cron: * * * * *)');
|
|
68
|
-
});
|
|
68
|
+
}, 15000);
|
|
69
69
|
|
|
70
70
|
it('should execute a job and inherit chat default agent and session', async () => {
|
|
71
71
|
// 1. Create a specific agent for this chat
|