clawmini 0.0.7 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.changeset/README.md +8 -0
- package/.changeset/config.json +14 -0
- package/.github/workflows/release.yml +49 -0
- package/CHANGELOG.md +36 -0
- package/README.md +5 -4
- package/dist/adapter-discord/index.d.mts.map +1 -1
- package/dist/adapter-discord/index.mjs +465 -282
- package/dist/adapter-discord/index.mjs.map +1 -1
- package/dist/adapter-google-chat/index.mjs +367 -243
- package/dist/adapter-google-chat/index.mjs.map +1 -1
- package/dist/cli/index.mjs +684 -24
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lite.mjs +43 -13
- package/dist/cli/lite.mjs.map +1 -1
- package/dist/cli/{propose-policy.mjs → manage-policies.mjs} +270 -47
- package/dist/cli/manage-policies.mjs.map +1 -0
- package/dist/cli/run-host.d.mts +1 -0
- package/dist/cli/run-host.mjs +3090 -0
- package/dist/cli/run-host.mjs.map +1 -0
- package/dist/config-CPFQIGdG.mjs +57 -0
- package/dist/config-CPFQIGdG.mjs.map +1 -0
- package/dist/config-Dvl-Pov4.mjs +76 -0
- package/dist/config-Dvl-Pov4.mjs.map +1 -0
- package/dist/daemon/index.d.mts.map +1 -1
- package/dist/daemon/index.mjs +970 -332
- package/dist/daemon/index.mjs.map +1 -1
- package/dist/supervisor-actions-CiW56eLi.mjs +843 -0
- package/dist/supervisor-actions-CiW56eLi.mjs.map +1 -0
- package/dist/turn-log-buffer-DRgW53gl.mjs +767 -0
- package/dist/turn-log-buffer-DRgW53gl.mjs.map +1 -0
- package/dist/web/_app/immutable/chunks/{Drm9vgeP.js → 3AZlWB6U.js} +1 -1
- package/dist/web/_app/immutable/chunks/BhRSsUCh.js +2 -0
- package/dist/web/_app/immutable/chunks/BiLeM2i1.js +1 -0
- package/{web/.svelte-kit/output/client/_app/immutable/chunks/CME08kGM.js → dist/web/_app/immutable/chunks/BmBj85Ll.js} +1 -1
- package/dist/web/_app/immutable/chunks/BrERcKAH.js +1 -0
- package/dist/web/_app/immutable/chunks/Bv9252RM.js +1 -0
- package/dist/web/_app/immutable/chunks/CIXNBPKi.js +1 -0
- package/dist/web/_app/immutable/chunks/DISKL3GN.js +2 -0
- package/dist/web/_app/immutable/chunks/{Zeh-C-mx.js → DcpaLzmX.js} +1 -1
- package/dist/web/_app/immutable/chunks/DnQ3vS13.js +1 -0
- package/dist/web/_app/immutable/chunks/KsloHTKS.js +1 -0
- package/{web/.svelte-kit/output/client/_app/immutable/chunks/Ck-be5J2.js → dist/web/_app/immutable/chunks/RsHsUj-8.js} +2 -2
- package/dist/web/_app/immutable/chunks/{G_zz-Gou.js → wpfV79dV.js} +1 -1
- package/dist/web/_app/immutable/entry/app.CIw1Qj0n.js +2 -0
- package/dist/web/_app/immutable/entry/start.Di0-Jhte.js +1 -0
- package/dist/web/_app/immutable/nodes/{0.CYS8iApT.js → 0.DYyUA1au.js} +1 -1
- package/dist/web/_app/immutable/nodes/1.D-3QEMMZ.js +1 -0
- package/dist/web/_app/immutable/nodes/{2.BnwnD1Ki.js → 2.4olHnH7U.js} +1 -1
- package/{web/.svelte-kit/output/client/_app/immutable/nodes/3.Dr0ot9sV.js → dist/web/_app/immutable/nodes/3.4w0bE-m2.js} +3 -3
- package/dist/web/_app/immutable/nodes/4.CZvjhVHt.js +60 -0
- package/dist/web/_app/immutable/nodes/{5.BBGQ_i84.js → 5.DLbPVJY2.js} +1 -1
- package/dist/web/_app/version.json +1 -1
- package/dist/web/index.html +12 -12
- package/dist/workspace-oWmVh5mi.mjs +1001 -0
- package/dist/workspace-oWmVh5mi.mjs.map +1 -0
- package/docs/23_adapter_slash_autocomplete/development_log.md +19 -0
- package/docs/23_adapter_slash_autocomplete/notes.md +18 -0
- package/docs/23_adapter_slash_autocomplete/prd.md +46 -0
- package/docs/23_adapter_slash_autocomplete/questions.md +6 -0
- package/docs/23_adapter_slash_autocomplete/tickets.md +21 -0
- package/docs/24_subagent_job_policy_fixes/development_log.md +22 -0
- package/docs/24_subagent_job_policy_fixes/notes.md +28 -0
- package/docs/24_subagent_job_policy_fixes/prd.md +59 -0
- package/docs/24_subagent_job_policy_fixes/questions.md +3 -0
- package/docs/24_subagent_job_policy_fixes/tickets.md +49 -0
- package/docs/25_e2e_test_improvements/development_log.md +30 -0
- package/docs/25_e2e_test_improvements/notes.md +29 -0
- package/docs/25_e2e_test_improvements/prd.md +43 -0
- package/docs/25_e2e_test_improvements/questions.md +12 -0
- package/docs/25_e2e_test_improvements/tickets-2.md +22 -0
- package/docs/25_e2e_test_improvements/tickets.md +22 -0
- package/docs/25_policy_cwd/development_log.md +30 -0
- package/docs/25_policy_cwd/notes.md +28 -0
- package/docs/25_policy_cwd/prd.md +77 -0
- package/docs/25_policy_cwd/questions.md +6 -0
- package/docs/25_policy_cwd/tickets.md +77 -0
- package/docs/CLI_REFERENCE.md +3 -1
- package/docs/PHILOSOPHY.md +35 -0
- package/docs/adapter-visibility/SPEC.md +461 -0
- package/docs/adapter-visibility/SPEC_v2.md +202 -0
- package/docs/auto-update/SPEC.md +344 -0
- package/docs/backups/SPEC.md +296 -0
- package/docs/backups/clawmini.gitignore +69 -0
- package/docs/guides/assets/clawmini-avatar.png +0 -0
- package/docs/guides/backups.md +332 -0
- package/docs/guides/discord_adapter_setup.md +1 -1
- package/docs/guides/google_chat_adapter_setup.md +81 -0
- package/docs/unified-startup/SPEC.md +203 -0
- package/e2e/_helpers/test-environment.test.ts +49 -0
- package/e2e/_helpers/test-environment.ts +548 -0
- package/e2e/adapters/_google-chat-fixtures.ts +340 -0
- package/{src/cli/e2e → e2e/adapters}/adapter-discord.test.ts +22 -23
- package/e2e/adapters/adapter-google-chat-downtime.test.ts +157 -0
- package/e2e/adapters/adapter-google-chat-inbound.test.ts +697 -0
- package/e2e/adapters/adapter-google-chat-outbound.test.ts +297 -0
- package/e2e/adapters/adapter-google-chat-roundtrip.test.ts +56 -0
- package/e2e/adapters/adapter-google-chat-threads.test.ts +1078 -0
- package/e2e/agents/custom-api-env.test.ts +80 -0
- package/e2e/agents/export-lite-func.test.ts +104 -0
- package/e2e/agents/fallbacks.test.ts +124 -0
- package/e2e/agents/interrupt.test.ts +50 -0
- package/e2e/agents/no-reply-necessary.test.ts +57 -0
- package/e2e/agents/session-timeout-subagents.test.ts +76 -0
- package/e2e/agents/subagent-authorization.test.ts +246 -0
- package/e2e/agents/subagent-env.test.ts +49 -0
- package/e2e/agents/subagent-lifecycle.test.ts +782 -0
- package/e2e/agents/subagents-depth.test.ts +47 -0
- package/e2e/cli/agents.test.ts +176 -0
- package/e2e/cli/auto-update.test.ts +741 -0
- package/e2e/cli/basic.test.ts +44 -0
- package/{src/cli/e2e → e2e/cli}/export-lite.test.ts +16 -12
- package/e2e/cli/init-gitignore.test.ts +86 -0
- package/e2e/cli/init.test.ts +76 -0
- package/e2e/cli/messages.test.ts +363 -0
- package/e2e/cli/serve.test.ts +76 -0
- package/{src/cli/e2e → e2e/cli}/skills.test.ts +11 -10
- package/{src/cli/e2e → e2e/daemon}/daemon.test.ts +57 -195
- package/e2e/jobs/agent-jobs.test.ts +216 -0
- package/e2e/jobs/cron.test.ts +64 -0
- package/e2e/jobs/restart.test.ts +108 -0
- package/e2e/policies/approval-session.test.ts +69 -0
- package/e2e/policies/auto-create-policies-file.test.ts +35 -0
- package/e2e/policies/builtin-manage-policies.test.ts +184 -0
- package/e2e/policies/builtin-run-host.test.ts +180 -0
- package/e2e/policies/environment-policies.test.ts +177 -0
- package/e2e/policies/manage-policies.test.ts +566 -0
- package/e2e/policies/output-size.test.ts +98 -0
- package/e2e/policies/policies-context-cwd.test.ts +160 -0
- package/e2e/policies/relative-script-path.test.ts +60 -0
- package/e2e/policies/requests-show.test.ts +135 -0
- package/e2e/policies/requests.test.ts +208 -0
- package/e2e/policies/slash-policies.test.ts +308 -0
- package/e2e/policies/startup-cleanup.test.ts +48 -0
- package/e2e/routers/session-timeout.test.ts +106 -0
- package/e2e/routers/slash-model.test.ts +152 -0
- package/e2e/routers/slash-new.test.ts +50 -0
- package/e2e/routers/slash-restart-adapter.test.ts +96 -0
- package/e2e/routers/slash-restart.test.ts +114 -0
- package/e2e/routers/slash-shutdown.test.ts +55 -0
- package/e2e/routers/slash-stop.test.ts +232 -0
- package/e2e/routers/slash-upgrade.test.ts +88 -0
- package/{src/cli/e2e → e2e/sandbox}/environments.test.ts +14 -13
- package/eslint.config.js +6 -0
- package/napkin.md +1 -1
- package/package.json +8 -3
- package/src/adapter-discord/commands.test.ts +42 -0
- package/src/adapter-discord/commands.ts +33 -0
- package/src/adapter-discord/config.ts +12 -0
- package/src/adapter-discord/forwarder.test.ts +499 -21
- package/src/adapter-discord/forwarder.ts +343 -124
- package/src/adapter-discord/inbound-cache.test.ts +47 -0
- package/src/adapter-discord/inbound-cache.ts +37 -0
- package/src/adapter-discord/index.test.ts +67 -2
- package/src/adapter-discord/index.ts +84 -216
- package/src/adapter-discord/interactions.test.ts +54 -3
- package/src/adapter-discord/interactions.ts +97 -53
- package/src/adapter-discord/processMessage.ts +239 -0
- package/src/adapter-discord/state.ts +1 -0
- package/src/adapter-google-chat/auth.test.ts +9 -5
- package/src/adapter-google-chat/auth.ts +29 -23
- package/src/adapter-google-chat/cards.ts +7 -2
- package/src/adapter-google-chat/client.test.ts +37 -2
- package/src/adapter-google-chat/client.ts +138 -38
- package/src/adapter-google-chat/config.ts +19 -0
- package/src/adapter-google-chat/forwarder.test.ts +81 -56
- package/src/adapter-google-chat/forwarder.ts +394 -185
- package/src/adapter-google-chat/inbound-cache.test.ts +61 -0
- package/src/adapter-google-chat/inbound-cache.ts +36 -0
- package/src/adapter-google-chat/state.test.ts +1 -0
- package/src/adapter-google-chat/state.ts +9 -1
- package/src/adapter-google-chat/subscriptions.ts +8 -6
- package/src/cli/builtin-policies.ts +44 -0
- package/src/cli/commands/agents.ts +59 -5
- package/src/cli/commands/down.ts +54 -2
- package/src/cli/commands/environments.ts +8 -2
- package/src/cli/commands/init.ts +31 -0
- package/src/cli/commands/logs.ts +116 -0
- package/src/cli/commands/policies.ts +6 -4
- package/src/cli/commands/serve.test.ts +67 -0
- package/src/cli/commands/serve.ts +284 -0
- package/src/cli/commands/up.ts +122 -2
- package/src/cli/commands/web-api/agents.ts +3 -2
- package/src/cli/index.ts +4 -0
- package/src/cli/install-detection.test.ts +72 -0
- package/src/cli/install-detection.ts +48 -0
- package/src/cli/lite.ts +54 -22
- package/src/cli/manage-policies-utils.ts +104 -0
- package/src/cli/manage-policies.ts +291 -0
- package/src/cli/run-host.ts +45 -0
- package/src/cli/supervisor-actions.ts +267 -0
- package/src/cli/supervisor-control.test.ts +129 -0
- package/src/cli/supervisor-control.ts +155 -0
- package/src/cli/supervisor-pid.ts +68 -0
- package/src/cli/supervisor.ts +277 -0
- package/src/daemon/agent/agent-context.ts +11 -11
- package/src/daemon/agent/agent-session.ts +8 -1
- package/src/daemon/agent/chat-logger.test.ts +78 -9
- package/src/daemon/agent/chat-logger.ts +25 -5
- package/src/daemon/agent/turn-registry.test.ts +89 -0
- package/src/daemon/agent/turn-registry.ts +94 -0
- package/src/daemon/agent/types.ts +2 -0
- package/src/daemon/api/agent-policy-endpoints.ts +263 -0
- package/src/daemon/api/agent-router.ts +47 -126
- package/src/daemon/api/index.test.ts +1 -0
- package/src/daemon/api/policy-request.test.ts +7 -5
- package/src/daemon/api/router-utils.ts +6 -5
- package/src/daemon/api/subagent-router.ts +110 -74
- package/src/daemon/api/subagent-utils.test.ts +60 -0
- package/src/daemon/api/subagent-utils.ts +113 -87
- package/src/daemon/api/user-router.ts +34 -8
- package/src/daemon/auth.ts +1 -0
- package/src/daemon/cron.test.ts +62 -4
- package/src/daemon/cron.ts +42 -16
- package/src/daemon/events.ts +65 -0
- package/src/daemon/index.ts +24 -1
- package/src/daemon/message-interruption.test.ts +1 -0
- package/src/daemon/message-jobs.test.ts +1 -0
- package/src/daemon/message.ts +78 -14
- package/src/daemon/observation.test.ts +26 -18
- package/src/daemon/pending-replies.test.ts +112 -0
- package/src/daemon/pending-replies.ts +162 -0
- package/src/daemon/policy-request-service.ts +3 -1
- package/src/daemon/policy-utils.test.ts +66 -1
- package/src/daemon/policy-utils.ts +126 -1
- package/src/daemon/request-store.ts +31 -0
- package/src/daemon/routers/session-timeout.ts +4 -0
- package/src/daemon/routers/slash-model.test.ts +344 -0
- package/src/daemon/routers/slash-model.ts +207 -0
- package/src/daemon/routers/slash-policies.test.ts +38 -32
- package/src/daemon/routers/slash-policies.ts +84 -33
- package/src/daemon/routers/slash-restart.test.ts +69 -0
- package/src/daemon/routers/slash-restart.ts +36 -0
- package/src/daemon/routers/slash-shutdown.test.ts +50 -0
- package/src/daemon/routers/slash-shutdown.ts +28 -0
- package/src/daemon/routers/slash-upgrade.test.ts +116 -0
- package/src/daemon/routers/slash-upgrade.ts +76 -0
- package/src/daemon/routers/types.ts +7 -0
- package/src/daemon/routers.ts +16 -0
- package/src/shared/adapters/blockquote.test.ts +28 -0
- package/src/shared/adapters/blockquote.ts +20 -0
- package/src/shared/adapters/filtering.test.ts +224 -10
- package/src/shared/adapters/filtering.ts +95 -7
- package/src/shared/adapters/inbound-cache.test.ts +48 -0
- package/src/shared/adapters/inbound-cache.ts +54 -0
- package/src/shared/adapters/turn-log-buffer.ts +266 -0
- package/src/shared/adapters/turn-log.test.ts +389 -0
- package/src/shared/adapters/turn-log.ts +357 -0
- package/src/shared/agent-utils.ts +12 -5
- package/src/shared/chats.test.ts +4 -0
- package/src/shared/chats.ts +9 -0
- package/src/shared/config.ts +16 -1
- package/src/shared/lite.ts +76 -2
- package/src/shared/policies.ts +26 -0
- package/src/shared/template-manifest.ts +267 -0
- package/src/shared/utils/shell.ts +61 -0
- package/src/shared/version.ts +34 -0
- package/src/shared/workspace.test.ts +217 -0
- package/src/shared/workspace.ts +626 -48
- package/templates/environments/cladding/allowlist-domain.mjs +125 -0
- package/templates/environments/cladding/env.json +21 -1
- package/templates/environments/cladding/run-with-network.mjs +54 -0
- package/templates/environments/macos-proxy/allowlist-domain.mjs +95 -0
- package/templates/environments/macos-proxy/env.json +8 -1
- package/templates/environments/macos-proxy/proxy.mjs +42 -13
- package/templates/gemini/template.json +5 -0
- package/templates/gemini-claw/template.json +13 -0
- package/templates/skills/clawmini-requests/SKILL.md +69 -10
- package/templates/skills/run-host/SKILL.md +51 -0
- package/templates/skills/skill-creator/SKILL.md +4 -3
- package/templates/skills/skill-creator/scripts/validate.sh +52 -0
- package/tsdown.config.ts +10 -1
- package/vitest.config.ts +2 -2
- package/web/.svelte-kit/ambient.d.ts +292 -176
- package/web/.svelte-kit/generated/server/internal.js +1 -1
- package/web/.svelte-kit/output/client/.vite/manifest.json +127 -137
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{Drm9vgeP.js → 3AZlWB6U.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BhRSsUCh.js +2 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BiLeM2i1.js +1 -0
- package/{dist/web/_app/immutable/chunks/CME08kGM.js → web/.svelte-kit/output/client/_app/immutable/chunks/BmBj85Ll.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BrERcKAH.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/Bv9252RM.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/CIXNBPKi.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DISKL3GN.js +2 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{Zeh-C-mx.js → DcpaLzmX.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DnQ3vS13.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/KsloHTKS.js +1 -0
- package/{dist/web/_app/immutable/chunks/Ck-be5J2.js → web/.svelte-kit/output/client/_app/immutable/chunks/RsHsUj-8.js} +2 -2
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{G_zz-Gou.js → wpfV79dV.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/entry/app.CIw1Qj0n.js +2 -0
- package/web/.svelte-kit/output/client/_app/immutable/entry/start.Di0-Jhte.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{0.CYS8iApT.js → 0.DYyUA1au.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/1.D-3QEMMZ.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{2.BnwnD1Ki.js → 2.4olHnH7U.js} +1 -1
- package/{dist/web/_app/immutable/nodes/3.Dr0ot9sV.js → web/.svelte-kit/output/client/_app/immutable/nodes/3.4w0bE-m2.js} +3 -3
- package/web/.svelte-kit/output/client/_app/immutable/nodes/4.CZvjhVHt.js +60 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{5.BBGQ_i84.js → 5.DLbPVJY2.js} +1 -1
- package/web/.svelte-kit/output/client/_app/version.json +1 -1
- package/web/.svelte-kit/output/server/.vite/manifest.json +12 -10
- package/web/.svelte-kit/output/server/chunks/Icon.js +1 -1
- package/web/.svelte-kit/output/server/chunks/client.js +1 -1
- package/web/.svelte-kit/output/server/chunks/exports.js +1 -1
- package/web/.svelte-kit/output/server/chunks/index-server.js +2 -1
- package/web/.svelte-kit/output/server/chunks/internal.js +1 -1
- package/web/.svelte-kit/output/server/chunks/render-context.js +77 -0
- package/web/.svelte-kit/output/server/chunks/root.js +739 -788
- package/web/.svelte-kit/output/server/chunks/shared.js +234 -21
- package/web/.svelte-kit/output/server/index.js +126 -90
- package/web/.svelte-kit/output/server/manifest-full.js +1 -1
- package/web/.svelte-kit/output/server/manifest.js +1 -1
- package/web/.svelte-kit/output/server/nodes/0.js +1 -1
- package/web/.svelte-kit/output/server/nodes/1.js +1 -1
- package/web/.svelte-kit/output/server/nodes/2.js +1 -1
- package/web/.svelte-kit/output/server/nodes/3.js +1 -1
- package/web/.svelte-kit/output/server/nodes/4.js +1 -1
- package/web/.svelte-kit/output/server/nodes/5.js +1 -1
- package/web/.svelte-kit/output/server/remote-entry.js +245 -81
- package/web/.svelte-kit/tsconfig.json +4 -1
- package/dist/cli/propose-policy.mjs.map +0 -1
- package/dist/lite-CBxOT1y5.mjs +0 -241
- package/dist/lite-CBxOT1y5.mjs.map +0 -1
- package/dist/routing-D8rTxtaV.mjs +0 -245
- package/dist/routing-D8rTxtaV.mjs.map +0 -1
- package/dist/web/_app/immutable/chunks/B6YN0Nuq.js +0 -1
- package/dist/web/_app/immutable/chunks/BmRlVmv6.js +0 -1
- package/dist/web/_app/immutable/chunks/CK9JZLaG.js +0 -2
- package/dist/web/_app/immutable/chunks/Ck3rYNON.js +0 -1
- package/dist/web/_app/immutable/chunks/DMtIqaiV.js +0 -2
- package/dist/web/_app/immutable/chunks/DhD271EB.js +0 -1
- package/dist/web/_app/immutable/chunks/DpuLqk8d.js +0 -1
- package/dist/web/_app/immutable/chunks/DsIToJCP.js +0 -1
- package/dist/web/_app/immutable/chunks/bBmtyQMj.js +0 -1
- package/dist/web/_app/immutable/entry/app.CJmSwntr.js +0 -2
- package/dist/web/_app/immutable/entry/start.ZpUrT2ak.js +0 -1
- package/dist/web/_app/immutable/nodes/1.Bli0Hqzn.js +0 -1
- package/dist/web/_app/immutable/nodes/4.oBhvQhcA.js +0 -60
- package/dist/workspace-BJmJBfKi.mjs +0 -456
- package/dist/workspace-BJmJBfKi.mjs.map +0 -1
- package/src/cli/e2e/agents.test.ts +0 -140
- package/src/cli/e2e/basic.test.ts +0 -43
- package/src/cli/e2e/cron.test.ts +0 -132
- package/src/cli/e2e/export-lite-func.test.ts +0 -206
- package/src/cli/e2e/fallbacks.test.ts +0 -175
- package/src/cli/e2e/init.test.ts +0 -77
- package/src/cli/e2e/messages.test.ts +0 -332
- package/src/cli/e2e/propose-policy.test.ts +0 -203
- package/src/cli/e2e/requests.test.ts +0 -180
- package/src/cli/e2e/session-timeout.test.ts +0 -192
- package/src/cli/e2e/slash-new.test.ts +0 -93
- package/src/cli/e2e/subagents.test.ts +0 -106
- package/src/cli/e2e/utils.ts +0 -66
- package/src/cli/propose-policy.ts +0 -91
- package/web/.svelte-kit/output/client/_app/immutable/chunks/B6YN0Nuq.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BmRlVmv6.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/CK9JZLaG.js +0 -2
- package/web/.svelte-kit/output/client/_app/immutable/chunks/Ck3rYNON.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DMtIqaiV.js +0 -2
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DhD271EB.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DpuLqk8d.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DsIToJCP.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/bBmtyQMj.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/entry/app.CJmSwntr.js +0 -2
- package/web/.svelte-kit/output/client/_app/immutable/entry/start.ZpUrT2ak.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/1.Bli0Hqzn.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/4.oBhvQhcA.js +0 -60
- package/web/.svelte-kit/output/server/chunks/false.js +0 -4
- /package/dist/cli/{propose-policy.d.mts → manage-policies.d.mts} +0 -0
- /package/{src/cli/e2e → e2e/_helpers}/global-setup.ts +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["fsPromises","fsPromises","authClient","fsPromises"],"sources":["../../src/adapter-google-chat/config.ts","../../src/adapter-google-chat/state.ts","../../src/adapter-google-chat/utils.ts","../../src/adapter-google-chat/auth.ts","../../src/adapter-google-chat/subscriptions.ts","../../src/adapter-google-chat/cards.ts","../../src/adapter-google-chat/client.ts","../../src/adapter-google-chat/upload.ts","../../src/adapter-google-chat/forwarder.ts","../../src/adapter-google-chat/cron.ts","../../src/adapter-google-chat/index.ts"],"sourcesContent":["import fsPromises from 'node:fs/promises';\nimport path from 'node:path';\nimport { z } from 'zod';\nimport { getClawminiDir } from '../shared/workspace.js';\nimport fs from 'node:fs';\n\nexport const GoogleChatConfigSchema = z.looseObject({\n projectId: z.string().min(1, 'GCP Project ID is required.'),\n subscriptionName: z.string().min(1, 'Pub/Sub Subscription Name is required.'),\n topicName: z.string().min(1, 'Pub/Sub Topic Name is required.'),\n authorizedUsers: z.array(z.string()).min(1, 'At least one Authorized User is required.'),\n maxAttachmentSizeMB: z.number().default(25).optional(),\n chatId: z.string().default('default').optional(),\n directMessageName: z.string().optional(),\n driveUploadEnabled: z.boolean().default(true).optional(),\n requireMention: z.boolean().default(false),\n oauthClientId: z.string().optional(),\n oauthClientSecret: z.string().optional(),\n});\n\nexport type GoogleChatConfig = z.infer<typeof GoogleChatConfigSchema>;\n\nexport function getGoogleChatConfigPath(startDir = process.cwd()): string {\n return path.join(getClawminiDir(startDir), 'adapters', 'google-chat', 'config.json');\n}\n\nexport async function readGoogleChatConfig(\n startDir = process.cwd()\n): Promise<GoogleChatConfig | null> {\n const configPath = getGoogleChatConfigPath(startDir);\n try {\n const data = await fsPromises.readFile(configPath, 'utf-8');\n const parsed = JSON.parse(data);\n return GoogleChatConfigSchema.parse(parsed);\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n return null;\n }\n throw err;\n }\n}\n\nexport async function updateGoogleChatConfig(\n config: GoogleChatConfig,\n startDir = process.cwd()\n): Promise<void> {\n const configPath = getGoogleChatConfigPath(startDir);\n await fsPromises.writeFile(configPath, JSON.stringify(config, null, 2), 'utf-8');\n}\n\nexport async function initGoogleChatConfig(startDir = process.cwd()): Promise<void> {\n const configPath = getGoogleChatConfigPath(startDir);\n const configDir = path.dirname(configPath);\n\n await fsPromises.mkdir(configDir, { recursive: true });\n\n if (fs.existsSync(configPath)) {\n console.log(`Config file already exists at ${configPath}`);\n return;\n }\n\n const templateConfig = {\n projectId: 'YOUR_PROJECT_ID',\n topicName: 'YOUR_TOPIC_NAME',\n subscriptionName: 'YOUR_SUBSCRIPTION_NAME',\n authorizedUsers: ['user@example.com'],\n chatId: 'default',\n requireMention: false,\n oauthClientId: 'YOUR_OAUTH_CLIENT_ID',\n oauthClientSecret: 'YOUR_OAUTH_CLIENT_SECRET',\n };\n\n await fsPromises.writeFile(configPath, JSON.stringify(templateConfig, null, 2), 'utf-8');\n console.log(`Created template configuration file at ${configPath}`);\n console.log(\n 'Please update it with your actual GCP Project ID, Pub/Sub Topic Name, Pub/Sub Subscription Name, and Authorized Users.'\n );\n}\n\nexport function isAuthorized(userIdOrEmail: string, authorizedUsers: string[]): boolean {\n return authorizedUsers.some((u) => u.toLowerCase() === userIdOrEmail.toLowerCase());\n}\n","import fsPromises from 'node:fs/promises';\nimport path from 'node:path';\nimport { z } from 'zod';\nimport { getClawminiDir } from '../shared/workspace.js';\n\nexport const GoogleChatStateSchema = z.object({\n lastSyncedMessageIds: z.record(z.string(), z.string()).optional(),\n channelChatMap: z\n .record(\n z.string(),\n z.object({\n chatId: z.string().nullable().optional(),\n subscriptionId: z.string().optional(),\n expirationDate: z.string().optional(),\n requireMention: z.boolean().optional(),\n })\n )\n .optional(),\n oauthTokens: z.any().optional(),\n filters: z.record(z.string(), z.boolean()).optional(),\n});\n\nexport type GoogleChatState = z.infer<typeof GoogleChatStateSchema>;\n\nexport function getGoogleChatStatePath(startDir = process.cwd()): string {\n return path.join(getClawminiDir(startDir), 'adapters', 'google-chat', 'state.json');\n}\n\nexport async function readGoogleChatState(startDir = process.cwd()): Promise<GoogleChatState> {\n const statePath = getGoogleChatStatePath(startDir);\n try {\n const data = await fsPromises.readFile(statePath, 'utf-8');\n const parsed = JSON.parse(data);\n\n // Migrate legacy state\n if (parsed.lastSyncedMessageId && !parsed.lastSyncedMessageIds) {\n parsed.lastSyncedMessageIds = { default: parsed.lastSyncedMessageId };\n }\n if (parsed.driveOauthTokens && !parsed.oauthTokens) {\n parsed.oauthTokens = parsed.driveOauthTokens;\n delete parsed.driveOauthTokens;\n }\n if (parsed.channelChatMap) {\n for (const [key, value] of Object.entries(parsed.channelChatMap)) {\n if (typeof value === 'string') {\n parsed.channelChatMap[key] = { chatId: value };\n }\n }\n }\n\n return GoogleChatStateSchema.parse(parsed);\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n return {\n oauthTokens: undefined,\n };\n }\n throw err;\n }\n}\n\nlet stateUpdatePromise = Promise.resolve();\n\nexport function updateGoogleChatState(\n updates: Partial<GoogleChatState> | ((state: GoogleChatState) => Partial<GoogleChatState>),\n startDir = process.cwd()\n): Promise<GoogleChatState> {\n return new Promise((resolve, reject) => {\n stateUpdatePromise = stateUpdatePromise.then(async () => {\n try {\n const currentState = await readGoogleChatState(startDir);\n const resolvedUpdates = typeof updates === 'function' ? updates(currentState) : updates;\n const newState = { ...currentState, ...resolvedUpdates };\n const statePath = getGoogleChatStatePath(startDir);\n const dir = path.dirname(statePath);\n await fsPromises.mkdir(dir, { recursive: true });\n await fsPromises.writeFile(statePath, JSON.stringify(newState, null, 2), 'utf-8');\n resolve(newState);\n } catch (err) {\n console.error(`Failed to write Google Chat state:`, err);\n reject(err);\n }\n });\n });\n}\n","import { google } from 'googleapis';\nimport type { Readable } from 'node:stream';\nimport type { ChatMessage } from '../shared/chats.js';\n\nlet authClient: Awaited<ReturnType<typeof google.auth.getClient>> | null = null;\n\nexport function resetAuthClient(): void {\n authClient = null;\n}\n\nexport function buildPolicyCard(logMessage: ChatMessage) {\n const policyId = ('requestId' in logMessage && logMessage.requestId) || logMessage.id;\n return [\n {\n cardId: logMessage.id,\n card: {\n header: {\n title: 'Action Required: Policy Approval',\n subtitle: 'A request needs your review.',\n },\n sections: [\n {\n widgets: [\n {\n textParagraph: {\n text: logMessage.content || 'Please review this request.',\n },\n },\n {\n buttonList: {\n buttons: [\n {\n text: 'Approve',\n color: {\n red: 0,\n green: 0.5,\n blue: 0,\n alpha: 1,\n },\n onClick: {\n action: {\n function: 'approve',\n parameters: [{ key: 'policyId', value: policyId }],\n },\n },\n },\n {\n text: 'Reject',\n color: {\n red: 0.8,\n green: 0,\n blue: 0,\n alpha: 1,\n },\n onClick: {\n action: {\n function: 'reject',\n parameters: [{ key: 'policyId', value: policyId }],\n },\n },\n },\n ],\n },\n },\n ],\n },\n ],\n },\n },\n ];\n}\n\nexport function chunkString(str: string, size: number): string[] {\n const chunks: string[] = [];\n const chars = Array.from(str);\n for (let i = 0; i < chars.length; i += size) {\n chunks.push(chars.slice(i, i + size).join(''));\n }\n return chunks;\n}\n\n/**\n * Downloads a file attachment securely using Application Default Credentials (ADC).\n * @param resourceName The resourceName of the attachment data to download.\n * @param maxAttachmentSizeMB The maximum allowed attachment size in MB (defaults to 25).\n * @returns A Buffer containing the file data.\n */\nexport async function downloadAttachment(\n resourceName: string,\n maxAttachmentSizeMB: number = 25\n): Promise<Buffer> {\n // Use ADC to authenticate\n if (!authClient) {\n authClient = await google.auth.getClient({\n scopes: ['https://www.googleapis.com/auth/chat.bot'],\n });\n }\n const client = authClient;\n\n const url = `https://chat.googleapis.com/v1/media/${resourceName}?alt=media`;\n\n const response = await client.request<Readable>({\n url,\n method: 'GET',\n responseType: 'stream',\n });\n\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n let totalBytes = 0;\n const maxSizeBytes = maxAttachmentSizeMB * 1024 * 1024;\n\n response.data.on('data', (chunk: Buffer) => {\n totalBytes += chunk.length;\n if (totalBytes > maxSizeBytes) {\n response.data.destroy();\n reject(\n new Error(`Attachment exceeds maximum size of ${maxSizeBytes} bytes: ${totalBytes} bytes`)\n );\n } else {\n chunks.push(chunk);\n }\n });\n\n response.data.on('end', () => {\n resolve(Buffer.concat(chunks));\n });\n\n response.data.on('error', (err: Error) => {\n reject(err);\n });\n });\n}\n","import { google } from 'googleapis';\nimport http from 'node:http';\nimport type { GoogleChatConfig } from './config.js';\nimport { readGoogleChatState, updateGoogleChatState } from './state.js';\n\nlet authClient: Awaited<ReturnType<typeof google.auth.getClient>> | null = null;\nexport async function getAuthClient() {\n if (!authClient) {\n authClient = await google.auth.getClient({\n scopes: ['https://www.googleapis.com/auth/chat.bot'],\n });\n }\n return authClient;\n}\n\nlet userAuthClient: InstanceType<typeof google.auth.OAuth2> | null = null;\nlet userAuthPromise: Promise<InstanceType<typeof google.auth.OAuth2>> | null = null;\n\nexport async function getUserAuthClient(config: GoogleChatConfig) {\n if (userAuthClient) return userAuthClient;\n if (userAuthPromise) return userAuthPromise;\n\n if (!config.oauthClientId || !config.oauthClientSecret) {\n console.error('DEBUG config:', config);\n throw new Error(\n 'oauthClientId and oauthClientSecret are required in config.json for user authentication.'\n );\n }\n\n userAuthPromise = (async () => {\n const oauth2Client = new google.auth.OAuth2(\n config.oauthClientId,\n config.oauthClientSecret,\n 'http://localhost:31338/oauth2callback'\n );\n\n oauth2Client.on('tokens', async (tokens) => {\n try {\n const currentState = await readGoogleChatState();\n await updateGoogleChatState({\n oauthTokens: {\n ...currentState.oauthTokens,\n ...tokens,\n },\n });\n } catch (err) {\n console.error('Failed to save refreshed Google User tokens', err);\n }\n });\n\n const state = await readGoogleChatState();\n if (state.oauthTokens) {\n oauth2Client.setCredentials(state.oauthTokens);\n userAuthClient = oauth2Client;\n userAuthPromise = null;\n return oauth2Client;\n }\n\n const authUrl = oauth2Client.generateAuthUrl({\n access_type: 'offline',\n scope: [\n 'https://www.googleapis.com/auth/drive.file',\n 'https://www.googleapis.com/auth/chat.messages.readonly',\n ],\n prompt: 'consent',\n });\n\n console.log('\\n======================================================');\n console.log('Google User Authorization Required!');\n console.log('Please visit the following URL to authorize this bot:');\n console.log(authUrl);\n console.log('======================================================\\n');\n\n return new Promise<typeof oauth2Client>((resolve, reject) => {\n let timeoutId: NodeJS.Timeout;\n\n const server = http.createServer(async (req, res) => {\n if (req.url?.startsWith('/oauth2callback')) {\n const url = new URL(req.url, 'http://localhost:31338');\n const code = url.searchParams.get('code');\n if (code) {\n res.end('Authentication successful! You can close this window.');\n clearTimeout(timeoutId);\n server.close();\n try {\n const { tokens } = await oauth2Client.getToken(code);\n oauth2Client.setCredentials(tokens);\n\n await updateGoogleChatState({ oauthTokens: tokens });\n\n console.log('Google User authorization successful!');\n userAuthClient = oauth2Client;\n userAuthPromise = null;\n resolve(oauth2Client);\n } catch (err) {\n console.error('Failed to get token', err);\n userAuthPromise = null;\n reject(err);\n }\n } else {\n res.end('Authentication failed!');\n clearTimeout(timeoutId);\n server.close();\n userAuthPromise = null;\n reject(new Error('No code provided in OAuth callback'));\n }\n }\n });\n\n server.on('error', (err) => {\n console.error('Failed to start local OAuth server on port 31338', err);\n clearTimeout(timeoutId);\n userAuthPromise = null;\n reject(err);\n });\n\n server.listen(31338, '127.0.0.1', () => {\n timeoutId = setTimeout(\n () => {\n server.close();\n userAuthPromise = null;\n console.error('Google User authorization timed out after 5 minutes.');\n reject(new Error('Google User authorization timed out.'));\n },\n 5 * 60 * 1000\n );\n });\n });\n })();\n\n return userAuthPromise;\n}\n","import { google } from 'googleapis';\nimport type { GoogleChatConfig } from './config.js';\nimport { getAuthClient, getUserAuthClient } from './auth.js';\nimport { updateGoogleChatState, type GoogleChatState } from './state.js';\n\nexport async function handleAddedToSpace(\n spaceName: string,\n externalContextId: string,\n spaceType: string | undefined,\n targetChatId: string | null | undefined,\n mappedChatId: string | null | undefined,\n config: GoogleChatConfig\n) {\n if (spaceType !== 'DIRECT_MESSAGE') {\n try {\n const userAuthClient = await getUserAuthClient(config);\n const tokenResponse = await userAuthClient.getAccessToken();\n const token = tokenResponse.token;\n\n if (token) {\n const res = await fetch('https://workspaceevents.googleapis.com/v1/subscriptions', {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n targetResource: `//chat.googleapis.com/${spaceName}`,\n eventTypes: ['google.workspace.chat.message.v1.created'],\n payloadOptions: { includeResource: true },\n notificationEndpoint: {\n pubsubTopic: `projects/${config.projectId}/topics/${config.topicName}`,\n },\n }),\n });\n\n if (res.ok) {\n const subData = (await res.json()) as { name: string; expireTime: string };\n await updateGoogleChatState((latestState) => {\n const currentMap = latestState.channelChatMap || {};\n return {\n channelChatMap: {\n ...currentMap,\n [externalContextId]: {\n ...(currentMap[externalContextId] || {}),\n subscriptionId: subData.name,\n expirationDate: subData.expireTime,\n },\n },\n };\n });\n console.log(`Created subscription ${subData.name} for space ${externalContextId}`);\n } else {\n const errText = await res.text();\n console.error(`Failed to create subscription for space ${externalContextId}:`, errText);\n }\n }\n } catch (err) {\n console.error('Error setting up subscription on ADDED_TO_SPACE:', err);\n }\n }\n\n if (targetChatId && mappedChatId) {\n try {\n const authClient = await getAuthClient();\n const chatApi = google.chat({ version: 'v1', auth: authClient });\n await chatApi.spaces.messages.create({\n parent: externalContextId,\n requestBody: {\n text: `Hello! I am currently mapped to chat \\`${targetChatId}\\`.`,\n },\n });\n } catch (err) {\n console.error('Failed to send greeting on ADDED_TO_SPACE:', err);\n }\n }\n}\n\nexport async function handleRemovedFromSpace(\n externalContextId: string,\n currentState: GoogleChatState,\n config: GoogleChatConfig\n) {\n const subId = currentState.channelChatMap?.[externalContextId]?.subscriptionId;\n if (subId) {\n try {\n const userAuthClient = await getUserAuthClient(config);\n const tokenResponse = await userAuthClient.getAccessToken();\n const token = tokenResponse.token;\n\n if (token) {\n const res = await fetch(`https://workspaceevents.googleapis.com/v1/${subId}`, {\n method: 'DELETE',\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (res.ok) {\n console.log(`Deleted subscription ${subId}`);\n } else {\n const errText = await res.text();\n console.error(`Failed to delete subscription ${subId}:`, errText);\n }\n }\n } catch (err) {\n console.error('Error tearing down subscription on REMOVED_FROM_SPACE:', err);\n }\n }\n\n await updateGoogleChatState((latestState) => {\n const map = { ...(latestState.channelChatMap || {}) };\n const entry = map[externalContextId];\n if (entry) {\n if (!entry.chatId) {\n delete map[externalContextId];\n } else {\n delete entry.subscriptionId;\n delete entry.expirationDate;\n }\n }\n return { channelChatMap: map };\n });\n}\n","import { google } from 'googleapis';\nimport { getAuthClient } from './auth.js';\nimport type { RoutingTrpcClient } from '../shared/adapters/routing.js';\n\nexport async function handleCardClicked(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n event: any,\n targetChatId: string,\n trpc: RoutingTrpcClient\n) {\n const action = event.action;\n if (!action) return;\n\n const methodName = action.actionMethodName;\n const params = action.parameters || [];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const policyIdParam = params.find((p: any) => p.key === 'policyId');\n const policyId = policyIdParam?.value;\n\n if (policyId && (methodName === 'approve' || methodName === 'reject')) {\n const cmd = methodName === 'approve' ? `/approve ${policyId}` : `/reject ${policyId}`;\n\n if (event.message?.name) {\n try {\n const chatApi = google.chat({ version: 'v1', auth: await getAuthClient() });\n\n const originalCards = event.message.cardsV2 || [];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const updatedCards = originalCards.map((c: any) => {\n if (c.card?.sections) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n c.card.sections = c.card.sections.map((s: any) => {\n if (s.widgets) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n s.widgets = s.widgets.filter((w: any) => !w.buttonList);\n }\n return s;\n });\n }\n if (c.card?.header) {\n const statusText = methodName === 'approve' ? 'Approved' : 'Rejected';\n c.card.header.subtitle = `Policy ${statusText}`;\n }\n return c;\n });\n\n await chatApi.spaces.messages.update({\n name: event.message.name,\n updateMask: 'cardsV2',\n requestBody: {\n cardsV2: updatedCards,\n },\n });\n } catch (updateErr) {\n console.error(`Failed to update card for policy ${policyId}:`, updateErr);\n }\n }\n\n await trpc.sendMessage.mutate({\n type: 'send-message',\n client: 'cli',\n data: {\n message: cmd,\n chatId: targetChatId,\n adapter: 'google-chat',\n noWait: true,\n },\n });\n console.log(`Forwarded ${methodName} for policy ${policyId} to daemon.`);\n }\n}\n","/* eslint-disable max-lines */\nimport { PubSub, Message } from '@google-cloud/pubsub';\nimport { createTRPCClient, httpLink, splitLink, httpSubscriptionLink } from '@trpc/client';\nimport fs from 'node:fs';\nimport fsPromises from 'node:fs/promises';\nimport path from 'node:path';\nimport crypto from 'node:crypto';\n\nimport type { UserRouter as AppRouter } from '../daemon/api/index.js';\nimport { getSocketPath, getClawminiDir } from '../shared/workspace.js';\nimport { createUnixSocketFetch } from '../shared/fetch.js';\nimport { createUnixSocketEventSource } from '../shared/event-source.js';\nimport type { GoogleChatConfig } from './config.js';\nimport { isAuthorized, updateGoogleChatConfig } from './config.js';\nimport { readGoogleChatState, updateGoogleChatState } from './state.js';\nimport { downloadAttachment } from './utils.js';\nimport { handleAdapterCommand, type CommandTrpcClient } from '../shared/adapters/commands.js';\nimport { formatMessage, type FilteringConfig } from '../shared/adapters/filtering.js';\nimport { google } from 'googleapis';\nimport { getAuthClient } from './auth.js';\nimport { handleRoutingCommand, type RoutingTrpcClient } from '../shared/adapters/routing.js';\n\nimport { handleAddedToSpace, handleRemovedFromSpace } from './subscriptions.js';\nimport { handleCardClicked } from './cards.js';\n\nexport function getTRPCClient(options: { socketPath?: string } = {}) {\n const socketPath = options.socketPath ?? getSocketPath();\n\n if (!fs.existsSync(socketPath)) {\n throw new Error(`Daemon not running. Socket not found at ${socketPath}`);\n }\n\n const customFetch = createUnixSocketFetch(socketPath);\n const CustomEventSource = createUnixSocketEventSource(socketPath);\n\n return createTRPCClient<AppRouter>({\n links: [\n splitLink({\n condition(op) {\n return op.type === 'subscription';\n },\n true: httpSubscriptionLink({\n url: 'http://localhost',\n EventSource: CustomEventSource,\n }),\n false: httpLink({\n url: 'http://localhost',\n fetch: customFetch,\n }),\n }),\n ],\n });\n}\n\nexport function startGoogleChatIngestion(\n config: GoogleChatConfig,\n trpc: ReturnType<typeof getTRPCClient>,\n filteringConfig: FilteringConfig\n) {\n const pubsub = new PubSub({ projectId: config.projectId });\n const subscription = pubsub.subscription(config.subscriptionName);\n\n const seenMessageIds = new Map<string, number>();\n\n // Periodically clean up deduplication cache every 5 minutes\n setInterval(\n () => {\n const now = Date.now();\n for (const [id, ts] of seenMessageIds.entries()) {\n if (now - ts > 10 * 60 * 1000) {\n seenMessageIds.delete(id);\n }\n }\n },\n 5 * 60 * 1000\n ).unref();\n\n subscription.on('message', async (message: Message) => {\n const downloadedFiles: string[] = [];\n try {\n const dataString = message.data.toString('utf8');\n const parsedData = JSON.parse(dataString);\n\n const isWorkspaceEvent =\n message.attributes &&\n message.attributes['ce-type'] === 'google.workspace.chat.message.v1.created';\n\n const eventType = isWorkspaceEvent ? 'MESSAGE' : parsedData.type;\n\n const eventMessage = isWorkspaceEvent ? parsedData.message || parsedData : parsedData.message;\n const email =\n (isWorkspaceEvent\n ? eventMessage?.sender?.email\n : parsedData.user?.email || eventMessage?.sender?.email) || '';\n const senderName = eventMessage?.sender?.name || parsedData.user?.name || '';\n\n const space = isWorkspaceEvent\n ? eventMessage?.space\n : parsedData.space || eventMessage?.space;\n const senderType = eventMessage?.sender?.type || '';\n const messageId = eventMessage?.name || '';\n const text = (eventMessage?.argumentText || eventMessage?.text || '').trim();\n\n if (senderType === 'BOT') return void message.ack();\n\n if (messageId) {\n if (seenMessageIds.has(messageId)) return void message.ack();\n seenMessageIds.set(messageId, Date.now());\n }\n\n // Only handle MESSAGE, CARD_CLICKED, ADDED_TO_SPACE, and REMOVED_FROM_SPACE events\n if (\n eventType !== 'MESSAGE' &&\n eventType !== 'CARD_CLICKED' &&\n eventType !== 'ADDED_TO_SPACE' &&\n eventType !== 'REMOVED_FROM_SPACE'\n ) {\n message.ack();\n return;\n }\n\n let isUserAuthorized = false;\n let authorizedByEmail = false;\n\n if (email && isAuthorized(email, config.authorizedUsers)) {\n isUserAuthorized = true;\n authorizedByEmail = true;\n } else if (senderName && isAuthorized(senderName, config.authorizedUsers)) {\n isUserAuthorized = true;\n }\n\n if (!isUserAuthorized) {\n console.log(`Unauthorized or missing identifier: email=${email}, name=${senderName}`);\n console.log('DEBUG missing identifier parsedData:', JSON.stringify(parsedData, null, 2));\n message.ack();\n return;\n }\n\n // Automatically authorize user IDs if associated an authorized email\n if (authorizedByEmail && senderName && !isAuthorized(senderName, config.authorizedUsers)) {\n console.log(\n `Automatically authorizing user ID ${senderName} based on authorized email ${email}`\n );\n config.authorizedUsers.push(senderName);\n updateGoogleChatConfig(config).catch((err) =>\n console.error('Failed to update config with new user ID:', err)\n );\n }\n\n const identifier = email || senderName;\n\n const spaceName = space?.name;\n\n if (!spaceName) {\n console.log('Ignoring message: Could not determine space name.');\n message.ack();\n return;\n }\n\n const currentState = await readGoogleChatState();\n\n const externalContextId = spaceName;\n const mappedChatId = currentState.channelChatMap?.[externalContextId]?.chatId;\n const isRoutingCommand = text.startsWith('/chat') || text.startsWith('/agent');\n\n if (eventType === 'ADDED_TO_SPACE') {\n await handleAddedToSpace(\n spaceName as string,\n externalContextId,\n space?.type,\n mappedChatId,\n mappedChatId,\n config\n );\n if (!text) {\n message.ack();\n return;\n }\n }\n\n if (eventType === 'REMOVED_FROM_SPACE') {\n await handleRemovedFromSpace(externalContextId, currentState, config);\n message.ack();\n return;\n }\n\n if (isRoutingCommand) {\n const stringChatMap = Object.fromEntries(\n Object.entries(currentState.channelChatMap || {}).map(([k, v]) => [k, v.chatId || ''])\n );\n const routingResult = await handleRoutingCommand(\n text,\n externalContextId,\n stringChatMap,\n 'google-chat',\n trpc as unknown as RoutingTrpcClient\n );\n\n if (routingResult) {\n if (routingResult.type === 'mapped') {\n await updateGoogleChatState((latestState) => ({\n channelChatMap: {\n ...(latestState.channelChatMap || {}),\n [externalContextId]: {\n ...(latestState.channelChatMap?.[externalContextId] || {}),\n chatId: routingResult.newChatId,\n },\n },\n }));\n }\n\n try {\n const authClient = await getAuthClient();\n const chatApi = google.chat({ version: 'v1', auth: authClient });\n await chatApi.spaces.messages.create({\n parent: externalContextId,\n requestBody: { text: routingResult.text },\n });\n } catch (err) {\n console.error('Failed to send routing command reply:', err);\n }\n\n message.ack();\n return;\n }\n }\n\n let targetChatId = mappedChatId;\n\n if (!targetChatId && !isRoutingCommand) {\n const isFirstEverMessage =\n !currentState.channelChatMap ||\n Object.values(currentState.channelChatMap).every((entry) => !entry.chatId);\n\n if (isFirstEverMessage) {\n targetChatId = config.chatId || 'default';\n console.log(\n `First contact detected. Automatically mapping space ${externalContextId} to chat ${targetChatId}.`\n );\n await updateGoogleChatState((latestState) => ({\n channelChatMap: {\n ...(latestState.channelChatMap || {}),\n [externalContextId]: {\n ...(latestState.channelChatMap?.[externalContextId] || {}),\n chatId: targetChatId as string,\n },\n },\n }));\n } else {\n const isDirectMessage =\n space?.type === 'DIRECT_MESSAGE' || space?.singleUserBotDm === true;\n const isMentioned =\n Array.isArray(eventMessage?.annotations) &&\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n eventMessage.annotations.some((a: any) => a.type === 'USER_MENTION');\n const isSlashCommand = text.startsWith('/');\n if (isDirectMessage || isMentioned || isSlashCommand) {\n console.log(`Unmapped space ${externalContextId}, sending first contact warning.`);\n try {\n const authClient = await getAuthClient();\n const chatApi = google.chat({ version: 'v1', auth: authClient });\n await chatApi.spaces.messages.create({\n parent: externalContextId,\n requestBody: {\n text: 'This channel/space is not currently mapped to a daemon chat. Please use `/chat [chat-id]` or `/agent [agent-id]` to map it.',\n },\n });\n } catch (err) {\n console.error('Failed to send first contact warning:', err);\n }\n } else {\n console.log(\n `Unmapped space ${externalContextId}, silently ignoring background message.`\n );\n }\n message.ack();\n return;\n }\n }\n\n // Fallback typing safeguard\n if (!targetChatId) targetChatId = config.chatId || 'default';\n\n const isDirectMessage = space?.type === 'DIRECT_MESSAGE' || space?.singleUserBotDm === true;\n if (!isDirectMessage && eventType === 'MESSAGE') {\n const channelConfig = currentState.channelChatMap?.[externalContextId];\n const requiresMention =\n channelConfig?.requireMention !== undefined\n ? channelConfig.requireMention\n : config.requireMention;\n\n if (requiresMention && !isRoutingCommand) {\n const isMentioned =\n Array.isArray(eventMessage?.annotations) &&\n eventMessage.annotations.some(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (a: any) => a.type === 'USER_MENTION' && a.userMention?.user?.type === 'BOT'\n );\n\n let isReplyToBot = false;\n if (eventMessage?.threadReply && eventMessage.thread?.name) {\n try {\n const authClient = await getAuthClient();\n const chatApi = google.chat({ version: 'v1', auth: authClient });\n const response = await chatApi.spaces.messages.list({\n parent: externalContextId,\n filter: `thread.name=\"${eventMessage.thread.name}\"`,\n });\n isReplyToBot =\n response.data.messages?.some(\n (m) =>\n m.sender?.type === 'BOT' ||\n m.annotations?.some(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (a: any) => a.type === 'USER_MENTION' && a.userMention?.user?.type === 'BOT'\n )\n ) ?? false;\n } catch (err) {\n console.error('Failed to fetch thread messages for mention check:', err);\n }\n }\n\n // If requireMention is true and it's not a DM, ignore if not mentioned and not a thread reply to the bot.\n if (!isMentioned && !isReplyToBot) {\n message.ack();\n return;\n }\n }\n }\n\n if (eventType === 'CARD_CLICKED') {\n await handleCardClicked(\n parsedData,\n targetChatId as string,\n trpc as unknown as RoutingTrpcClient\n );\n message.ack();\n return;\n }\n\n const commandResult = await handleAdapterCommand(\n text,\n filteringConfig,\n trpc as unknown as CommandTrpcClient,\n targetChatId\n );\n\n if (commandResult) {\n let resultText = '';\n if (commandResult.type === 'text') {\n if (commandResult.newConfig) {\n filteringConfig.filters = commandResult.newConfig.filters;\n await updateGoogleChatState({ filters: filteringConfig.filters });\n }\n resultText = commandResult.text;\n } else if (commandResult.type === 'debug') {\n resultText =\n commandResult.messages.length === 0\n ? 'No ignored background messages found.'\n : `**Debug Output (${commandResult.messages.length} ignored messages):**\\n\\n` +\n commandResult.messages.map((msg) => formatMessage(msg)).join('\\n\\n---\\n\\n');\n }\n\n const authClient = await getAuthClient();\n const chatApi = google.chat({ version: 'v1', auth: authClient });\n await chatApi.spaces.messages.create({\n parent: spaceName as string,\n requestBody: { text: resultText },\n });\n message.ack();\n return;\n }\n const attachments = eventMessage?.attachment || [];\n\n if (attachments.length > 0) {\n const tmpDir = path.join(getClawminiDir(process.cwd()), 'tmp', 'google-chat');\n await fsPromises.mkdir(tmpDir, { recursive: true });\n\n for (const att of attachments) {\n const resourceName = att.attachmentDataRef?.resourceName;\n if (resourceName) {\n try {\n const buffer = await downloadAttachment(resourceName, config.maxAttachmentSizeMB);\n const uniqueName = `${crypto.randomUUID()}-${att.contentName || 'attachment'}`;\n const filePath = path.join(tmpDir, uniqueName);\n await fsPromises.writeFile(filePath, buffer);\n downloadedFiles.push(filePath);\n } catch (err) {\n console.error(`Error downloading attachment:`, err);\n }\n }\n }\n }\n\n await trpc.sendMessage.mutate({\n type: 'send-message',\n client: 'cli',\n data: {\n message: text,\n chatId: targetChatId,\n files: downloadedFiles.length > 0 ? downloadedFiles : undefined,\n adapter: 'google-chat',\n noWait: true,\n },\n });\n\n console.log(`Forwarded message from ${identifier} to daemon.`);\n message.ack();\n } catch (error) {\n console.error('Error processing Pub/Sub message:', error);\n for (const file of downloadedFiles) {\n try {\n await fsPromises.unlink(file);\n } catch (unlinkErr) {\n console.error(`Failed to delete downloaded file ${file} after error:`, unlinkErr);\n }\n }\n // Add a brief artificial delay before nacking to avoid tight retry loops\n await new Promise((resolve) => setTimeout(resolve, 2000));\n // Nack the message so it can be retried if it's a transient failure\n message.nack();\n }\n });\n\n subscription.on('error', (error) => {\n console.error('Pub/Sub subscription error:', error);\n });\n\n return subscription;\n}\n","import { google } from 'googleapis';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport mime from 'mime-types';\nimport type { GoogleChatConfig } from './config.js';\nimport { getUserAuthClient } from './auth.js';\nimport { getWorkspaceRoot } from '../shared/workspace.js';\n\nexport async function uploadFilesToDrive(\n files: string[],\n config: GoogleChatConfig\n): Promise<string[]> {\n const driveClient = await getUserAuthClient(config);\n const driveApi = google.drive({ version: 'v3', auth: driveClient });\n const workspaceRoot = getWorkspaceRoot(process.cwd());\n\n let folderId: string | undefined;\n try {\n const queryRes = await driveApi.files.list({\n q: \"mimeType='application/vnd.google-apps.folder' and name='Clawmini Uploads' and trashed=false\",\n fields: 'files(id)',\n });\n if (queryRes.data.files && queryRes.data.files.length > 0) {\n folderId = queryRes.data.files[0]!.id!;\n } else {\n const folderRes = await driveApi.files.create({\n requestBody: {\n name: 'Clawmini Uploads',\n mimeType: 'application/vnd.google-apps.folder',\n },\n fields: 'id',\n });\n if (folderRes.data.id) {\n folderId = folderRes.data.id;\n }\n }\n } catch (err) {\n console.error('Failed to create or find Clawmini Uploads folder', err);\n }\n\n const uploadPromises = files.map(async (fileRelPath) => {\n const filePath = path.resolve(workspaceRoot, fileRelPath);\n if (!fs.existsSync(filePath)) return null;\n\n const fileName = path.basename(filePath);\n const mimeType = mime.lookup(filePath) || 'application/octet-stream';\n\n try {\n const driveRes = await driveApi.files.create({\n requestBody: {\n name: fileName,\n ...(folderId ? { parents: [folderId] } : {}),\n },\n media: { mimeType, body: fs.createReadStream(filePath) },\n fields: 'id, webViewLink',\n });\n\n if (driveRes.data.id && driveRes.data.webViewLink) {\n const fileId = driveRes.data.id;\n try {\n await Promise.all(\n config.authorizedUsers.map((email) =>\n driveApi.permissions.create({\n fileId,\n requestBody: {\n type: 'user',\n role: 'reader',\n emailAddress: email,\n },\n sendNotificationEmail: false,\n })\n )\n );\n } catch (err) {\n console.error(`Failed to grant permissions for ${fileName}`, err);\n }\n return driveRes.data.webViewLink;\n }\n return null;\n } catch (err) {\n console.error(`Failed to upload file ${fileName} to Google Drive`, err);\n return `*(Failed to upload to Drive: ${fileName})*`;\n }\n });\n\n const uploadResults = await Promise.all(uploadPromises);\n return uploadResults.filter((r) => r !== null) as string[];\n}\n","/* eslint-disable max-lines */\nimport { google } from 'googleapis';\nimport { getAuthClient } from './auth.js';\nimport type { getTRPCClient } from './client.js';\nimport type { ChatMessage } from '../shared/chats.js';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport type { GoogleChatConfig } from './config.js';\nimport { readGoogleChatState, updateGoogleChatState, getGoogleChatStatePath } from './state.js';\nimport {\n shouldDisplayMessage,\n formatMessage,\n type FilteringConfig,\n} from '../shared/adapters/filtering.js';\nimport { buildPolicyCard, chunkString } from './utils.js';\nimport { uploadFilesToDrive } from './upload.js';\n\nexport async function startDaemonToGoogleChatForwarder(\n trpc: ReturnType<typeof getTRPCClient>,\n config: GoogleChatConfig,\n filteringConfig: FilteringConfig,\n signal?: AbortSignal\n) {\n const defaultChatId = config.chatId || 'default';\n\n const activeSubscriptions = new Map<string, { unsubscribe: () => void }>();\n let currentLastSyncedMessageIds = (await readGoogleChatState()).lastSyncedMessageIds || {};\n\n const saveLastMessageId = async (chatId: string, id: string) => {\n currentLastSyncedMessageIds = { ...currentLastSyncedMessageIds, [chatId]: id };\n return updateGoogleChatState((state) => ({\n lastSyncedMessageIds: {\n ...state.lastSyncedMessageIds,\n ...currentLastSyncedMessageIds,\n },\n }));\n };\n\n const startSubscriptionForChat = async (chatId: string) => {\n if (activeSubscriptions.has(chatId)) return;\n if (signal?.aborted) return;\n\n let lastMessageId = currentLastSyncedMessageIds[chatId];\n\n if (!lastMessageId) {\n try {\n const messages = await trpc.getMessages.query({ chatId, limit: 1 });\n if (Array.isArray(messages) && messages.length > 0) {\n const lastMsg = messages[messages.length - 1];\n if (lastMsg) {\n await saveLastMessageId(chatId, lastMsg.id);\n lastMessageId = lastMsg.id;\n }\n }\n } catch (error) {\n if (signal?.aborted) return;\n console.error(`Failed to fetch initial messages from daemon for ${chatId}:`, error);\n }\n }\n\n console.log(\n `Starting daemon-to-google-chat forwarder for chat ${chatId}, lastMessageId: ${lastMessageId}`\n );\n\n let retryDelay = 1000;\n const maxRetryDelay = 30000;\n\n let subscription: { unsubscribe: () => void } | null = null;\n let messageQueue = Promise.resolve();\n\n const connect = () => {\n if (signal?.aborted || !activeSubscriptions.has(chatId)) {\n return;\n }\n\n subscription = trpc.waitForMessages.subscribe(\n { chatId, lastMessageId },\n {\n onData: (messages) => {\n retryDelay = 1000;\n\n if (!Array.isArray(messages) || messages.length === 0) {\n return;\n }\n\n messageQueue = messageQueue\n .then(async () => {\n for (const rawMessage of messages) {\n if (signal?.aborted || !activeSubscriptions.has(chatId)) break;\n\n const message = rawMessage as ChatMessage;\n\n const isDisplayed = shouldDisplayMessage(message, filteringConfig);\n\n if (isDisplayed) {\n const logMessage = message;\n\n const currentState = await readGoogleChatState();\n let activeSpaceName: string | undefined;\n\n if (!activeSpaceName && currentState.channelChatMap) {\n const entry = Object.entries(currentState.channelChatMap).find(\n ([_, mapChatId]) => mapChatId?.chatId === chatId\n );\n if (entry) {\n activeSpaceName = entry[0];\n }\n }\n\n // We no longer fallback to config.directMessageName. If it's not mapped, we'll drop it below.\n\n const isPolicyRequest =\n logMessage.role === 'policy' && logMessage.status === 'pending';\n\n if (isPolicyRequest) {\n if (!activeSpaceName) {\n console.warn(\n 'No active Google Chat space to reply to. Ignoring policy request:',\n logMessage.content\n );\n await saveLastMessageId(chatId, logMessage.id).catch(console.error);\n lastMessageId = logMessage.id;\n continue;\n }\n\n try {\n const client = await getAuthClient();\n const chatApi = google.chat({ version: 'v1', auth: client });\n\n try {\n await chatApi.spaces.messages.create({\n parent: activeSpaceName as string,\n requestBody: {\n text: '',\n cardsV2: buildPolicyCard(logMessage),\n },\n });\n } catch (richError) {\n console.warn(\n 'Failed to send rich policy request to Google Chat, falling back to plain text:',\n richError\n );\n const policyId =\n ('requestId' in logMessage && logMessage.requestId) || logMessage.id;\n await chatApi.spaces.messages.create({\n parent: activeSpaceName as string,\n requestBody: {\n text: `Action Required: Policy Request\\n\\n${logMessage.content || 'A pending policy request requires your attention.'}\\n\\nApprove: \\`/approve ${policyId}\\`\\nReject: \\`/reject ${policyId} <optional_rationale>\\``,\n },\n });\n }\n } catch (error) {\n console.error('Failed to send policy request to Google Chat:', error);\n }\n\n await saveLastMessageId(chatId, logMessage.id).catch(console.error);\n lastMessageId = logMessage.id;\n continue;\n }\n\n const hasContent = !!logMessage.content?.trim();\n const files =\n 'files' in logMessage ? (logMessage.files as string[]) : undefined;\n const hasFiles = Array.isArray(files) && files.length > 0;\n\n if (\n ('level' in logMessage && logMessage.level === 'verbose') ||\n (!hasContent && !hasFiles)\n ) {\n await saveLastMessageId(chatId, logMessage.id).catch(console.error);\n lastMessageId = logMessage.id;\n continue;\n }\n\n if (!activeSpaceName) {\n console.warn(\n 'No active Google Chat space to reply to. Ignoring message:',\n logMessage.content\n );\n await saveLastMessageId(chatId, logMessage.id).catch(console.error);\n lastMessageId = logMessage.id;\n continue;\n }\n\n try {\n const client = await getAuthClient();\n const chatApi = google.chat({ version: 'v1', auth: client });\n\n let text = formatMessage(logMessage) || '';\n\n if (hasFiles && files) {\n const fileNames = files.map((f) => path.basename(f)).join(', ');\n\n if (\n config.driveUploadEnabled !== false &&\n config.oauthClientId &&\n config.oauthClientSecret\n ) {\n text += `\\n\\n`;\n try {\n const uploadResults = await uploadFilesToDrive(files, config);\n for (const result of uploadResults) {\n text += `${result}\\n`;\n }\n } catch (driveAuthErr) {\n console.error(\n 'Drive API/Auth Failed, degrading to local files output:',\n driveAuthErr\n );\n text += `*(Files generated: ${fileNames})*`;\n }\n } else {\n text += `\\n\\n*(Files generated: ${fileNames})*`;\n }\n }\n\n if (text.length > 4000) {\n const chunks = chunkString(text, 4000);\n for (let i = 0; i < chunks.length; i++) {\n if (signal?.aborted || !activeSubscriptions.has(chatId)) break;\n await chatApi.spaces.messages.create({\n parent: activeSpaceName as string,\n requestBody: { text: chunks[i] as string },\n });\n }\n } else {\n await chatApi.spaces.messages.create({\n parent: activeSpaceName as string,\n requestBody: { text },\n });\n }\n } catch (error) {\n console.error('Failed to send message to Google Chat:', error);\n }\n }\n\n await saveLastMessageId(chatId, message.id).catch(console.error);\n lastMessageId = message.id;\n }\n })\n .catch((error) => {\n console.error('Message queue failed, forcing reconnect...', error);\n subscription?.unsubscribe();\n subscription = null;\n if (signal?.aborted || !activeSubscriptions.has(chatId)) {\n return;\n }\n setTimeout(() => {\n retryDelay = Math.min(retryDelay * 2, maxRetryDelay);\n connect();\n }, retryDelay);\n });\n },\n onError: (error) => {\n console.error(\n `Error in daemon-to-google-chat forwarder subscription for ${chatId}. Retrying in ${retryDelay}ms.`,\n error\n );\n subscription?.unsubscribe();\n subscription = null;\n\n if (signal?.aborted || !activeSubscriptions.has(chatId)) {\n return;\n }\n\n setTimeout(() => {\n retryDelay = Math.min(retryDelay * 2, maxRetryDelay);\n connect();\n }, retryDelay);\n },\n onComplete: () => {\n subscription = null;\n if (!signal?.aborted && activeSubscriptions.has(chatId)) {\n setTimeout(() => connect(), retryDelay);\n }\n },\n }\n );\n };\n\n activeSubscriptions.set(chatId, {\n unsubscribe: () => subscription?.unsubscribe(),\n });\n\n connect();\n };\n\n const syncSubscriptions = async () => {\n if (signal?.aborted) return;\n const state = await readGoogleChatState();\n\n // Update local copy of last message IDs\n if (state.lastSyncedMessageIds) {\n currentLastSyncedMessageIds = {\n ...state.lastSyncedMessageIds,\n ...currentLastSyncedMessageIds,\n };\n }\n\n const targetChatIds = new Set<string>();\n targetChatIds.add(defaultChatId);\n\n if (state.channelChatMap) {\n for (const mappedEntry of Object.values(state.channelChatMap)) {\n if (mappedEntry.chatId) {\n targetChatIds.add(mappedEntry.chatId);\n }\n }\n }\n\n for (const targetChatId of targetChatIds) {\n if (!activeSubscriptions.has(targetChatId)) {\n startSubscriptionForChat(targetChatId);\n }\n }\n\n for (const [activeChatId, sub] of activeSubscriptions.entries()) {\n if (!targetChatIds.has(activeChatId)) {\n sub.unsubscribe();\n activeSubscriptions.delete(activeChatId);\n }\n }\n };\n return new Promise<void>((resolve) => {\n syncSubscriptions().catch(console.error);\n\n const statePath = getGoogleChatStatePath();\n const stateDir = path.dirname(statePath);\n if (!fs.existsSync(stateDir)) {\n fs.mkdirSync(stateDir, { recursive: true });\n }\n let debounceTimer: NodeJS.Timeout | null = null;\n const watcher = fs.watch(stateDir, (eventType, filename) => {\n if (filename === path.basename(statePath)) {\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n syncSubscriptions().catch(console.error);\n }, 200);\n }\n });\n\n signal?.addEventListener('abort', () => {\n if (debounceTimer) clearTimeout(debounceTimer);\n watcher.close();\n for (const sub of activeSubscriptions.values()) sub.unsubscribe();\n resolve();\n });\n });\n}\n","import { readGoogleChatState, updateGoogleChatState } from './state.js';\nimport { getUserAuthClient } from './auth.js';\nimport type { GoogleChatConfig } from './config.js';\n\nexport function startSubscriptionRenewalCron(config: GoogleChatConfig): NodeJS.Timeout {\n // Run every hour\n return setInterval(\n async () => {\n try {\n await renewExpiringSubscriptions(config);\n } catch (err) {\n console.error('Error in subscription renewal cron:', err);\n }\n },\n 1000 * 60 * 60\n );\n}\n\nexport async function renewExpiringSubscriptions(config: GoogleChatConfig): Promise<void> {\n const state = await readGoogleChatState();\n if (!state.channelChatMap) return;\n\n const now = Date.now();\n const FORTY_EIGHT_HOURS_MS = 48 * 60 * 60 * 1000;\n\n for (const [externalContextId, entry] of Object.entries(state.channelChatMap)) {\n if (entry.subscriptionId && entry.expirationDate) {\n const expirationTime = new Date(entry.expirationDate).getTime();\n const timeUntilExpiration = expirationTime - now;\n\n if (timeUntilExpiration < FORTY_EIGHT_HOURS_MS) {\n console.log(\n `Renewing expiring subscription ${entry.subscriptionId} for space ${externalContextId}`\n );\n try {\n const userAuthClient = await getUserAuthClient(config);\n const tokenResponse = await userAuthClient.getAccessToken();\n const token = tokenResponse.token;\n\n if (token) {\n const res = await fetch(\n `https://workspaceevents.googleapis.com/v1/${entry.subscriptionId}?updateMask=ttl`,\n {\n method: 'PATCH',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n ttl: '604800s', // 7 days\n }),\n }\n );\n\n if (res.ok) {\n const subData = (await res.json()) as { expireTime: string };\n await updateGoogleChatState((latestState) => {\n const currentMap = latestState.channelChatMap || {};\n return {\n channelChatMap: {\n ...currentMap,\n [externalContextId]: {\n ...(currentMap[externalContextId] || {}),\n expirationDate: subData.expireTime,\n },\n },\n };\n });\n console.log(`Successfully renewed subscription ${entry.subscriptionId}`);\n } else {\n const errText = await res.text();\n console.error(`Failed to renew subscription ${entry.subscriptionId}:`, errText);\n }\n }\n } catch (err) {\n console.error(`Error renewing subscription ${entry.subscriptionId}:`, err);\n }\n }\n }\n }\n}\n","#!/usr/bin/env node\n\nimport { initGoogleChatConfig, readGoogleChatConfig } from './config.js';\nimport { readGoogleChatState } from './state.js';\nimport { getTRPCClient, startGoogleChatIngestion } from './client.js';\nimport { startDaemonToGoogleChatForwarder } from './forwarder.js';\nimport { getUserAuthClient } from './auth.js';\nimport { startSubscriptionRenewalCron } from './cron.js';\nimport type { FilteringConfig } from '../shared/adapters/filtering.js';\n\nexport async function main() {\n const args = process.argv.slice(2);\n\n if (args[0] === 'init') {\n await initGoogleChatConfig();\n return;\n }\n\n console.log('Google Chat Adapter starting...');\n\n const config = await readGoogleChatConfig();\n if (!config) {\n console.error(\n 'Failed to load Google Chat configuration. Please ensure .clawmini/adapters/google-chat/config.json exists and is valid.'\n );\n process.exit(1);\n }\n\n if (config.oauthClientId && config.oauthClientSecret) {\n try {\n console.log('Initializing Google User Authentication...');\n await getUserAuthClient(config);\n } catch (err) {\n console.error('Failed to initialize Google User authentication:', err);\n process.exit(1);\n }\n }\n\n const trpc = getTRPCClient();\n const state = await readGoogleChatState();\n const filteringConfig: FilteringConfig = { filters: state.filters };\n\n // Start ingestion from Pub/Sub\n startGoogleChatIngestion(config, trpc, filteringConfig);\n console.log(`Listening to Pub/Sub subscription: ${config.subscriptionName}`);\n\n // Start forwarding from daemon to Google Chat API\n startDaemonToGoogleChatForwarder(trpc, config, filteringConfig).catch((error) => {\n console.error('Error in daemon-to-google-chat forwarder:', error);\n });\n\n // Start background cron for renewing Space Subscriptions\n startSubscriptionRenewalCron(config);\n}\n\nif (process.env.NODE_ENV !== 'test') {\n main().catch((error) => {\n console.error('Unhandled error in Google Chat Adapter:', error);\n process.exit(1);\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["authClient","downloadAttachment","defaultDownloadAttachment"],"sources":["../../src/adapter-google-chat/state.ts","../../src/adapter-google-chat/inbound-cache.ts","../../src/adapter-google-chat/utils.ts","../../src/adapter-google-chat/auth.ts","../../src/adapter-google-chat/subscriptions.ts","../../src/adapter-google-chat/cards.ts","../../src/adapter-google-chat/client.ts","../../src/adapter-google-chat/upload.ts","../../src/adapter-google-chat/forwarder.ts","../../src/adapter-google-chat/cron.ts","../../src/adapter-google-chat/index.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fsPromises from 'node:fs/promises';\nimport path from 'node:path';\nimport { z } from 'zod';\nimport { getClawminiDir } from '../shared/workspace.js';\n\nexport const GoogleChatStateSchema = z.object({\n lastSyncedMessageIds: z.record(z.string(), z.string()).optional(),\n channelChatMap: z\n .record(\n z.string(),\n z.object({\n chatId: z.string().nullable().optional(),\n subscriptionId: z.string().optional(),\n expirationDate: z.string().optional(),\n requireMention: z.boolean().optional(),\n threadsDisabled: z.boolean().optional(),\n })\n )\n .optional(),\n oauthTokens: z.any().optional(),\n filters: z.record(z.string(), z.boolean()).optional(),\n});\n\nexport type GoogleChatState = z.infer<typeof GoogleChatStateSchema>;\n\nexport function getGoogleChatStatePath(startDir = process.cwd()): string {\n return path.join(getClawminiDir(startDir), 'adapters', 'google-chat', 'state.json');\n}\n\nexport async function readGoogleChatState(startDir = process.cwd()): Promise<GoogleChatState> {\n const statePath = getGoogleChatStatePath(startDir);\n try {\n const data = await fsPromises.readFile(statePath, 'utf-8');\n const parsed = JSON.parse(data);\n\n // Migrate legacy state\n if (parsed.lastSyncedMessageId && !parsed.lastSyncedMessageIds) {\n parsed.lastSyncedMessageIds = { default: parsed.lastSyncedMessageId };\n }\n if (parsed.driveOauthTokens && !parsed.oauthTokens) {\n parsed.oauthTokens = parsed.driveOauthTokens;\n delete parsed.driveOauthTokens;\n }\n if (parsed.channelChatMap) {\n for (const [key, value] of Object.entries(parsed.channelChatMap)) {\n if (typeof value === 'string') {\n parsed.channelChatMap[key] = { chatId: value };\n }\n }\n }\n\n return GoogleChatStateSchema.parse(parsed);\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n return {\n oauthTokens: undefined,\n };\n }\n throw err;\n }\n}\n\nlet stateUpdatePromise = Promise.resolve();\n\nexport function updateGoogleChatState(\n updates: Partial<GoogleChatState> | ((state: GoogleChatState) => Partial<GoogleChatState>),\n startDir = process.cwd()\n): Promise<GoogleChatState> {\n return new Promise((resolve, reject) => {\n stateUpdatePromise = stateUpdatePromise.then(async () => {\n try {\n const currentState = await readGoogleChatState(startDir);\n const resolvedUpdates = typeof updates === 'function' ? updates(currentState) : updates;\n const newState = { ...currentState, ...resolvedUpdates };\n const statePath = getGoogleChatStatePath(startDir);\n const dir = path.dirname(statePath);\n await fsPromises.mkdir(dir, { recursive: true });\n // Atomic write: a plain writeFile truncates then writes, so a\n // concurrent reader (resolveSpaceForChat runs on every inbound) can\n // observe an empty file and throw `JSON.parse(\"\")`. rename(2) on the\n // same filesystem is atomic, so readers always see old or new.\n const tmpPath = `${statePath}.${process.pid}.${crypto.randomBytes(4).toString('hex')}.tmp`;\n await fsPromises.writeFile(tmpPath, JSON.stringify(newState, null, 2), 'utf-8');\n await fsPromises.rename(tmpPath, statePath);\n resolve(newState);\n } catch (err) {\n console.error(`Failed to write Google Chat state:`, err);\n reject(err);\n }\n });\n });\n}\n","/**\n * Google Chat-side wrapper around the shared inbound-message TTL cache.\n *\n * Ingestion records each inbound by its `gchatMessageName` (also sent to the\n * daemon as `externalRef`). When the forwarder later sees `turnStarted` with\n * that `externalRef`, it resolves the thread anchor by looking up the same\n * key here.\n */\nimport { createInboundCache } from '../shared/adapters/inbound-cache.js';\n\nexport const INBOUND_TTL_MS = 10 * 60 * 1000; // 10 minutes\n\ninterface GChatInboundValue {\n gchatThreadName: string;\n}\n\nconst cache = createInboundCache<GChatInboundValue>(INBOUND_TTL_MS);\n\nexport interface GChatInboundRecord {\n gchatMessageName: string;\n gchatThreadName: string;\n}\n\nexport function recordInbound(entry: GChatInboundRecord): void {\n cache.record(entry.gchatMessageName, { gchatThreadName: entry.gchatThreadName });\n}\n\nexport function resolveInbound(gchatMessageName: string): GChatInboundRecord | null {\n const value = cache.resolve(gchatMessageName);\n return value ? { gchatMessageName, gchatThreadName: value.gchatThreadName } : null;\n}\n\n/** Test hook: drop all cached records. */\nexport function _resetInboundCacheForTests(): void {\n cache.reset();\n}\n","import { google } from 'googleapis';\nimport type { Readable } from 'node:stream';\nimport type { ChatMessage } from '../shared/chats.js';\n\nlet authClient: Awaited<ReturnType<typeof google.auth.getClient>> | null = null;\n\nexport function resetAuthClient(): void {\n authClient = null;\n}\n\nexport function buildPolicyCard(logMessage: ChatMessage) {\n const policyId = ('requestId' in logMessage && logMessage.requestId) || logMessage.id;\n return [\n {\n cardId: logMessage.id,\n card: {\n header: {\n title: 'Action Required: Policy Approval',\n subtitle: 'A request needs your review.',\n },\n sections: [\n {\n widgets: [\n {\n textParagraph: {\n text: logMessage.content || 'Please review this request.',\n },\n },\n {\n buttonList: {\n buttons: [\n {\n text: 'Approve',\n color: {\n red: 0,\n green: 0.5,\n blue: 0,\n alpha: 1,\n },\n onClick: {\n action: {\n function: 'approve',\n parameters: [{ key: 'policyId', value: policyId }],\n },\n },\n },\n {\n text: 'Reject',\n color: {\n red: 0.8,\n green: 0,\n blue: 0,\n alpha: 1,\n },\n onClick: {\n action: {\n function: 'reject',\n parameters: [{ key: 'policyId', value: policyId }],\n },\n },\n },\n ],\n },\n },\n ],\n },\n ],\n },\n },\n ];\n}\n\nexport function chunkString(str: string, size: number): string[] {\n const chunks: string[] = [];\n const chars = Array.from(str);\n for (let i = 0; i < chars.length; i += size) {\n chunks.push(chars.slice(i, i + size).join(''));\n }\n return chunks;\n}\n\n/**\n * Downloads a file attachment securely using Application Default Credentials (ADC).\n * @param resourceName The resourceName of the attachment data to download.\n * @param maxAttachmentSizeMB The maximum allowed attachment size in MB (defaults to 25).\n * @returns A Buffer containing the file data.\n */\nexport async function downloadAttachment(\n resourceName: string,\n maxAttachmentSizeMB: number = 25\n): Promise<Buffer> {\n // Use ADC to authenticate\n if (!authClient) {\n authClient = await google.auth.getClient({\n scopes: ['https://www.googleapis.com/auth/chat.bot'],\n });\n }\n const client = authClient;\n\n const url = `https://chat.googleapis.com/v1/media/${resourceName}?alt=media`;\n\n const response = await client.request<Readable>({\n url,\n method: 'GET',\n responseType: 'stream',\n });\n\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n let totalBytes = 0;\n const maxSizeBytes = maxAttachmentSizeMB * 1024 * 1024;\n\n response.data.on('data', (chunk: Buffer) => {\n totalBytes += chunk.length;\n if (totalBytes > maxSizeBytes) {\n response.data.destroy();\n reject(\n new Error(`Attachment exceeds maximum size of ${maxSizeBytes} bytes: ${totalBytes} bytes`)\n );\n } else {\n chunks.push(chunk);\n }\n });\n\n response.data.on('end', () => {\n resolve(Buffer.concat(chunks));\n });\n\n response.data.on('error', (err: Error) => {\n reject(err);\n });\n });\n}\n","import { google } from 'googleapis';\nimport http from 'node:http';\nimport type { GoogleChatConfig } from './config.js';\nimport { readGoogleChatState, updateGoogleChatState } from './state.js';\n\nlet authClient: Awaited<ReturnType<typeof google.auth.getClient>> | null = null;\nexport async function getAuthClient() {\n if (!authClient) {\n authClient = await google.auth.getClient({\n scopes: ['https://www.googleapis.com/auth/chat.bot'],\n });\n }\n return authClient;\n}\n\nconst userAuthClients = new Map<string, InstanceType<typeof google.auth.OAuth2>>();\nconst userAuthPromises = new Map<string, Promise<InstanceType<typeof google.auth.OAuth2>>>();\n\nexport async function getUserAuthClient(config: GoogleChatConfig, startDir = process.cwd()) {\n const cached = userAuthClients.get(startDir);\n if (cached) return cached;\n const pending = userAuthPromises.get(startDir);\n if (pending) return pending;\n\n if (!config.oauthClientId || !config.oauthClientSecret) {\n console.error('DEBUG config:', config);\n throw new Error(\n 'oauthClientId and oauthClientSecret are required in config.json for user authentication.'\n );\n }\n\n const promise = (async () => {\n const oauth2Client = new google.auth.OAuth2(\n config.oauthClientId,\n config.oauthClientSecret,\n 'http://localhost:31338/oauth2callback'\n );\n\n oauth2Client.on('tokens', async (tokens) => {\n try {\n const currentState = await readGoogleChatState(startDir);\n await updateGoogleChatState(\n {\n oauthTokens: {\n ...currentState.oauthTokens,\n ...tokens,\n },\n },\n startDir\n );\n } catch (err) {\n console.error('Failed to save refreshed Google User tokens', err);\n }\n });\n\n const state = await readGoogleChatState(startDir);\n if (state.oauthTokens) {\n oauth2Client.setCredentials(state.oauthTokens);\n userAuthClients.set(startDir, oauth2Client);\n userAuthPromises.delete(startDir);\n return oauth2Client;\n }\n\n const authUrl = oauth2Client.generateAuthUrl({\n access_type: 'offline',\n scope: [\n 'https://www.googleapis.com/auth/drive.file',\n 'https://www.googleapis.com/auth/chat.messages.readonly',\n ],\n prompt: 'consent',\n });\n\n console.log('\\n======================================================');\n console.log('Google User Authorization Required!');\n console.log('Please visit the following URL to authorize this bot:');\n console.log(authUrl);\n console.log('======================================================\\n');\n\n return new Promise<typeof oauth2Client>((resolve, reject) => {\n let timeoutId: NodeJS.Timeout;\n\n const server = http.createServer(async (req, res) => {\n if (req.url?.startsWith('/oauth2callback')) {\n const url = new URL(req.url, 'http://localhost:31338');\n const code = url.searchParams.get('code');\n if (code) {\n res.end('Authentication successful! You can close this window.');\n clearTimeout(timeoutId);\n server.close();\n try {\n const { tokens } = await oauth2Client.getToken(code);\n oauth2Client.setCredentials(tokens);\n\n await updateGoogleChatState({ oauthTokens: tokens }, startDir);\n\n console.log('Google User authorization successful!');\n userAuthClients.set(startDir, oauth2Client);\n userAuthPromises.delete(startDir);\n resolve(oauth2Client);\n } catch (err) {\n console.error('Failed to get token', err);\n userAuthPromises.delete(startDir);\n reject(err);\n }\n } else {\n res.end('Authentication failed!');\n clearTimeout(timeoutId);\n server.close();\n userAuthPromises.delete(startDir);\n reject(new Error('No code provided in OAuth callback'));\n }\n }\n });\n\n server.on('error', (err) => {\n console.error('Failed to start local OAuth server on port 31338', err);\n clearTimeout(timeoutId);\n userAuthPromises.delete(startDir);\n reject(err);\n });\n\n server.listen(31338, '127.0.0.1', () => {\n timeoutId = setTimeout(\n () => {\n server.close();\n userAuthPromises.delete(startDir);\n console.error('Google User authorization timed out after 5 minutes.');\n reject(new Error('Google User authorization timed out.'));\n },\n 5 * 60 * 1000\n );\n });\n });\n })();\n\n userAuthPromises.set(startDir, promise);\n return promise;\n}\n","import { google } from 'googleapis';\nimport type { GoogleChatConfig } from './config.js';\nimport { getAuthClient, getUserAuthClient } from './auth.js';\nimport { updateGoogleChatState, type GoogleChatState } from './state.js';\n\nexport async function handleAddedToSpace(\n spaceName: string,\n externalContextId: string,\n spaceType: string | undefined,\n targetChatId: string | null | undefined,\n mappedChatId: string | null | undefined,\n config: GoogleChatConfig,\n startDir: string = process.cwd()\n) {\n if (spaceType !== 'DIRECT_MESSAGE') {\n try {\n const userAuthClient = await getUserAuthClient(config, startDir);\n const tokenResponse = await userAuthClient.getAccessToken();\n const token = tokenResponse.token;\n\n if (token) {\n const res = await fetch('https://workspaceevents.googleapis.com/v1/subscriptions', {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n targetResource: `//chat.googleapis.com/${spaceName}`,\n eventTypes: ['google.workspace.chat.message.v1.created'],\n payloadOptions: { includeResource: true },\n notificationEndpoint: {\n pubsubTopic: `projects/${config.projectId}/topics/${config.topicName}`,\n },\n }),\n });\n\n if (res.ok) {\n const subData = (await res.json()) as { name: string; expireTime: string };\n await updateGoogleChatState((latestState) => {\n const currentMap = latestState.channelChatMap || {};\n return {\n channelChatMap: {\n ...currentMap,\n [externalContextId]: {\n ...(currentMap[externalContextId] || {}),\n subscriptionId: subData.name,\n expirationDate: subData.expireTime,\n },\n },\n };\n }, startDir);\n console.log(`Created subscription ${subData.name} for space ${externalContextId}`);\n } else {\n const errText = await res.text();\n console.error(`Failed to create subscription for space ${externalContextId}:`, errText);\n }\n }\n } catch (err) {\n console.error('Error setting up subscription on ADDED_TO_SPACE:', err);\n }\n }\n\n if (targetChatId && mappedChatId) {\n try {\n const authClient = await getAuthClient();\n const chatApi = google.chat({ version: 'v1', auth: authClient });\n await chatApi.spaces.messages.create({\n parent: externalContextId,\n requestBody: {\n text: `Hello! I am currently mapped to chat \\`${targetChatId}\\`.`,\n },\n });\n } catch (err) {\n console.error('Failed to send greeting on ADDED_TO_SPACE:', err);\n }\n }\n}\n\nexport async function handleRemovedFromSpace(\n externalContextId: string,\n currentState: GoogleChatState,\n config: GoogleChatConfig,\n startDir: string = process.cwd()\n) {\n const subId = currentState.channelChatMap?.[externalContextId]?.subscriptionId;\n if (subId) {\n try {\n const userAuthClient = await getUserAuthClient(config, startDir);\n const tokenResponse = await userAuthClient.getAccessToken();\n const token = tokenResponse.token;\n\n if (token) {\n const res = await fetch(`https://workspaceevents.googleapis.com/v1/${subId}`, {\n method: 'DELETE',\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (res.ok) {\n console.log(`Deleted subscription ${subId}`);\n } else {\n const errText = await res.text();\n console.error(`Failed to delete subscription ${subId}:`, errText);\n }\n }\n } catch (err) {\n console.error('Error tearing down subscription on REMOVED_FROM_SPACE:', err);\n }\n }\n\n await updateGoogleChatState((latestState) => {\n const map = { ...(latestState.channelChatMap || {}) };\n const entry = map[externalContextId];\n if (entry) {\n if (!entry.chatId) {\n delete map[externalContextId];\n } else {\n delete entry.subscriptionId;\n delete entry.expirationDate;\n }\n }\n return { channelChatMap: map };\n }, startDir);\n}\n","import { google } from 'googleapis';\nimport { getAuthClient } from './auth.js';\nimport type { RoutingTrpcClient } from '../shared/adapters/routing.js';\n\ntype ChatApiLike = ReturnType<typeof google.chat>;\n\nexport async function handleCardClicked(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n event: any,\n targetChatId: string,\n trpc: RoutingTrpcClient,\n getChatApi?: () => Promise<ChatApiLike>\n) {\n const action = event.action;\n if (!action) return;\n\n const methodName = action.actionMethodName;\n const params = action.parameters || [];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const policyIdParam = params.find((p: any) => p.key === 'policyId');\n const policyId = policyIdParam?.value;\n\n if (policyId && (methodName === 'approve' || methodName === 'reject')) {\n const cmd = methodName === 'approve' ? `/approve ${policyId}` : `/reject ${policyId}`;\n\n if (event.message?.name) {\n try {\n const chatApi = getChatApi\n ? await getChatApi()\n : google.chat({ version: 'v1', auth: await getAuthClient() });\n\n const originalCards = event.message.cardsV2 || [];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const updatedCards = originalCards.map((c: any) => {\n if (c.card?.sections) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n c.card.sections = c.card.sections.map((s: any) => {\n if (s.widgets) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n s.widgets = s.widgets.filter((w: any) => !w.buttonList);\n }\n return s;\n });\n }\n if (c.card?.header) {\n const statusText = methodName === 'approve' ? 'Approved' : 'Rejected';\n c.card.header.subtitle = `Policy ${statusText}`;\n }\n return c;\n });\n\n await chatApi.spaces.messages.update({\n name: event.message.name,\n updateMask: 'cardsV2',\n requestBody: {\n cardsV2: updatedCards,\n },\n });\n } catch (updateErr) {\n console.error(`Failed to update card for policy ${policyId}:`, updateErr);\n }\n }\n\n await trpc.sendMessage.mutate({\n type: 'send-message',\n client: 'cli',\n data: {\n message: cmd,\n chatId: targetChatId,\n adapter: 'google-chat',\n noWait: true,\n },\n });\n console.log(`Forwarded ${methodName} for policy ${policyId} to daemon.`);\n }\n}\n","/* eslint-disable max-lines */\nimport { PubSub, Message } from '@google-cloud/pubsub';\nimport { createTRPCClient, httpLink, splitLink, httpSubscriptionLink } from '@trpc/client';\nimport fs from 'node:fs';\nimport fsPromises from 'node:fs/promises';\nimport path from 'node:path';\nimport crypto from 'node:crypto';\n\nimport type { UserRouter as AppRouter } from '../daemon/api/index.js';\nimport { getSocketPath, getClawminiDir } from '../shared/workspace.js';\nimport { createUnixSocketFetch } from '../shared/fetch.js';\nimport { createUnixSocketEventSource } from '../shared/event-source.js';\nimport type { GoogleChatConfig } from './config.js';\nimport { isAuthorized, updateGoogleChatConfig } from './config.js';\nimport { readGoogleChatState, updateGoogleChatState } from './state.js';\nimport { recordInbound } from './inbound-cache.js';\nimport { downloadAttachment as defaultDownloadAttachment } from './utils.js';\nimport { handleAdapterCommand, type CommandTrpcClient } from '../shared/adapters/commands.js';\nimport { formatMessage, type FilteringConfig } from '../shared/adapters/filtering.js';\nimport { google } from 'googleapis';\nimport { getAuthClient } from './auth.js';\nimport { handleRoutingCommand, type RoutingTrpcClient } from '../shared/adapters/routing.js';\nimport { prependBlockquote } from '../shared/adapters/blockquote.js';\n\nimport { handleAddedToSpace, handleRemovedFromSpace } from './subscriptions.js';\nimport { handleCardClicked } from './cards.js';\n\nexport function getTRPCClient(options: { socketPath?: string } = {}) {\n const socketPath = options.socketPath ?? getSocketPath();\n\n if (!fs.existsSync(socketPath)) {\n throw new Error(`Daemon not running. Socket not found at ${socketPath}`);\n }\n\n const customFetch = createUnixSocketFetch(socketPath);\n const CustomEventSource = createUnixSocketEventSource(socketPath);\n\n return createTRPCClient<AppRouter>({\n links: [\n splitLink({\n condition(op) {\n return op.type === 'subscription';\n },\n true: httpSubscriptionLink({\n url: 'http://localhost',\n EventSource: CustomEventSource,\n }),\n false: httpLink({\n url: 'http://localhost',\n fetch: customFetch,\n }),\n }),\n ],\n });\n}\n\nexport type GoogleChatApi = ReturnType<typeof google.chat>;\n\ninterface QuotedSender {\n type?: string | null;\n email?: string | null;\n name?: string | null;\n displayName?: string | null;\n}\n\n/**\n * Map a quoted message's sender to a short attribution label. Returns \"Bot\"\n * for the assistant, \"You\" if it's one of the configured authorized users,\n * and otherwise the sender's email (preferred) or `users/{id}` resource name.\n */\nexport function formatQuotedSender(\n sender: QuotedSender | undefined,\n authorizedUsers: string[]\n): string | undefined {\n if (!sender) return undefined;\n if (sender.type === 'BOT') return 'Assistant';\n const email = sender.email ?? undefined;\n const name = sender.name ?? undefined;\n if (\n (email && isAuthorized(email, authorizedUsers)) ||\n (name && isAuthorized(name, authorizedUsers))\n ) {\n return undefined;\n }\n return email || sender.displayName || name || undefined;\n}\n\nexport interface MessageSourceLike {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n on(event: string, listener: (...args: any[]) => void | Promise<void>): unknown;\n}\n\nexport interface GoogleChatIngestionDeps {\n /** Inbound message source (defaults to a real Pub/Sub subscription). */\n subscription?: MessageSourceLike;\n /** Google Chat API client (defaults to `google.chat()` with ADC credentials). */\n chatApi?: GoogleChatApi;\n /** Root directory for resolving adapter state/config (defaults to `process.cwd()`). */\n startDir?: string;\n /** Attachment downloader (defaults to the real Chat media endpoint). */\n downloadAttachment?: (resourceName: string, maxSizeMB?: number) => Promise<Buffer>;\n}\n\nexport function startGoogleChatIngestion(\n config: GoogleChatConfig,\n trpc: ReturnType<typeof getTRPCClient>,\n filteringConfig: FilteringConfig,\n deps: GoogleChatIngestionDeps = {}\n) {\n const startDir = deps.startDir ?? process.cwd();\n const subscription: MessageSourceLike =\n deps.subscription ??\n (() => {\n const pubsub = new PubSub({ projectId: config.projectId });\n return pubsub.subscription(config.subscriptionName);\n })();\n\n const getChatApi = async (): Promise<GoogleChatApi> => {\n if (deps.chatApi) return deps.chatApi;\n const authClient = await getAuthClient();\n return google.chat({ version: 'v1', auth: authClient });\n };\n\n const downloadAttachment = deps.downloadAttachment ?? defaultDownloadAttachment;\n\n const seenMessageIds = new Map<string, number>();\n\n // Periodically clean up deduplication cache every 5 minutes\n setInterval(\n () => {\n const now = Date.now();\n for (const [id, ts] of seenMessageIds.entries()) {\n if (now - ts > 10 * 60 * 1000) {\n seenMessageIds.delete(id);\n }\n }\n },\n 5 * 60 * 1000\n ).unref();\n\n subscription.on('message', async (message: Message) => {\n const downloadedFiles: string[] = [];\n try {\n const dataString = message.data.toString('utf8');\n const parsedData = JSON.parse(dataString);\n\n const isWorkspaceEvent =\n message.attributes &&\n message.attributes['ce-type'] === 'google.workspace.chat.message.v1.created';\n\n const eventType = isWorkspaceEvent ? 'MESSAGE' : parsedData.type;\n\n const eventMessage = isWorkspaceEvent ? parsedData.message || parsedData : parsedData.message;\n const email =\n (isWorkspaceEvent\n ? eventMessage?.sender?.email\n : parsedData.user?.email || eventMessage?.sender?.email) || '';\n const senderName = eventMessage?.sender?.name || parsedData.user?.name || '';\n\n const space = isWorkspaceEvent\n ? eventMessage?.space\n : parsedData.space || eventMessage?.space;\n const senderType = eventMessage?.sender?.type || '';\n const messageId = eventMessage?.name || '';\n const text = (eventMessage?.text || '').trim();\n\n // CARD_CLICKED events carry the bot's original card message as\n // `parsedData.message`, so `sender.type` is BOT even though the\n // interaction was initiated by a real user (`parsedData.user`).\n // Only drop BOT-authored MESSAGE events here.\n if (senderType === 'BOT' && eventType !== 'CARD_CLICKED') return void message.ack();\n\n if (messageId) {\n if (seenMessageIds.has(messageId)) return void message.ack();\n seenMessageIds.set(messageId, Date.now());\n }\n\n // Only handle MESSAGE, CARD_CLICKED, ADDED_TO_SPACE, and REMOVED_FROM_SPACE events\n if (\n eventType !== 'MESSAGE' &&\n eventType !== 'CARD_CLICKED' &&\n eventType !== 'ADDED_TO_SPACE' &&\n eventType !== 'REMOVED_FROM_SPACE'\n ) {\n message.ack();\n return;\n }\n\n let isUserAuthorized = false;\n let authorizedByEmail = false;\n\n if (email && isAuthorized(email, config.authorizedUsers)) {\n isUserAuthorized = true;\n authorizedByEmail = true;\n } else if (senderName && isAuthorized(senderName, config.authorizedUsers)) {\n isUserAuthorized = true;\n }\n\n if (!isUserAuthorized) {\n console.log(`Unauthorized or missing identifier: email=${email}, name=${senderName}`);\n console.log('DEBUG missing identifier parsedData:', JSON.stringify(parsedData, null, 2));\n message.ack();\n return;\n }\n\n // Automatically authorize user IDs if associated an authorized email\n if (authorizedByEmail && senderName && !isAuthorized(senderName, config.authorizedUsers)) {\n console.log(\n `Automatically authorizing user ID ${senderName} based on authorized email ${email}`\n );\n config.authorizedUsers.push(senderName);\n updateGoogleChatConfig(config, startDir).catch((err) =>\n console.error('Failed to update config with new user ID:', err)\n );\n }\n\n const identifier = email || senderName;\n\n const spaceName = space?.name;\n\n if (!spaceName) {\n console.log('Ignoring message: Could not determine space name.');\n message.ack();\n return;\n }\n\n const currentState = await readGoogleChatState(startDir);\n\n const externalContextId = spaceName;\n const mappedChatId = currentState.channelChatMap?.[externalContextId]?.chatId;\n const isRoutingCommand = text.startsWith('/chat') || text.startsWith('/agent');\n\n if (eventType === 'ADDED_TO_SPACE') {\n await handleAddedToSpace(\n spaceName as string,\n externalContextId,\n space?.type,\n mappedChatId,\n mappedChatId,\n config,\n startDir\n );\n if (!text) {\n message.ack();\n return;\n }\n }\n\n if (eventType === 'REMOVED_FROM_SPACE') {\n await handleRemovedFromSpace(externalContextId, currentState, config, startDir);\n message.ack();\n return;\n }\n\n if (isRoutingCommand) {\n const stringChatMap = Object.fromEntries(\n Object.entries(currentState.channelChatMap || {}).map(([k, v]) => [k, v.chatId || ''])\n );\n const routingResult = await handleRoutingCommand(\n text,\n externalContextId,\n stringChatMap,\n 'google-chat',\n trpc as unknown as RoutingTrpcClient\n );\n\n if (routingResult) {\n if (routingResult.type === 'mapped') {\n await updateGoogleChatState(\n (latestState) => ({\n channelChatMap: {\n ...(latestState.channelChatMap || {}),\n [externalContextId]: {\n ...(latestState.channelChatMap?.[externalContextId] || {}),\n chatId: routingResult.newChatId,\n },\n },\n }),\n startDir\n );\n }\n\n try {\n const chatApi = await getChatApi();\n await chatApi.spaces.messages.create({\n parent: externalContextId,\n requestBody: { text: routingResult.text },\n });\n } catch (err) {\n console.error('Failed to send routing command reply:', err);\n }\n\n message.ack();\n return;\n }\n }\n\n let targetChatId = mappedChatId;\n\n if (!targetChatId && !isRoutingCommand) {\n const isFirstEverMessage =\n !currentState.channelChatMap ||\n Object.values(currentState.channelChatMap).every((entry) => !entry.chatId);\n\n if (isFirstEverMessage) {\n targetChatId = config.chatId || 'default';\n console.log(\n `First contact detected. Automatically mapping space ${externalContextId} to chat ${targetChatId}.`\n );\n await updateGoogleChatState(\n (latestState) => ({\n channelChatMap: {\n ...(latestState.channelChatMap || {}),\n [externalContextId]: {\n ...(latestState.channelChatMap?.[externalContextId] || {}),\n chatId: targetChatId as string,\n },\n },\n }),\n startDir\n );\n } else {\n const isDirectMessage =\n space?.type === 'DIRECT_MESSAGE' || space?.singleUserBotDm === true;\n const isMentioned =\n Array.isArray(eventMessage?.annotations) &&\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n eventMessage.annotations.some((a: any) => a.type === 'USER_MENTION');\n const isSlashCommand = text.startsWith('/');\n if (isDirectMessage || isMentioned || isSlashCommand) {\n console.log(`Unmapped space ${externalContextId}, sending first contact warning.`);\n try {\n const chatApi = await getChatApi();\n await chatApi.spaces.messages.create({\n parent: externalContextId,\n requestBody: {\n text: 'This channel/space is not currently mapped to a daemon chat. Please use `/chat [chat-id]` or `/agent [agent-id]` to map it.',\n },\n });\n } catch (err) {\n console.error('Failed to send first contact warning:', err);\n }\n } else {\n console.log(\n `Unmapped space ${externalContextId}, silently ignoring background message.`\n );\n }\n message.ack();\n return;\n }\n }\n\n // Fallback typing safeguard\n if (!targetChatId) targetChatId = config.chatId || 'default';\n\n const isDirectMessage = space?.type === 'DIRECT_MESSAGE' || space?.singleUserBotDm === true;\n if (!isDirectMessage && eventType === 'MESSAGE') {\n const channelConfig = currentState.channelChatMap?.[externalContextId];\n const requiresMention =\n channelConfig?.requireMention !== undefined\n ? channelConfig.requireMention\n : config.requireMention;\n\n if (requiresMention && !isRoutingCommand) {\n const isMentioned =\n Array.isArray(eventMessage?.annotations) &&\n eventMessage.annotations.some(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (a: any) => a.type === 'USER_MENTION' && a.userMention?.user?.type === 'BOT'\n );\n\n let isReplyToBot = false;\n if (eventMessage?.threadReply && eventMessage.thread?.name) {\n try {\n const chatApi = await getChatApi();\n const response = await chatApi.spaces.messages.list({\n parent: externalContextId,\n filter: `thread.name=\"${eventMessage.thread.name}\"`,\n });\n isReplyToBot =\n response.data.messages?.some(\n (m) =>\n m.sender?.type === 'BOT' ||\n m.annotations?.some(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (a: any) => a.type === 'USER_MENTION' && a.userMention?.user?.type === 'BOT'\n )\n ) ?? false;\n } catch (err) {\n console.error('Failed to fetch thread messages for mention check:', err);\n }\n }\n\n // If requireMention is true and it's not a DM, ignore if not mentioned and not a thread reply to the bot.\n if (!isMentioned && !isReplyToBot) {\n message.ack();\n return;\n }\n }\n }\n\n if (eventType === 'CARD_CLICKED') {\n await handleCardClicked(\n parsedData,\n targetChatId as string,\n trpc as unknown as RoutingTrpcClient,\n getChatApi\n );\n message.ack();\n return;\n }\n\n const commandResult = await handleAdapterCommand(\n text,\n filteringConfig,\n trpc as unknown as CommandTrpcClient,\n targetChatId\n );\n\n if (commandResult) {\n let resultText = '';\n if (commandResult.type === 'text') {\n if (commandResult.newConfig) {\n filteringConfig.filters = commandResult.newConfig.filters;\n await updateGoogleChatState({ filters: filteringConfig.filters }, startDir);\n }\n resultText = commandResult.text;\n } else if (commandResult.type === 'debug') {\n resultText =\n commandResult.messages.length === 0\n ? 'No ignored background messages found.'\n : `**Debug Output (${commandResult.messages.length} ignored messages):**\\n\\n` +\n commandResult.messages.map((msg) => formatMessage(msg)).join('\\n\\n---\\n\\n');\n }\n\n const chatApi = await getChatApi();\n await chatApi.spaces.messages.create({\n parent: spaceName as string,\n requestBody: { text: resultText },\n });\n message.ack();\n return;\n }\n const attachments = eventMessage?.attachment || [];\n\n if (attachments.length > 0) {\n const tmpDir = path.join(getClawminiDir(startDir), 'tmp', 'google-chat');\n await fsPromises.mkdir(tmpDir, { recursive: true });\n\n for (const att of attachments) {\n const resourceName = att.attachmentDataRef?.resourceName;\n if (resourceName) {\n try {\n const buffer = await downloadAttachment(resourceName, config.maxAttachmentSizeMB);\n const uniqueName = `${crypto.randomUUID()}-${att.contentName || 'attachment'}`;\n const filePath = path.join(tmpDir, uniqueName);\n await fsPromises.writeFile(filePath, buffer);\n downloadedFiles.push(filePath);\n } catch (err) {\n console.error(`Error downloading attachment:`, err);\n }\n }\n }\n }\n\n let forwardedText = text;\n const quotedMetadata = eventMessage?.quotedMessageMetadata;\n if (quotedMetadata) {\n let quotedText: string | undefined = quotedMetadata.quotedMessageSnapshot?.text;\n let quotedSender: QuotedSender | undefined = quotedMetadata.quotedMessageSnapshot?.sender;\n if ((!quotedText || !quotedSender) && quotedMetadata.name) {\n try {\n const chatApi = await getChatApi();\n const quotedRes = await chatApi.spaces.messages.get({ name: quotedMetadata.name });\n quotedText = quotedText || quotedRes.data?.text || undefined;\n quotedSender =\n quotedSender || (quotedRes.data?.sender as QuotedSender | undefined) || undefined;\n } catch (err) {\n console.error('Failed to fetch quoted message:', err);\n }\n }\n if (quotedText) {\n const senderLabel = formatQuotedSender(quotedSender, config.authorizedUsers);\n forwardedText = prependBlockquote(quotedText, text, senderLabel);\n }\n }\n\n const gchatThreadName: string | undefined =\n eventMessage?.thread?.name ?? eventMessage?.threadName ?? undefined;\n if (messageId && gchatThreadName) {\n recordInbound({ gchatMessageName: messageId, gchatThreadName });\n }\n\n await trpc.sendMessage.mutate({\n type: 'send-message',\n client: 'cli',\n data: {\n message: forwardedText,\n chatId: targetChatId,\n files: downloadedFiles.length > 0 ? downloadedFiles : undefined,\n adapter: 'google-chat',\n noWait: true,\n ...(messageId ? { externalRef: messageId } : {}),\n },\n });\n\n console.log(`Forwarded message from ${identifier} to daemon.`);\n message.ack();\n } catch (error) {\n console.error('Error processing Pub/Sub message:', error);\n for (const file of downloadedFiles) {\n try {\n await fsPromises.unlink(file);\n } catch (unlinkErr) {\n console.error(`Failed to delete downloaded file ${file} after error:`, unlinkErr);\n }\n }\n // Add a brief artificial delay before nacking to avoid tight retry loops\n await new Promise((resolve) => setTimeout(resolve, 2000));\n // Nack the message so it can be retried if it's a transient failure\n message.nack();\n }\n });\n\n subscription.on('error', (error) => {\n console.error('Pub/Sub subscription error:', error);\n });\n\n return subscription;\n}\n","import { google } from 'googleapis';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport mime from 'mime-types';\nimport type { GoogleChatConfig } from './config.js';\nimport { getUserAuthClient } from './auth.js';\nimport { getWorkspaceRoot } from '../shared/workspace.js';\n\nexport async function uploadFilesToDrive(\n files: string[],\n config: GoogleChatConfig\n): Promise<string[]> {\n const driveClient = await getUserAuthClient(config);\n const driveApi = google.drive({ version: 'v3', auth: driveClient });\n const workspaceRoot = getWorkspaceRoot(process.cwd());\n\n let folderId: string | undefined;\n try {\n const queryRes = await driveApi.files.list({\n q: \"mimeType='application/vnd.google-apps.folder' and name='Clawmini Uploads' and trashed=false\",\n fields: 'files(id)',\n });\n if (queryRes.data.files && queryRes.data.files.length > 0) {\n folderId = queryRes.data.files[0]!.id!;\n } else {\n const folderRes = await driveApi.files.create({\n requestBody: {\n name: 'Clawmini Uploads',\n mimeType: 'application/vnd.google-apps.folder',\n },\n fields: 'id',\n });\n if (folderRes.data.id) {\n folderId = folderRes.data.id;\n }\n }\n } catch (err) {\n console.error('Failed to create or find Clawmini Uploads folder', err);\n }\n\n const uploadPromises = files.map(async (fileRelPath) => {\n const filePath = path.resolve(workspaceRoot, fileRelPath);\n if (!fs.existsSync(filePath)) return null;\n\n const fileName = path.basename(filePath);\n const mimeType = mime.lookup(filePath) || 'application/octet-stream';\n\n try {\n const driveRes = await driveApi.files.create({\n requestBody: {\n name: fileName,\n ...(folderId ? { parents: [folderId] } : {}),\n },\n media: { mimeType, body: fs.createReadStream(filePath) },\n fields: 'id, webViewLink',\n });\n\n if (driveRes.data.id && driveRes.data.webViewLink) {\n const fileId = driveRes.data.id;\n try {\n await Promise.all(\n config.authorizedUsers.map((email) =>\n driveApi.permissions.create({\n fileId,\n requestBody: {\n type: 'user',\n role: 'reader',\n emailAddress: email,\n },\n sendNotificationEmail: false,\n })\n )\n );\n } catch (err) {\n console.error(`Failed to grant permissions for ${fileName}`, err);\n }\n return driveRes.data.webViewLink;\n }\n return null;\n } catch (err) {\n console.error(`Failed to upload file ${fileName} to Google Drive`, err);\n return `*(Failed to upload to Drive: ${fileName})*`;\n }\n });\n\n const uploadResults = await Promise.all(uploadPromises);\n return uploadResults.filter((r) => r !== null) as string[];\n}\n","/* eslint-disable max-lines */\nimport { google } from 'googleapis';\nimport { getAuthClient } from './auth.js';\nimport type { getTRPCClient, GoogleChatApi } from './client.js';\nimport type { ChatMessage } from '../shared/chats.js';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport type { GoogleChatConfig } from './config.js';\nimport { readGoogleChatState, updateGoogleChatState, getGoogleChatStatePath } from './state.js';\nimport { resolveInbound } from './inbound-cache.js';\nimport {\n routeMessage,\n formatMessage,\n type Destination,\n type FilteringConfig,\n} from '../shared/adapters/filtering.js';\nimport { createTurnLogBuffer, type TurnLogBuffer } from '../shared/adapters/turn-log-buffer.js';\nimport { buildPolicyCard, chunkString } from './utils.js';\nimport { uploadFilesToDrive } from './upload.js';\n\n/**\n * Google Chat anchor handle: the space (`parent`) and the thread (`thread.name`)\n * are both required by the spaces.messages.create API, so we carry them\n * together as the buffer's opaque `TAnchor`.\n */\ninterface GChatAnchor {\n spaceName: string;\n threadName: string;\n}\n\nexport interface GoogleChatForwarderDeps {\n /** Google Chat API client (defaults to `google.chat()` with ADC credentials). */\n chatApi?: GoogleChatApi;\n /** Root directory for resolving adapter state (defaults to `process.cwd()`). */\n startDir?: string;\n}\n\ninterface ThreadLogOptions {\n maxToolPreview: number;\n maxLogMessageChars: number;\n editDebounceMs: number;\n}\n\nconst DEFAULT_THREAD_LOG_OPTS: ThreadLogOptions = {\n maxToolPreview: 400,\n maxLogMessageChars: 3500,\n editDebounceMs: 1000,\n};\n\nfunction resolveThreadLogOpts(config: GoogleChatConfig): ThreadLogOptions {\n const v = config.visibility?.threadLog;\n return {\n maxToolPreview: v?.maxToolPreview ?? DEFAULT_THREAD_LOG_OPTS.maxToolPreview,\n maxLogMessageChars: v?.maxLogMessageChars ?? DEFAULT_THREAD_LOG_OPTS.maxLogMessageChars,\n editDebounceMs: v?.editDebounceMs ?? DEFAULT_THREAD_LOG_OPTS.editDebounceMs,\n };\n}\n\nasync function resolveSpaceForChat(\n chatId: string,\n startDir: string\n): Promise<{ spaceName: string; threadsDisabled: boolean } | null> {\n const state = await readGoogleChatState(startDir);\n const entry = Object.entries(state.channelChatMap || {}).find(([, v]) => v?.chatId === chatId);\n if (!entry) return null;\n const [spaceName, mapping] = entry;\n return {\n spaceName,\n threadsDisabled: mapping.threadsDisabled === true,\n };\n}\n\nexport async function startDaemonToGoogleChatForwarder(\n trpc: ReturnType<typeof getTRPCClient>,\n config: GoogleChatConfig,\n filteringConfig: FilteringConfig,\n signal?: AbortSignal,\n deps: GoogleChatForwarderDeps = {}\n) {\n const defaultChatId = config.chatId || 'default';\n const startDir = deps.startDir ?? process.cwd();\n const threadLogOpts = resolveThreadLogOpts(config);\n const threadsGloballyEnabled = config.visibility?.threads !== false;\n const jobsMode: 'silent' | 'header' = config.visibility?.jobs ?? 'silent';\n\n const getChatApi = async (): Promise<GoogleChatApi> => {\n if (deps.chatApi) return deps.chatApi;\n const authClient = await getAuthClient();\n return google.chat({ version: 'v1', auth: authClient });\n };\n\n const activeSubscriptions = new Map<string, { unsubscribe: () => void }>();\n /**\n * When a turn has no inbound-user anchor (cron, subagent completion, any\n * proactive turn), its first top-level post implicitly creates a GChat\n * thread. Record `daemonMessageId -> gchatThreadName` here so a late\n * `turnStarted` event can still resolve the anchor. LRU-bounded to keep\n * memory predictable on long-running daemons.\n */\n const proactiveAnchors = new Map<string, string>();\n const MAX_PROACTIVE_ANCHORS = 64;\n const recordProactiveAnchor = (daemonMessageId: string, threadName: string) => {\n while (proactiveAnchors.size >= MAX_PROACTIVE_ANCHORS) {\n const oldest = proactiveAnchors.keys().next().value;\n if (!oldest) break;\n proactiveAnchors.delete(oldest);\n }\n proactiveAnchors.set(daemonMessageId, threadName);\n };\n let currentLastSyncedMessageIds =\n (await readGoogleChatState(startDir)).lastSyncedMessageIds || {};\n\n const saveLastMessageId = async (chatId: string, id: string) => {\n currentLastSyncedMessageIds = { ...currentLastSyncedMessageIds, [chatId]: id };\n return updateGoogleChatState(\n (state) => ({\n lastSyncedMessageIds: {\n ...state.lastSyncedMessageIds,\n ...currentLastSyncedMessageIds,\n },\n }),\n startDir\n );\n };\n\n /**\n * Post a top-level message (no `thread` field; GChat auto-creates a fresh\n * thread). Returns the newly-created thread's `name` so callers can anchor\n * subsequent threaded replies on it — used to thread activity under\n * proactive turns (cron, subagent_update) that have no inbound user\n * message to anchor on.\n */\n const postTopLevel = async (\n spaceName: string,\n text: string,\n cardsV2?: ReturnType<typeof buildPolicyCard>\n ): Promise<{ threadName: string | undefined }> => {\n const chatApi = await getChatApi();\n const extractThread = (res: unknown): string | undefined => {\n const data =\n (res as { data?: { thread?: { name?: string } } }).data ??\n (res as { thread?: { name?: string } });\n return data?.thread?.name ?? undefined;\n };\n if (cardsV2 && cardsV2.length > 0) {\n const res = await chatApi.spaces.messages.create({\n parent: spaceName,\n requestBody: { text: text || '', cardsV2 },\n });\n return { threadName: extractThread(res) };\n }\n if (text.length > 4000) {\n const chunks = chunkString(text, 4000);\n let firstThread: string | undefined;\n for (const chunk of chunks) {\n const res = await chatApi.spaces.messages.create({\n parent: spaceName,\n requestBody: { text: chunk },\n });\n firstThread ??= extractThread(res);\n }\n return { threadName: firstThread };\n }\n const res = await chatApi.spaces.messages.create({\n parent: spaceName,\n requestBody: { text },\n });\n return { threadName: extractThread(res) };\n };\n\n const postThreaded = async (anchor: GChatAnchor, text: string): Promise<string | undefined> => {\n const chatApi = await getChatApi();\n const res = await chatApi.spaces.messages.create({\n parent: anchor.spaceName,\n requestBody: {\n text: text || '',\n thread: { name: anchor.threadName },\n },\n messageReplyOption: 'REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD',\n });\n const data = (res as { data?: { name?: string } }).data ?? (res as { name?: string });\n return data?.name ?? undefined;\n };\n\n const editThreaded = async (\n _anchor: GChatAnchor,\n messageName: string,\n text: string\n ): Promise<void> => {\n const chatApi = await getChatApi();\n await chatApi.spaces.messages.update({\n name: messageName,\n updateMask: 'text',\n requestBody: { text },\n });\n };\n\n // GChat surfaces a missing message as HTTP 404 on update; treat that as the\n // signal to open a fresh log message rather than retrying the edit.\n const isMissingMessageError = (err: unknown): boolean => {\n const status = (err as { code?: number; status?: number })?.code ?? 0;\n return status === 404;\n };\n\n const turnLog: TurnLogBuffer<GChatAnchor> = createTurnLogBuffer<GChatAnchor>({\n postThreaded,\n editThreaded,\n isMissingMessageError,\n options: threadLogOpts,\n threadsEnabled: threadsGloballyEnabled,\n });\n\n const handlePolicyCard = async (\n message: Extract<ChatMessage, { role: 'policy' }>,\n spaceName: string\n ) => {\n const cards = buildPolicyCard(message);\n try {\n await postTopLevel(spaceName, '', cards);\n } catch (richError) {\n console.warn(\n 'Failed to send rich policy request to Google Chat, falling back to plain text:',\n richError\n );\n const policyId = ('requestId' in message && message.requestId) || message.id;\n const text = `Action Required: Policy Request\\n\\n${\n message.content || 'A pending policy request requires your attention.'\n }\\n\\nApprove: \\`/approve ${policyId}\\`\\nReject: \\`/reject ${policyId} <optional_rationale>\\``;\n await postTopLevel(spaceName, text);\n }\n };\n\n const collapseDestination = (dest: Destination, turnId?: string): Destination => {\n // Both the global `visibility.threads: false` kill switch and the\n // per-space `threadsDisabled` flag mean \"quiet bot\": drop thread-log\n // activity rather than promoting it top-level. Top-level spam is only\n // opt-in via `filters` (e.g. `/show`), matching pre-threaded behavior.\n if (dest.kind !== 'thread-log') return dest;\n if (!threadsGloballyEnabled) return { kind: 'drop' };\n if (turnId && turnLog.threadsDisabledFor(turnId)) return { kind: 'drop' };\n return dest;\n };\n\n const handleMessageForChat = async (chatId: string, message: ChatMessage) => {\n const routed = routeMessage(message, filteringConfig);\n\n // Cron SystemMessages route to `drop` by default (silent mode: the\n // activity log anchors on the agent's reply; nothing posts if it stays\n // silent). In `header` mode, promote back to top-level and swap the\n // prompt text for a terse `🕒 <jobId>` heartbeat.\n const isCronHeader =\n jobsMode === 'header' && message.role === 'system' && message.event === 'cron';\n\n const effective = collapseDestination(routed, message.turnId);\n\n if (!isCronHeader && effective.kind === 'drop') return;\n\n const space = await resolveSpaceForChat(chatId, startDir);\n if (!space) {\n console.warn('No active Google Chat space to reply to. Ignoring message:', message.content);\n return;\n }\n\n // Verbose-level legacy messages still drop silently.\n if ('level' in message && (message as { level?: string }).level === 'verbose') return;\n\n const hasContent = !!message.content?.trim();\n const files = 'files' in message ? ((message as { files?: string[] }).files ?? []) : [];\n const hasFiles = files.length > 0;\n\n if (!isCronHeader && effective.kind === 'thread-log') {\n if (!message.turnId) {\n console.warn(`thread-log event for ${message.role} has no turnId — dropping.`);\n return;\n }\n // No turn context: turnStarted may have been missed (adapter restart,\n // subscription reconnect). Drop silently rather than flooding the space.\n if (!turnLog.has(message.turnId)) return;\n turnLog.append(message.turnId, message);\n return;\n }\n\n // Top-level.\n if (!isCronHeader && !hasContent && !hasFiles && message.role !== 'policy') return;\n\n try {\n if (message.role === 'policy' && message.status === 'pending') {\n await handlePolicyCard(message, space.spaceName);\n return;\n }\n\n let text: string;\n if (isCronHeader) {\n const cron = message as Extract<ChatMessage, { role: 'system' }>;\n const label = cron.jobId ?? 'scheduled';\n text = `🕒 ${label}`;\n } else {\n text = formatMessage(message) || '';\n }\n\n if (hasFiles) {\n const fileNames = files.map((f: string) => path.basename(f)).join(', ');\n if (\n config.driveUploadEnabled !== false &&\n config.oauthClientId &&\n config.oauthClientSecret\n ) {\n text += `\\n\\n`;\n try {\n const uploadResults = await uploadFilesToDrive(files, config);\n for (const result of uploadResults) {\n text += `${result}\\n`;\n }\n } catch (driveAuthErr) {\n console.error('Drive API/Auth Failed, degrading to local files output:', driveAuthErr);\n text += `*(Files generated: ${fileNames})*`;\n }\n } else {\n text += `\\n\\n*(Files generated: ${fileNames})*`;\n }\n }\n\n const { threadName: createdThread } = await postTopLevel(space.spaceName, text);\n\n // If this post belongs to a turn that currently has no anchor, treat it\n // as the implicit root: subsequent thread-log events for the same turn\n // will be posted into the GChat thread that this top-level message just\n // created. Covers cron-triggered and other proactive turns that have no\n // inbound user message to anchor on.\n if (createdThread && message.turnId) {\n if (turnLog.has(message.turnId)) {\n if (!turnLog.isAnchored(message.turnId)) {\n turnLog.assignAnchor(message.turnId, {\n spaceName: space.spaceName,\n threadName: createdThread,\n });\n }\n } else {\n // turnStarted hasn't arrived yet — cache the mapping so it picks\n // up the anchor when it does.\n recordProactiveAnchor(message.id, createdThread);\n }\n }\n } catch (err) {\n console.error('Failed to send message to Google Chat:', err);\n }\n };\n\n const handleTurnStarted = async (\n chatId: string,\n turnId: string,\n rootMessageId: string,\n externalRef?: string\n ) => {\n const space = await resolveSpaceForChat(chatId, startDir);\n if (!space) {\n console.warn(`turnStarted for chat ${chatId} with no mapped space.`);\n return;\n }\n\n // The adapter sent `externalRef` as the GChat message.name of the inbound\n // that triggered this turn, so we look up the thread anchor directly\n // rather than guessing via FIFO pairing. Turns with no externalRef\n // (proactive crons, CLI messages) fall back to the thread created by\n // their first top-level post — recorded in `proactiveAnchors` — so\n // subsequent activity can still anchor into the right thread.\n const entry = externalRef ? resolveInbound(externalRef) : null;\n const proactiveThread = entry ? undefined : proactiveAnchors.get(rootMessageId);\n if (proactiveThread) proactiveAnchors.delete(rootMessageId);\n\n const threadName = entry?.gchatThreadName ?? proactiveThread;\n turnLog.start({\n turnId,\n threadsDisabled: space.threadsDisabled,\n anchorThread: threadName ? { spaceName: space.spaceName, threadName } : undefined,\n });\n };\n\n const handleTurnEnded = async (turnId: string) => {\n await turnLog.end(turnId);\n };\n\n const startSubscriptionForChat = async (chatId: string) => {\n if (activeSubscriptions.has(chatId)) return;\n if (signal?.aborted) return;\n\n let lastMessageId = currentLastSyncedMessageIds[chatId];\n\n if (!lastMessageId) {\n try {\n const messages = await trpc.getMessages.query({ chatId, limit: 1 });\n if (Array.isArray(messages) && messages.length > 0) {\n const lastMsg = messages[messages.length - 1];\n if (lastMsg) {\n await saveLastMessageId(chatId, lastMsg.id);\n lastMessageId = lastMsg.id;\n }\n }\n } catch (error) {\n if (signal?.aborted) return;\n console.error(`Failed to fetch initial messages from daemon for ${chatId}:`, error);\n }\n }\n\n console.log(\n `Starting daemon-to-google-chat forwarder for chat ${chatId}, lastMessageId: ${lastMessageId}`\n );\n\n let retryDelay = 1000;\n const maxRetryDelay = 30000;\n\n let subscription: { unsubscribe: () => void } | null = null;\n let pending = Promise.resolve();\n\n type StreamItem =\n | { kind: 'message'; message: ChatMessage }\n | {\n kind: 'turn';\n event:\n | { type: 'started'; turnId: string; rootMessageId: string; externalRef?: string }\n | { type: 'ended'; turnId: string; outcome: 'ok' | 'error' };\n };\n\n const connect = () => {\n if (signal?.aborted || !activeSubscriptions.has(chatId)) return;\n\n subscription = trpc.waitForMessages.subscribe(\n { chatId, lastMessageId },\n {\n onData: (items) => {\n retryDelay = 1000;\n if (!Array.isArray(items) || items.length === 0) return;\n\n pending = pending\n .then(async () => {\n for (const raw of items) {\n if (signal?.aborted || !activeSubscriptions.has(chatId)) break;\n const item = raw as StreamItem;\n if (item.kind === 'message') {\n await handleMessageForChat(chatId, item.message);\n await saveLastMessageId(chatId, item.message.id).catch(console.error);\n lastMessageId = item.message.id;\n } else if (item.event.type === 'started') {\n await handleTurnStarted(\n chatId,\n item.event.turnId,\n item.event.rootMessageId,\n item.event.externalRef\n );\n } else {\n await handleTurnEnded(item.event.turnId);\n }\n }\n })\n .catch((error) => {\n console.error('Stream queue failed, forcing reconnect...', error);\n subscription?.unsubscribe();\n subscription = null;\n if (signal?.aborted || !activeSubscriptions.has(chatId)) return;\n setTimeout(() => {\n retryDelay = Math.min(retryDelay * 2, maxRetryDelay);\n connect();\n }, retryDelay);\n });\n },\n onError: (error) => {\n console.error(\n `Error in daemon-to-google-chat forwarder subscription for ${chatId}. Retrying in ${retryDelay}ms.`,\n error\n );\n subscription?.unsubscribe();\n subscription = null;\n if (signal?.aborted || !activeSubscriptions.has(chatId)) return;\n setTimeout(() => {\n retryDelay = Math.min(retryDelay * 2, maxRetryDelay);\n connect();\n }, retryDelay);\n },\n onComplete: () => {\n subscription = null;\n if (!signal?.aborted && activeSubscriptions.has(chatId)) {\n setTimeout(() => connect(), retryDelay);\n }\n },\n }\n );\n };\n\n activeSubscriptions.set(chatId, {\n unsubscribe: () => {\n subscription?.unsubscribe();\n },\n });\n\n connect();\n };\n\n const syncSubscriptions = async () => {\n if (signal?.aborted) return;\n const state = await readGoogleChatState(startDir);\n\n if (state.lastSyncedMessageIds) {\n currentLastSyncedMessageIds = {\n ...state.lastSyncedMessageIds,\n ...currentLastSyncedMessageIds,\n };\n }\n\n const targetChatIds = new Set<string>();\n targetChatIds.add(defaultChatId);\n\n if (state.channelChatMap) {\n for (const mappedEntry of Object.values(state.channelChatMap)) {\n if (mappedEntry.chatId) targetChatIds.add(mappedEntry.chatId);\n }\n }\n\n for (const targetChatId of targetChatIds) {\n if (!activeSubscriptions.has(targetChatId)) {\n startSubscriptionForChat(targetChatId);\n }\n }\n\n for (const [activeChatId, sub] of activeSubscriptions.entries()) {\n if (!targetChatIds.has(activeChatId)) {\n sub.unsubscribe();\n activeSubscriptions.delete(activeChatId);\n }\n }\n };\n\n return new Promise<void>((resolve) => {\n syncSubscriptions().catch(console.error);\n\n const statePath = getGoogleChatStatePath(startDir);\n const stateDir = path.dirname(statePath);\n if (!fs.existsSync(stateDir)) {\n fs.mkdirSync(stateDir, { recursive: true });\n }\n let debounceTimer: NodeJS.Timeout | null = null;\n const watcher = fs.watch(stateDir, (_eventType, filename) => {\n if (filename === path.basename(statePath)) {\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n syncSubscriptions().catch(console.error);\n }, 200);\n }\n });\n\n signal?.addEventListener('abort', () => {\n if (debounceTimer) clearTimeout(debounceTimer);\n watcher.close();\n for (const sub of activeSubscriptions.values()) sub.unsubscribe();\n turnLog.shutdown();\n resolve();\n });\n });\n}\n","import { readGoogleChatState, updateGoogleChatState } from './state.js';\nimport { getUserAuthClient } from './auth.js';\nimport type { GoogleChatConfig } from './config.js';\n\nexport function startSubscriptionRenewalCron(config: GoogleChatConfig): NodeJS.Timeout {\n // Run every hour\n return setInterval(\n async () => {\n try {\n await renewExpiringSubscriptions(config);\n } catch (err) {\n console.error('Error in subscription renewal cron:', err);\n }\n },\n 1000 * 60 * 60\n );\n}\n\nexport async function renewExpiringSubscriptions(config: GoogleChatConfig): Promise<void> {\n const state = await readGoogleChatState();\n if (!state.channelChatMap) return;\n\n const now = Date.now();\n const FORTY_EIGHT_HOURS_MS = 48 * 60 * 60 * 1000;\n\n for (const [externalContextId, entry] of Object.entries(state.channelChatMap)) {\n if (entry.subscriptionId && entry.expirationDate) {\n const expirationTime = new Date(entry.expirationDate).getTime();\n const timeUntilExpiration = expirationTime - now;\n\n if (timeUntilExpiration < FORTY_EIGHT_HOURS_MS) {\n console.log(\n `Renewing expiring subscription ${entry.subscriptionId} for space ${externalContextId}`\n );\n try {\n const userAuthClient = await getUserAuthClient(config);\n const tokenResponse = await userAuthClient.getAccessToken();\n const token = tokenResponse.token;\n\n if (token) {\n const res = await fetch(\n `https://workspaceevents.googleapis.com/v1/${entry.subscriptionId}?updateMask=ttl`,\n {\n method: 'PATCH',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n ttl: '604800s', // 7 days\n }),\n }\n );\n\n if (res.ok) {\n const subData = (await res.json()) as { expireTime: string };\n await updateGoogleChatState((latestState) => {\n const currentMap = latestState.channelChatMap || {};\n return {\n channelChatMap: {\n ...currentMap,\n [externalContextId]: {\n ...(currentMap[externalContextId] || {}),\n expirationDate: subData.expireTime,\n },\n },\n };\n });\n console.log(`Successfully renewed subscription ${entry.subscriptionId}`);\n } else {\n const errText = await res.text();\n console.error(`Failed to renew subscription ${entry.subscriptionId}:`, errText);\n }\n }\n } catch (err) {\n console.error(`Error renewing subscription ${entry.subscriptionId}:`, err);\n }\n }\n }\n }\n}\n","#!/usr/bin/env node\n\nimport { initGoogleChatConfig, readGoogleChatConfig } from './config.js';\nimport { readGoogleChatState } from './state.js';\nimport { getTRPCClient, startGoogleChatIngestion } from './client.js';\nimport { startDaemonToGoogleChatForwarder } from './forwarder.js';\nimport { getUserAuthClient } from './auth.js';\nimport { startSubscriptionRenewalCron } from './cron.js';\nimport type { FilteringConfig } from '../shared/adapters/filtering.js';\n\nexport async function main() {\n const args = process.argv.slice(2);\n\n if (args[0] === 'init') {\n await initGoogleChatConfig();\n return;\n }\n\n console.log('Google Chat Adapter starting...');\n\n const config = await readGoogleChatConfig();\n if (!config) {\n console.error(\n 'Failed to load Google Chat configuration. Please ensure .clawmini/adapters/google-chat/config.json exists and is valid.'\n );\n process.exit(1);\n }\n\n if (config.oauthClientId && config.oauthClientSecret) {\n try {\n console.log('Initializing Google User Authentication...');\n await getUserAuthClient(config);\n } catch (err) {\n console.error('Failed to initialize Google User authentication:', err);\n process.exit(1);\n }\n }\n\n const trpc = getTRPCClient();\n const state = await readGoogleChatState();\n const filteringConfig: FilteringConfig = { filters: state.filters };\n\n // Start ingestion from Pub/Sub\n startGoogleChatIngestion(config, trpc, filteringConfig);\n console.log(`Listening to Pub/Sub subscription: ${config.subscriptionName}`);\n\n // Start forwarding from daemon to Google Chat API\n startDaemonToGoogleChatForwarder(trpc, config, filteringConfig).catch((error) => {\n console.error('Error in daemon-to-google-chat forwarder:', error);\n });\n\n // Start background cron for renewing Space Subscriptions\n startSubscriptionRenewalCron(config);\n}\n\nif (process.env.NODE_ENV !== 'test') {\n main().catch((error) => {\n console.error('Unhandled error in Google Chat Adapter:', error);\n process.exit(1);\n });\n}\n"],"mappings":"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"}
|