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
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { c as getClawminiDir, d as getSocketPath, f as getWorkspaceRoot } from "../workspace-BJmJBfKi.mjs";
|
|
3
3
|
import { t as createUnixSocketFetch } from "../fetch-Cn1XNyiO.mjs";
|
|
4
|
+
import { a as createUnixSocketEventSource, i as shouldDisplayMessage, n as handleAdapterCommand, r as formatMessage, t as handleRoutingCommand } from "../routing-D8rTxtaV.mjs";
|
|
4
5
|
import fs from "node:fs";
|
|
5
6
|
import path from "node:path";
|
|
6
7
|
import fs$1 from "node:fs/promises";
|
|
7
|
-
import { fileURLToPath } from "node:url";
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
import { createTRPCClient, httpLink, httpSubscriptionLink, splitLink } from "@trpc/client";
|
|
10
|
-
import
|
|
11
|
-
import { Client, Events, GatewayIntentBits, Partials } from "discord.js";
|
|
10
|
+
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, Client, Colors, EmbedBuilder, Events, GatewayIntentBits, ModalBuilder, Partials, TextInputBuilder, TextInputStyle } from "discord.js";
|
|
12
11
|
|
|
13
12
|
//#region src/adapter-discord/config.ts
|
|
14
13
|
const DiscordConfigSchema = z.looseObject({
|
|
15
14
|
botToken: z.string().min(1, "Discord Bot Token is required."),
|
|
16
15
|
authorizedUserId: z.string().min(1, "Authorized Discord User ID is required."),
|
|
17
16
|
chatId: z.string().default("default"),
|
|
18
|
-
maxAttachmentSizeMB: z.number().default(25)
|
|
17
|
+
maxAttachmentSizeMB: z.number().default(25),
|
|
18
|
+
requireMention: z.boolean().default(false)
|
|
19
19
|
});
|
|
20
20
|
function getDiscordConfigPath(startDir = process.cwd()) {
|
|
21
21
|
return path.join(getClawminiDir(startDir), "adapters", "discord", "config.json");
|
|
@@ -25,14 +25,10 @@ async function readDiscordConfig(startDir = process.cwd()) {
|
|
|
25
25
|
try {
|
|
26
26
|
const data = await fs$1.readFile(configPath, "utf-8");
|
|
27
27
|
const parsed = JSON.parse(data);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
return result.data;
|
|
34
|
-
} catch {
|
|
35
|
-
return null;
|
|
28
|
+
return DiscordConfigSchema.parse(parsed);
|
|
29
|
+
} catch (err) {
|
|
30
|
+
if (err.code === "ENOENT") return null;
|
|
31
|
+
throw err;
|
|
36
32
|
}
|
|
37
33
|
}
|
|
38
34
|
async function initDiscordConfig(startDir = process.cwd()) {
|
|
@@ -51,98 +47,174 @@ async function initDiscordConfig(startDir = process.cwd()) {
|
|
|
51
47
|
console.log(`Created template configuration file at ${configPath}`);
|
|
52
48
|
console.log("Please update it with your actual Discord Bot Token and User ID.");
|
|
53
49
|
}
|
|
54
|
-
function isAuthorized(userId, authorizedUserId) {
|
|
50
|
+
function isAuthorized$1(userId, authorizedUserId) {
|
|
55
51
|
return userId === authorizedUserId;
|
|
56
52
|
}
|
|
57
53
|
|
|
58
54
|
//#endregion
|
|
59
|
-
//#region src/
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
55
|
+
//#region src/adapter-discord/state.ts
|
|
56
|
+
const DiscordStateSchema = z.object({
|
|
57
|
+
lastSyncedMessageIds: z.record(z.string(), z.string()).optional(),
|
|
58
|
+
channelChatMap: z.record(z.string(), z.object({
|
|
59
|
+
chatId: z.string().nullable().optional(),
|
|
60
|
+
requireMention: z.boolean().optional()
|
|
61
|
+
})).optional(),
|
|
62
|
+
filters: z.record(z.string(), z.boolean()).optional()
|
|
63
|
+
});
|
|
64
|
+
function getDiscordStatePath(startDir = process.cwd()) {
|
|
65
|
+
return path.join(getClawminiDir(startDir), "adapters", "discord", "state.json");
|
|
66
|
+
}
|
|
67
|
+
async function readDiscordState(startDir = process.cwd()) {
|
|
68
|
+
const statePath = getDiscordStatePath(startDir);
|
|
69
|
+
try {
|
|
70
|
+
const data = await fs$1.readFile(statePath, "utf-8");
|
|
71
|
+
const parsed = JSON.parse(data);
|
|
72
|
+
if (parsed.lastSyncedMessageId && !parsed.lastSyncedMessageIds) parsed.lastSyncedMessageIds = { default: parsed.lastSyncedMessageId };
|
|
73
|
+
if (parsed.channelChatMap) {
|
|
74
|
+
for (const [key, value] of Object.entries(parsed.channelChatMap)) if (typeof value === "string") parsed.channelChatMap[key] = { chatId: value };
|
|
75
|
+
}
|
|
76
|
+
return DiscordStateSchema.parse(parsed);
|
|
77
|
+
} catch (err) {
|
|
78
|
+
if (err.code === "ENOENT") return {};
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function writeDiscordState(state, startDir = process.cwd()) {
|
|
83
|
+
const statePath = getDiscordStatePath(startDir);
|
|
84
|
+
const dir = path.dirname(statePath);
|
|
85
|
+
try {
|
|
86
|
+
await fs$1.mkdir(dir, { recursive: true });
|
|
87
|
+
await fs$1.writeFile(statePath, JSON.stringify(state, null, 2), "utf-8");
|
|
88
|
+
} catch (err) {
|
|
89
|
+
console.error(`Failed to write Discord state to ${statePath}:`, err);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
let stateUpdatePromise = Promise.resolve();
|
|
93
|
+
function updateDiscordState(updates, startDir = process.cwd()) {
|
|
94
|
+
return new Promise((resolve, reject) => {
|
|
95
|
+
stateUpdatePromise = stateUpdatePromise.then(async () => {
|
|
96
|
+
try {
|
|
97
|
+
const currentState = await readDiscordState(startDir);
|
|
98
|
+
const resolvedUpdates = typeof updates === "function" ? updates(currentState) : updates;
|
|
99
|
+
const newState = {
|
|
100
|
+
...currentState,
|
|
101
|
+
...resolvedUpdates
|
|
102
|
+
};
|
|
103
|
+
await writeDiscordState(newState, startDir);
|
|
104
|
+
resolve(newState);
|
|
105
|
+
} catch (err) {
|
|
106
|
+
console.error(`Failed to write Discord state:`, err);
|
|
107
|
+
reject(err);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
//#endregion
|
|
114
|
+
//#region src/adapter-discord/interactions.ts
|
|
115
|
+
function isAuthorized(userId, authorizedUserId) {
|
|
116
|
+
return userId === authorizedUserId;
|
|
117
|
+
}
|
|
118
|
+
async function handleDiscordInteraction(interaction, config, trpc) {
|
|
119
|
+
if (!interaction.isButton() && !interaction.isModalSubmit()) return;
|
|
120
|
+
if (!isAuthorized(interaction.user.id, config.authorizedUserId)) {
|
|
121
|
+
if (interaction.isRepliable()) await interaction.reply({
|
|
122
|
+
content: "You are not authorized to perform this action.",
|
|
123
|
+
ephemeral: true
|
|
124
|
+
});
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (interaction.isButton()) {
|
|
128
|
+
if (interaction.customId.startsWith("approve_") || interaction.customId.startsWith("approve|")) {
|
|
129
|
+
let policyId, explicitChatId;
|
|
130
|
+
if (interaction.customId.startsWith("approve|")) {
|
|
131
|
+
const parts = interaction.customId.split("|");
|
|
132
|
+
policyId = parts[1];
|
|
133
|
+
explicitChatId = parts[2] || void 0;
|
|
134
|
+
} else policyId = interaction.customId.replace("approve_", "");
|
|
135
|
+
await interaction.update({ components: [] });
|
|
136
|
+
await interaction.followUp({
|
|
137
|
+
content: `Approving policy ${policyId}...`,
|
|
138
|
+
ephemeral: true
|
|
104
139
|
});
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
140
|
+
try {
|
|
141
|
+
const currentState = await readDiscordState();
|
|
142
|
+
const targetChatId = explicitChatId || (interaction.channelId ? currentState.channelChatMap?.[interaction.channelId]?.chatId || config.chatId : config.chatId);
|
|
143
|
+
await trpc.sendMessage.mutate({
|
|
144
|
+
type: "send-message",
|
|
145
|
+
client: "cli",
|
|
146
|
+
data: {
|
|
147
|
+
message: `/approve ${policyId}`,
|
|
148
|
+
chatId: targetChatId,
|
|
149
|
+
adapter: "discord",
|
|
150
|
+
noWait: true
|
|
151
|
+
}
|
|
110
152
|
});
|
|
111
|
-
})
|
|
112
|
-
|
|
153
|
+
} catch (error) {
|
|
154
|
+
console.error("Failed to send approve command to daemon:", error);
|
|
155
|
+
await interaction.followUp({
|
|
156
|
+
content: `Failed to approve policy ${policyId}.`,
|
|
157
|
+
ephemeral: true
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
} else if (interaction.customId.startsWith("reject_") || interaction.customId.startsWith("reject|")) {
|
|
161
|
+
let policyId, explicitChatId;
|
|
162
|
+
if (interaction.customId.startsWith("reject|")) {
|
|
163
|
+
const parts = interaction.customId.split("|");
|
|
164
|
+
policyId = parts[1];
|
|
165
|
+
explicitChatId = parts[2] || "";
|
|
166
|
+
} else {
|
|
167
|
+
policyId = interaction.customId.replace("reject_", "");
|
|
168
|
+
explicitChatId = "";
|
|
169
|
+
}
|
|
170
|
+
const modal = new ModalBuilder().setCustomId(`modal_reject|${policyId}|${explicitChatId}`).setTitle("Reject Policy");
|
|
171
|
+
const rationaleInput = new TextInputBuilder().setCustomId("rationale").setLabel("Rationale (optional)").setStyle(TextInputStyle.Paragraph).setRequired(false);
|
|
172
|
+
const actionRow = new ActionRowBuilder().addComponents(rationaleInput);
|
|
173
|
+
modal.addComponents(actionRow);
|
|
174
|
+
await interaction.showModal(modal);
|
|
113
175
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
else
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
176
|
+
} else if (interaction.isModalSubmit()) {
|
|
177
|
+
if (interaction.customId.startsWith("modal_reject_") || interaction.customId.startsWith("modal_reject|")) {
|
|
178
|
+
let policyId, explicitChatId;
|
|
179
|
+
if (interaction.customId.startsWith("modal_reject|")) {
|
|
180
|
+
const parts = interaction.customId.split("|");
|
|
181
|
+
policyId = parts[1];
|
|
182
|
+
explicitChatId = parts[2] || void 0;
|
|
183
|
+
} else policyId = interaction.customId.replace("modal_reject_", "");
|
|
184
|
+
const rationale = interaction.fields.getTextInputValue("rationale");
|
|
185
|
+
const command = rationale ? `/reject ${policyId} ${rationale}` : `/reject ${policyId}`;
|
|
186
|
+
if (interaction.isFromMessage()) {
|
|
187
|
+
await interaction.update({ components: [] });
|
|
188
|
+
await interaction.followUp({
|
|
189
|
+
content: `Rejecting policy ${policyId}...`,
|
|
190
|
+
ephemeral: true
|
|
191
|
+
});
|
|
192
|
+
} else await interaction.reply({
|
|
193
|
+
content: `Rejecting policy ${policyId}...`,
|
|
194
|
+
ephemeral: true
|
|
127
195
|
});
|
|
196
|
+
try {
|
|
197
|
+
const currentState = await readDiscordState();
|
|
198
|
+
const targetChatId = explicitChatId || (interaction.channelId ? currentState.channelChatMap?.[interaction.channelId]?.chatId || config.chatId : config.chatId);
|
|
199
|
+
await trpc.sendMessage.mutate({
|
|
200
|
+
type: "send-message",
|
|
201
|
+
client: "cli",
|
|
202
|
+
data: {
|
|
203
|
+
message: command,
|
|
204
|
+
chatId: targetChatId,
|
|
205
|
+
adapter: "discord",
|
|
206
|
+
noWait: true
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.error("Failed to send reject command to daemon:", error);
|
|
211
|
+
await interaction.followUp({
|
|
212
|
+
content: `Failed to reject policy ${policyId}.`,
|
|
213
|
+
ephemeral: true
|
|
214
|
+
});
|
|
215
|
+
}
|
|
128
216
|
}
|
|
129
|
-
|
|
130
|
-
if (!this.listeners[type]) this.listeners[type] = [];
|
|
131
|
-
this.listeners[type].push(listener);
|
|
132
|
-
}
|
|
133
|
-
removeEventListener(type, listener) {
|
|
134
|
-
if (!this.listeners[type]) return;
|
|
135
|
-
this.listeners[type] = this.listeners[type].filter((l) => l !== listener);
|
|
136
|
-
}
|
|
137
|
-
dispatchEvent(event) {
|
|
138
|
-
const type = event.type;
|
|
139
|
-
if (this.listeners[type]) for (const listener of this.listeners[type]) listener(event);
|
|
140
|
-
}
|
|
141
|
-
close() {
|
|
142
|
-
this.readyState = this.CLOSED;
|
|
143
|
-
if (this.req) this.req.destroy();
|
|
144
|
-
}
|
|
145
|
-
};
|
|
217
|
+
}
|
|
146
218
|
}
|
|
147
219
|
|
|
148
220
|
//#endregion
|
|
@@ -173,65 +245,66 @@ function getTRPCClient(options = {}) {
|
|
|
173
245
|
}
|
|
174
246
|
|
|
175
247
|
//#endregion
|
|
176
|
-
//#region src/adapter-discord/
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
try {
|
|
184
|
-
const data = await fs$1.readFile(statePath, "utf-8");
|
|
185
|
-
const parsed = JSON.parse(data);
|
|
186
|
-
const result = DiscordStateSchema.safeParse(parsed);
|
|
187
|
-
if (!result.success) return { lastSyncedMessageId: void 0 };
|
|
188
|
-
return result.data;
|
|
189
|
-
} catch {
|
|
190
|
-
return { lastSyncedMessageId: void 0 };
|
|
248
|
+
//#region src/adapter-discord/forwarder.ts
|
|
249
|
+
async function resolveDiscordDestination(client, discordUserId, chatId) {
|
|
250
|
+
const channelChatMap = (await readDiscordState()).channelChatMap || {};
|
|
251
|
+
let targetDiscordChannelId;
|
|
252
|
+
for (const [channelId, mappedChatId] of Object.entries(channelChatMap)) if (mappedChatId?.chatId === chatId) {
|
|
253
|
+
targetDiscordChannelId = channelId;
|
|
254
|
+
break;
|
|
191
255
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
await fs$1.mkdir(dir, { recursive: true });
|
|
198
|
-
await fs$1.writeFile(statePath, JSON.stringify(state, null, 2), "utf-8");
|
|
199
|
-
} catch (err) {
|
|
200
|
-
console.error(`Failed to write Discord state to ${statePath}:`, err);
|
|
256
|
+
if (targetDiscordChannelId) try {
|
|
257
|
+
const channel = await client.channels.fetch(targetDiscordChannelId);
|
|
258
|
+
if (channel && channel.isTextBased() && !channel.isDMBased()) return channel;
|
|
259
|
+
} catch (error) {
|
|
260
|
+
console.warn(`Failed to fetch mapped channel ${targetDiscordChannelId} for chat ${chatId}, falling back to DM.`, error);
|
|
201
261
|
}
|
|
262
|
+
return (await client.users.fetch(discordUserId)).createDM();
|
|
202
263
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
264
|
+
async function startDaemonToDiscordForwarder(client, trpc, discordUserId, options = {}) {
|
|
265
|
+
const defaultChatId = options.chatId ?? "default";
|
|
266
|
+
const signal = options.signal;
|
|
267
|
+
const config = options.config ?? {};
|
|
268
|
+
const activeSubscriptions = /* @__PURE__ */ new Map();
|
|
269
|
+
const activeTypingSubscriptions = /* @__PURE__ */ new Map();
|
|
270
|
+
let currentLastSyncedMessageIds = (await readDiscordState()).lastSyncedMessageIds || {};
|
|
271
|
+
const saveLastMessageId = async (chatId, id) => {
|
|
272
|
+
currentLastSyncedMessageIds = {
|
|
273
|
+
...currentLastSyncedMessageIds,
|
|
274
|
+
[chatId]: id
|
|
275
|
+
};
|
|
276
|
+
return updateDiscordState((state) => ({ lastSyncedMessageIds: {
|
|
277
|
+
...state.lastSyncedMessageIds,
|
|
278
|
+
...currentLastSyncedMessageIds
|
|
279
|
+
} }));
|
|
280
|
+
};
|
|
281
|
+
const startSubscriptionForChat = async (chatId) => {
|
|
282
|
+
if (activeSubscriptions.has(chatId)) return;
|
|
283
|
+
if (signal?.aborted) return;
|
|
284
|
+
let lastMessageId = currentLastSyncedMessageIds[chatId];
|
|
285
|
+
if (!lastMessageId) try {
|
|
286
|
+
const messages = await trpc.getMessages.query({
|
|
287
|
+
chatId,
|
|
288
|
+
limit: 1
|
|
289
|
+
});
|
|
290
|
+
if (Array.isArray(messages) && messages.length > 0) {
|
|
291
|
+
const lastMsg = messages[messages.length - 1];
|
|
292
|
+
if (lastMsg) {
|
|
293
|
+
await saveLastMessageId(chatId, lastMsg.id);
|
|
294
|
+
lastMessageId = lastMsg.id;
|
|
295
|
+
}
|
|
218
296
|
}
|
|
297
|
+
} catch (error) {
|
|
298
|
+
if (signal?.aborted) return;
|
|
299
|
+
console.error(`Failed to fetch initial messages from daemon for ${chatId}:`, error);
|
|
219
300
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
console.log(`Starting daemon-to-discord forwarder for chat ${chatId}, lastMessageId: ${lastMessageId}`);
|
|
225
|
-
let retryDelay = 1e3;
|
|
226
|
-
const maxRetryDelay = 3e4;
|
|
227
|
-
return new Promise((resolve) => {
|
|
301
|
+
console.log(`Starting daemon-to-discord forwarder for chat ${chatId}, lastMessageId: ${lastMessageId}`);
|
|
302
|
+
let retryDelay = 1e3;
|
|
303
|
+
const maxRetryDelay = 3e4;
|
|
228
304
|
let subscription = null;
|
|
229
305
|
let messageQueue = Promise.resolve();
|
|
230
306
|
const connect = () => {
|
|
231
|
-
if (signal?.aborted)
|
|
232
|
-
resolve();
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
307
|
+
if (signal?.aborted || !activeSubscriptions.has(chatId)) return;
|
|
235
308
|
subscription = trpc.waitForMessages.subscribe({
|
|
236
309
|
chatId,
|
|
237
310
|
lastMessageId
|
|
@@ -241,61 +314,83 @@ async function startDaemonToDiscordForwarder(client, trpc, discordUserId, chatId
|
|
|
241
314
|
if (!Array.isArray(messages) || messages.length === 0) return;
|
|
242
315
|
messageQueue = messageQueue.then(async () => {
|
|
243
316
|
for (const rawMessage of messages) {
|
|
244
|
-
if (signal?.aborted) break;
|
|
317
|
+
if (signal?.aborted || !activeSubscriptions.has(chatId)) break;
|
|
245
318
|
const message = rawMessage;
|
|
246
|
-
if (message
|
|
319
|
+
if (shouldDisplayMessage(message, config)) {
|
|
247
320
|
const logMessage = message;
|
|
248
|
-
if (logMessage.
|
|
321
|
+
if (logMessage.role === "policy" && logMessage.status === "pending") {
|
|
322
|
+
try {
|
|
323
|
+
const dm = await resolveDiscordDestination(client, discordUserId, chatId);
|
|
324
|
+
const embed = new EmbedBuilder().setTitle("Action Required: Policy Request").setDescription(logMessage.content || "A pending policy request requires your attention.").setColor(Colors.Yellow);
|
|
325
|
+
const policyId = "requestId" in logMessage && logMessage.requestId || logMessage.id;
|
|
326
|
+
const row = new ActionRowBuilder().addComponents(new ButtonBuilder().setCustomId(`approve|${policyId}|${chatId}`).setLabel("Approve").setStyle(ButtonStyle.Success), new ButtonBuilder().setCustomId(`reject|${policyId}|${chatId}`).setLabel("Reject").setStyle(ButtonStyle.Danger));
|
|
327
|
+
const optionsMsg = {
|
|
328
|
+
embeds: [embed],
|
|
329
|
+
components: [row]
|
|
330
|
+
};
|
|
331
|
+
try {
|
|
332
|
+
await dm.send(optionsMsg);
|
|
333
|
+
} catch (richError) {
|
|
334
|
+
console.warn(`Failed to send rich message to Discord user ${discordUserId}, falling back to plain text:`, richError);
|
|
335
|
+
await dm.send({ content: `Action Required: Policy Request\n\n${logMessage.content || "A pending policy request requires your attention."}\n\nApprove: \`/approve ${policyId}\`\nReject: \`/reject ${policyId} <optional_rationale>\`` });
|
|
336
|
+
}
|
|
337
|
+
} catch (error) {
|
|
338
|
+
console.error(`Failed to send message to Discord user ${discordUserId}:`, error);
|
|
339
|
+
}
|
|
340
|
+
await saveLastMessageId(chatId, logMessage.id).catch(console.error);
|
|
341
|
+
lastMessageId = logMessage.id;
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
if ("level" in logMessage && logMessage.level === "verbose") {
|
|
345
|
+
await saveLastMessageId(chatId, logMessage.id).catch(console.error);
|
|
249
346
|
lastMessageId = logMessage.id;
|
|
250
|
-
await writeDiscordState({ lastSyncedMessageId: lastMessageId }).catch(console.error);
|
|
251
347
|
continue;
|
|
252
348
|
}
|
|
253
349
|
const hasContent = !!logMessage.content?.trim();
|
|
254
|
-
const
|
|
350
|
+
const files = "files" in logMessage ? logMessage.files : void 0;
|
|
351
|
+
const hasFiles = Array.isArray(files) && files.length > 0;
|
|
255
352
|
let absoluteFiles = [];
|
|
256
|
-
if (hasFiles) {
|
|
353
|
+
if (hasFiles && files) {
|
|
257
354
|
const workspaceRoot = getWorkspaceRoot(process.cwd());
|
|
258
|
-
absoluteFiles =
|
|
355
|
+
absoluteFiles = files.map((f) => path.resolve(workspaceRoot, f));
|
|
259
356
|
}
|
|
260
357
|
if (!hasContent && !hasFiles) {
|
|
358
|
+
await saveLastMessageId(chatId, logMessage.id).catch(console.error);
|
|
261
359
|
lastMessageId = logMessage.id;
|
|
262
|
-
await writeDiscordState({ lastSyncedMessageId: lastMessageId }).catch(console.error);
|
|
263
360
|
continue;
|
|
264
361
|
}
|
|
265
362
|
try {
|
|
266
|
-
const dm = await (
|
|
267
|
-
|
|
268
|
-
|
|
363
|
+
const dm = await resolveDiscordDestination(client, discordUserId, chatId);
|
|
364
|
+
const formattedContent = formatMessage(message);
|
|
365
|
+
if (formattedContent && formattedContent.length > 2e3) {
|
|
366
|
+
const chunks = chunkString(formattedContent, 2e3);
|
|
269
367
|
for (let i = 0; i < chunks.length; i++) {
|
|
270
|
-
if (signal?.aborted) break;
|
|
368
|
+
if (signal?.aborted || !activeSubscriptions.has(chatId)) break;
|
|
271
369
|
const chunkOptions = { content: chunks[i] };
|
|
272
370
|
if (i === chunks.length - 1 && hasFiles) chunkOptions.files = absoluteFiles;
|
|
273
371
|
await dm.send(chunkOptions);
|
|
274
372
|
}
|
|
275
373
|
} else {
|
|
276
|
-
const
|
|
277
|
-
if (
|
|
278
|
-
if (hasFiles)
|
|
279
|
-
await dm.send(
|
|
374
|
+
const optionsMsg = {};
|
|
375
|
+
if (formattedContent) optionsMsg.content = formattedContent;
|
|
376
|
+
if (hasFiles) optionsMsg.files = absoluteFiles;
|
|
377
|
+
await dm.send(optionsMsg);
|
|
280
378
|
}
|
|
281
379
|
} catch (error) {
|
|
282
380
|
console.error(`Failed to send message to Discord user ${discordUserId}:`, error);
|
|
283
381
|
break;
|
|
284
382
|
}
|
|
285
383
|
}
|
|
384
|
+
await saveLastMessageId(chatId, message.id).catch(console.error);
|
|
286
385
|
lastMessageId = message.id;
|
|
287
|
-
await writeDiscordState({ lastSyncedMessageId: lastMessageId }).catch(console.error);
|
|
288
386
|
}
|
|
289
387
|
});
|
|
290
388
|
},
|
|
291
389
|
onError: (error) => {
|
|
292
|
-
console.error(`Error in daemon-to-discord forwarder subscription. Retrying in ${retryDelay}ms.`, error);
|
|
390
|
+
console.error(`Error in daemon-to-discord forwarder subscription for ${chatId}. Retrying in ${retryDelay}ms.`, error);
|
|
293
391
|
subscription?.unsubscribe();
|
|
294
392
|
subscription = null;
|
|
295
|
-
if (signal?.aborted)
|
|
296
|
-
resolve();
|
|
297
|
-
return;
|
|
298
|
-
}
|
|
393
|
+
if (signal?.aborted || !activeSubscriptions.has(chatId)) return;
|
|
299
394
|
setTimeout(() => {
|
|
300
395
|
retryDelay = Math.min(retryDelay * 2, maxRetryDelay);
|
|
301
396
|
connect();
|
|
@@ -303,30 +398,30 @@ async function startDaemonToDiscordForwarder(client, trpc, discordUserId, chatId
|
|
|
303
398
|
},
|
|
304
399
|
onComplete: () => {
|
|
305
400
|
subscription = null;
|
|
306
|
-
if (!signal?.aborted) setTimeout(() => connect(), retryDelay);
|
|
307
|
-
else resolve();
|
|
401
|
+
if (!signal?.aborted && activeSubscriptions.has(chatId)) setTimeout(() => connect(), retryDelay);
|
|
308
402
|
}
|
|
309
403
|
});
|
|
310
404
|
};
|
|
311
405
|
let typingSubscription = null;
|
|
312
406
|
let typingRetryDelay = 1e3;
|
|
313
407
|
const connectTyping = () => {
|
|
314
|
-
if (signal?.aborted) return;
|
|
408
|
+
if (signal?.aborted || !activeSubscriptions.has(chatId)) return;
|
|
315
409
|
typingSubscription = trpc.waitForTyping.subscribe({ chatId }, {
|
|
316
410
|
onData: async (event) => {
|
|
317
411
|
typingRetryDelay = 1e3;
|
|
318
412
|
if (!event) return;
|
|
319
413
|
try {
|
|
320
|
-
|
|
414
|
+
const dm = await resolveDiscordDestination(client, discordUserId, chatId);
|
|
415
|
+
if (dm.sendTyping) await dm.sendTyping();
|
|
321
416
|
} catch (error) {
|
|
322
417
|
console.error(`Failed to send typing indicator to Discord user ${discordUserId}:`, error);
|
|
323
418
|
}
|
|
324
419
|
},
|
|
325
420
|
onError: (error) => {
|
|
326
|
-
console.error(`Error in daemon-to-discord typing forwarder subscription. Retrying in ${typingRetryDelay}ms.`, error);
|
|
421
|
+
console.error(`Error in daemon-to-discord typing forwarder subscription for ${chatId}. Retrying in ${typingRetryDelay}ms.`, error);
|
|
327
422
|
typingSubscription?.unsubscribe();
|
|
328
423
|
typingSubscription = null;
|
|
329
|
-
if (signal?.aborted) return;
|
|
424
|
+
if (signal?.aborted || !activeSubscriptions.has(chatId)) return;
|
|
330
425
|
setTimeout(() => {
|
|
331
426
|
typingRetryDelay = Math.min(typingRetryDelay * 2, maxRetryDelay);
|
|
332
427
|
connectTyping();
|
|
@@ -334,15 +429,54 @@ async function startDaemonToDiscordForwarder(client, trpc, discordUserId, chatId
|
|
|
334
429
|
},
|
|
335
430
|
onComplete: () => {
|
|
336
431
|
typingSubscription = null;
|
|
337
|
-
if (!signal?.aborted) setTimeout(() => connectTyping(), typingRetryDelay);
|
|
432
|
+
if (!signal?.aborted && activeSubscriptions.has(chatId)) setTimeout(() => connectTyping(), typingRetryDelay);
|
|
338
433
|
}
|
|
339
434
|
});
|
|
340
435
|
};
|
|
436
|
+
activeSubscriptions.set(chatId, { unsubscribe: () => subscription?.unsubscribe() });
|
|
437
|
+
activeTypingSubscriptions.set(chatId, { unsubscribe: () => typingSubscription?.unsubscribe() });
|
|
341
438
|
connect();
|
|
342
439
|
connectTyping();
|
|
440
|
+
};
|
|
441
|
+
const syncSubscriptions = async () => {
|
|
442
|
+
if (signal?.aborted) return;
|
|
443
|
+
const state = await readDiscordState();
|
|
444
|
+
if (state.lastSyncedMessageIds) currentLastSyncedMessageIds = {
|
|
445
|
+
...currentLastSyncedMessageIds,
|
|
446
|
+
...state.lastSyncedMessageIds
|
|
447
|
+
};
|
|
448
|
+
const targetChatIds = /* @__PURE__ */ new Set();
|
|
449
|
+
targetChatIds.add(defaultChatId);
|
|
450
|
+
if (state.channelChatMap) {
|
|
451
|
+
for (const mappedEntry of Object.values(state.channelChatMap)) if (mappedEntry.chatId) targetChatIds.add(mappedEntry.chatId);
|
|
452
|
+
}
|
|
453
|
+
for (const targetChatId of targetChatIds) if (!activeSubscriptions.has(targetChatId)) startSubscriptionForChat(targetChatId);
|
|
454
|
+
for (const [activeChatId, sub] of activeSubscriptions.entries()) if (!targetChatIds.has(activeChatId)) {
|
|
455
|
+
sub.unsubscribe();
|
|
456
|
+
activeSubscriptions.delete(activeChatId);
|
|
457
|
+
activeTypingSubscriptions.get(activeChatId)?.unsubscribe();
|
|
458
|
+
activeTypingSubscriptions.delete(activeChatId);
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
return new Promise((resolve) => {
|
|
462
|
+
syncSubscriptions().catch(console.error);
|
|
463
|
+
const statePath = getDiscordStatePath();
|
|
464
|
+
const stateDir = path.dirname(statePath);
|
|
465
|
+
if (!fs.existsSync(stateDir)) fs.mkdirSync(stateDir, { recursive: true });
|
|
466
|
+
let debounceTimer = null;
|
|
467
|
+
const watcher = fs.watch(stateDir, (eventType, filename) => {
|
|
468
|
+
if (filename === path.basename(statePath)) {
|
|
469
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
470
|
+
debounceTimer = setTimeout(() => {
|
|
471
|
+
syncSubscriptions().catch(console.error);
|
|
472
|
+
}, 200);
|
|
473
|
+
}
|
|
474
|
+
});
|
|
343
475
|
signal?.addEventListener("abort", () => {
|
|
344
|
-
|
|
345
|
-
|
|
476
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
477
|
+
watcher.close();
|
|
478
|
+
for (const sub of activeSubscriptions.values()) sub.unsubscribe();
|
|
479
|
+
for (const sub of activeTypingSubscriptions.values()) sub.unsubscribe();
|
|
346
480
|
resolve();
|
|
347
481
|
});
|
|
348
482
|
});
|
|
@@ -369,23 +503,100 @@ async function main() {
|
|
|
369
503
|
}
|
|
370
504
|
const trpc = getTRPCClient();
|
|
371
505
|
const client = new Client({
|
|
372
|
-
intents: [
|
|
506
|
+
intents: [
|
|
507
|
+
GatewayIntentBits.DirectMessages,
|
|
508
|
+
GatewayIntentBits.MessageContent,
|
|
509
|
+
GatewayIntentBits.Guilds,
|
|
510
|
+
GatewayIntentBits.GuildMessages
|
|
511
|
+
],
|
|
373
512
|
partials: [Partials.Channel]
|
|
374
513
|
});
|
|
514
|
+
const filteringConfig = { filters: (await readDiscordState()).filters };
|
|
375
515
|
client.once(Events.ClientReady, (readyClient) => {
|
|
376
516
|
console.log(`Ready! Logged in as ${readyClient.user.tag}`);
|
|
377
|
-
startDaemonToDiscordForwarder(readyClient, trpc, config.authorizedUserId,
|
|
517
|
+
startDaemonToDiscordForwarder(readyClient, trpc, config.authorizedUserId, {
|
|
518
|
+
chatId: config.chatId,
|
|
519
|
+
config: filteringConfig
|
|
520
|
+
}).catch((error) => {
|
|
378
521
|
console.error("Error in daemon-to-discord forwarder:", error);
|
|
379
522
|
});
|
|
380
523
|
});
|
|
381
524
|
client.on(Events.MessageCreate, async (message) => {
|
|
382
525
|
if (message.author.id === client.user?.id) return;
|
|
383
|
-
if (message.
|
|
384
|
-
|
|
526
|
+
if (message.author.bot) return;
|
|
527
|
+
const externalContextId = message.channelId;
|
|
528
|
+
const currentState = await readDiscordState();
|
|
529
|
+
const mappedChatId = currentState.channelChatMap?.[externalContextId]?.chatId;
|
|
530
|
+
const isRoutingCommand = message.content.startsWith("/chat") || message.content.startsWith("/agent");
|
|
531
|
+
if (message.guild) {
|
|
532
|
+
const channelConfig = currentState.channelChatMap?.[externalContextId];
|
|
533
|
+
if (channelConfig?.requireMention !== void 0 ? channelConfig.requireMention : config.requireMention) {
|
|
534
|
+
const isMentioned = message.mentions.has(client.user.id);
|
|
535
|
+
let isReplyToBot = false;
|
|
536
|
+
if (message.reference && message.reference.messageId) try {
|
|
537
|
+
isReplyToBot = (await message.channel.messages.fetch(message.reference.messageId)).author.id === client.user.id;
|
|
538
|
+
} catch (err) {
|
|
539
|
+
console.error("Failed to fetch referenced message for mention check:", err);
|
|
540
|
+
}
|
|
541
|
+
if (!isMentioned && !isReplyToBot) return;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
if (!isAuthorized$1(message.author.id, config.authorizedUserId)) {
|
|
385
545
|
console.log(`Unauthorized message from ${message.author.tag} (${message.author.id}) ignored.`);
|
|
386
546
|
return;
|
|
387
547
|
}
|
|
388
548
|
console.log(`Received message from ${message.author.tag}: ${message.content}`);
|
|
549
|
+
if (isRoutingCommand) {
|
|
550
|
+
const stringChatMap = Object.fromEntries(Object.entries(currentState.channelChatMap || {}).map(([k, v]) => [k, v.chatId || ""]));
|
|
551
|
+
const routingResult = await handleRoutingCommand(message.content, externalContextId, stringChatMap, "discord", trpc);
|
|
552
|
+
if (routingResult) {
|
|
553
|
+
if (routingResult.type === "mapped") await updateDiscordState((latestState) => ({ channelChatMap: {
|
|
554
|
+
...latestState.channelChatMap || {},
|
|
555
|
+
[externalContextId]: {
|
|
556
|
+
...latestState.channelChatMap?.[externalContextId] || {},
|
|
557
|
+
chatId: routingResult.newChatId
|
|
558
|
+
}
|
|
559
|
+
} }));
|
|
560
|
+
await message.reply(routingResult.text);
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
let targetChatId = mappedChatId;
|
|
565
|
+
if (!targetChatId && !isRoutingCommand) if (!currentState.channelChatMap || Object.values(currentState.channelChatMap).every((entry) => !entry.chatId)) {
|
|
566
|
+
targetChatId = config.chatId || "default";
|
|
567
|
+
console.log(`First contact detected. Automatically mapping channel ${externalContextId} to chat ${targetChatId}.`);
|
|
568
|
+
await updateDiscordState((latestState) => ({ channelChatMap: {
|
|
569
|
+
...latestState.channelChatMap || {},
|
|
570
|
+
[externalContextId]: {
|
|
571
|
+
...latestState.channelChatMap?.[externalContextId] || {},
|
|
572
|
+
chatId: targetChatId
|
|
573
|
+
}
|
|
574
|
+
} }));
|
|
575
|
+
} else {
|
|
576
|
+
const isDirectMessage = !message.guild;
|
|
577
|
+
const isMentioned = message.mentions.has(client.user.id);
|
|
578
|
+
const isSlashCommand = message.content.startsWith("/");
|
|
579
|
+
if (isDirectMessage || isMentioned || isSlashCommand) {
|
|
580
|
+
console.log(`Unmapped channel ${externalContextId}, sending first contact warning.`);
|
|
581
|
+
await message.reply("This channel/space is not currently mapped to a daemon chat. Please use `/chat [chat-id]` or `/agent [agent-id]` to map it.");
|
|
582
|
+
} else console.log(`Unmapped channel ${externalContextId}, silently ignoring background message.`);
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
if (!targetChatId) targetChatId = config.chatId || "default";
|
|
586
|
+
const commandResult = await handleAdapterCommand(message.content, filteringConfig, trpc, targetChatId);
|
|
587
|
+
if (commandResult) {
|
|
588
|
+
if (commandResult.type === "text") {
|
|
589
|
+
if (commandResult.newConfig) {
|
|
590
|
+
filteringConfig.filters = commandResult.newConfig.filters;
|
|
591
|
+
await updateDiscordState({ filters: filteringConfig.filters });
|
|
592
|
+
}
|
|
593
|
+
await message.reply(commandResult.text);
|
|
594
|
+
} else if (commandResult.type === "debug") {
|
|
595
|
+
const formatted = commandResult.messages.length === 0 ? "No ignored background messages found." : `**Debug Output (${commandResult.messages.length} ignored messages):**\n\n` + commandResult.messages.map((msg) => formatMessage(msg)).join("\n\n---\n\n");
|
|
596
|
+
await message.reply(formatted);
|
|
597
|
+
}
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
389
600
|
const downloadedFiles = [];
|
|
390
601
|
if (message.attachments.size > 0) {
|
|
391
602
|
const tmpDir = path.join(getClawminiDir(process.cwd()), "tmp", "discord");
|
|
@@ -428,7 +639,7 @@ async function main() {
|
|
|
428
639
|
client: "cli",
|
|
429
640
|
data: {
|
|
430
641
|
message: finalContent,
|
|
431
|
-
chatId:
|
|
642
|
+
chatId: targetChatId,
|
|
432
643
|
files: downloadedFiles.length > 0 ? downloadedFiles : void 0,
|
|
433
644
|
adapter: "discord",
|
|
434
645
|
noWait: true
|
|
@@ -439,6 +650,9 @@ async function main() {
|
|
|
439
650
|
console.error("Failed to forward message to daemon:", error);
|
|
440
651
|
}
|
|
441
652
|
});
|
|
653
|
+
client.on(Events.InteractionCreate, async (interaction) => {
|
|
654
|
+
await handleDiscordInteraction(interaction, config, trpc);
|
|
655
|
+
});
|
|
442
656
|
try {
|
|
443
657
|
await client.login(config.botToken);
|
|
444
658
|
} catch (error) {
|
|
@@ -446,16 +660,7 @@ async function main() {
|
|
|
446
660
|
process.exit(1);
|
|
447
661
|
}
|
|
448
662
|
}
|
|
449
|
-
|
|
450
|
-
try {
|
|
451
|
-
if (typeof process === "undefined" || !process.argv || process.argv.length < 2) return false;
|
|
452
|
-
const argv1 = process.argv[1];
|
|
453
|
-
if (!argv1) return false;
|
|
454
|
-
return path.resolve(argv1) === path.resolve(fileURLToPath(import.meta.url));
|
|
455
|
-
} catch {
|
|
456
|
-
return false;
|
|
457
|
-
}
|
|
458
|
-
})()) main().catch((error) => {
|
|
663
|
+
main().catch((error) => {
|
|
459
664
|
console.error("Unhandled error in Discord Adapter:", error);
|
|
460
665
|
process.exit(1);
|
|
461
666
|
});
|