cowork-os 0.3.21

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.
Files changed (526) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1638 -0
  3. package/bin/cowork.js +42 -0
  4. package/build/entitlements.mac.plist +16 -0
  5. package/build/icon.icns +0 -0
  6. package/build/icon.png +0 -0
  7. package/dist/electron/electron/activity/ActivityRepository.js +190 -0
  8. package/dist/electron/electron/agent/browser/browser-service.js +639 -0
  9. package/dist/electron/electron/agent/context-manager.js +225 -0
  10. package/dist/electron/electron/agent/custom-skill-loader.js +566 -0
  11. package/dist/electron/electron/agent/daemon.js +975 -0
  12. package/dist/electron/electron/agent/executor.js +3561 -0
  13. package/dist/electron/electron/agent/llm/anthropic-provider.js +155 -0
  14. package/dist/electron/electron/agent/llm/bedrock-provider.js +202 -0
  15. package/dist/electron/electron/agent/llm/gemini-provider.js +375 -0
  16. package/dist/electron/electron/agent/llm/index.js +34 -0
  17. package/dist/electron/electron/agent/llm/ollama-provider.js +263 -0
  18. package/dist/electron/electron/agent/llm/openai-oauth.js +101 -0
  19. package/dist/electron/electron/agent/llm/openai-provider.js +657 -0
  20. package/dist/electron/electron/agent/llm/openrouter-provider.js +232 -0
  21. package/dist/electron/electron/agent/llm/pricing.js +160 -0
  22. package/dist/electron/electron/agent/llm/provider-factory.js +880 -0
  23. package/dist/electron/electron/agent/llm/types.js +178 -0
  24. package/dist/electron/electron/agent/queue-manager.js +378 -0
  25. package/dist/electron/electron/agent/sandbox/docker-sandbox.js +402 -0
  26. package/dist/electron/electron/agent/sandbox/macos-sandbox.js +407 -0
  27. package/dist/electron/electron/agent/sandbox/runner.js +410 -0
  28. package/dist/electron/electron/agent/sandbox/sandbox-factory.js +228 -0
  29. package/dist/electron/electron/agent/sandbox/security-utils.js +258 -0
  30. package/dist/electron/electron/agent/search/brave-provider.js +119 -0
  31. package/dist/electron/electron/agent/search/google-provider.js +100 -0
  32. package/dist/electron/electron/agent/search/index.js +28 -0
  33. package/dist/electron/electron/agent/search/provider-factory.js +395 -0
  34. package/dist/electron/electron/agent/search/serpapi-provider.js +112 -0
  35. package/dist/electron/electron/agent/search/tavily-provider.js +90 -0
  36. package/dist/electron/electron/agent/search/types.js +40 -0
  37. package/dist/electron/electron/agent/security/index.js +12 -0
  38. package/dist/electron/electron/agent/security/input-sanitizer.js +303 -0
  39. package/dist/electron/electron/agent/security/output-filter.js +217 -0
  40. package/dist/electron/electron/agent/skill-eligibility.js +281 -0
  41. package/dist/electron/electron/agent/skill-registry.js +396 -0
  42. package/dist/electron/electron/agent/skills/document.js +878 -0
  43. package/dist/electron/electron/agent/skills/image-generator.js +225 -0
  44. package/dist/electron/electron/agent/skills/organizer.js +141 -0
  45. package/dist/electron/electron/agent/skills/presentation.js +367 -0
  46. package/dist/electron/electron/agent/skills/spreadsheet.js +165 -0
  47. package/dist/electron/electron/agent/tools/browser-tools.js +523 -0
  48. package/dist/electron/electron/agent/tools/builtin-settings.js +384 -0
  49. package/dist/electron/electron/agent/tools/canvas-tools.js +530 -0
  50. package/dist/electron/electron/agent/tools/cron-tools.js +577 -0
  51. package/dist/electron/electron/agent/tools/edit-tools.js +194 -0
  52. package/dist/electron/electron/agent/tools/file-tools.js +719 -0
  53. package/dist/electron/electron/agent/tools/glob-tools.js +283 -0
  54. package/dist/electron/electron/agent/tools/grep-tools.js +387 -0
  55. package/dist/electron/electron/agent/tools/image-tools.js +111 -0
  56. package/dist/electron/electron/agent/tools/mention-tools.js +282 -0
  57. package/dist/electron/electron/agent/tools/node-tools.js +476 -0
  58. package/dist/electron/electron/agent/tools/registry.js +2719 -0
  59. package/dist/electron/electron/agent/tools/search-tools.js +91 -0
  60. package/dist/electron/electron/agent/tools/shell-tools.js +574 -0
  61. package/dist/electron/electron/agent/tools/skill-tools.js +274 -0
  62. package/dist/electron/electron/agent/tools/system-tools.js +578 -0
  63. package/dist/electron/electron/agent/tools/web-fetch-tools.js +444 -0
  64. package/dist/electron/electron/agent/tools/x-tools.js +264 -0
  65. package/dist/electron/electron/agents/AgentRoleRepository.js +420 -0
  66. package/dist/electron/electron/agents/HeartbeatService.js +356 -0
  67. package/dist/electron/electron/agents/MentionRepository.js +197 -0
  68. package/dist/electron/electron/agents/TaskSubscriptionRepository.js +168 -0
  69. package/dist/electron/electron/agents/WorkingStateRepository.js +229 -0
  70. package/dist/electron/electron/canvas/canvas-manager.js +714 -0
  71. package/dist/electron/electron/canvas/canvas-preload.js +53 -0
  72. package/dist/electron/electron/canvas/canvas-protocol.js +195 -0
  73. package/dist/electron/electron/canvas/canvas-store.js +174 -0
  74. package/dist/electron/electron/canvas/index.js +13 -0
  75. package/dist/electron/electron/control-plane/client.js +364 -0
  76. package/dist/electron/electron/control-plane/handlers.js +572 -0
  77. package/dist/electron/electron/control-plane/index.js +41 -0
  78. package/dist/electron/electron/control-plane/node-manager.js +264 -0
  79. package/dist/electron/electron/control-plane/protocol.js +194 -0
  80. package/dist/electron/electron/control-plane/remote-client.js +437 -0
  81. package/dist/electron/electron/control-plane/server.js +640 -0
  82. package/dist/electron/electron/control-plane/settings.js +369 -0
  83. package/dist/electron/electron/control-plane/ssh-tunnel.js +549 -0
  84. package/dist/electron/electron/cron/index.js +30 -0
  85. package/dist/electron/electron/cron/schedule.js +190 -0
  86. package/dist/electron/electron/cron/service.js +614 -0
  87. package/dist/electron/electron/cron/store.js +155 -0
  88. package/dist/electron/electron/cron/types.js +82 -0
  89. package/dist/electron/electron/cron/webhook.js +258 -0
  90. package/dist/electron/electron/database/SecureSettingsRepository.js +444 -0
  91. package/dist/electron/electron/database/TaskLabelRepository.js +120 -0
  92. package/dist/electron/electron/database/repositories.js +1781 -0
  93. package/dist/electron/electron/database/schema.js +978 -0
  94. package/dist/electron/electron/extensions/index.js +33 -0
  95. package/dist/electron/electron/extensions/loader.js +313 -0
  96. package/dist/electron/electron/extensions/registry.js +485 -0
  97. package/dist/electron/electron/extensions/types.js +11 -0
  98. package/dist/electron/electron/gateway/channel-registry.js +1102 -0
  99. package/dist/electron/electron/gateway/channels/bluebubbles-client.js +479 -0
  100. package/dist/electron/electron/gateway/channels/bluebubbles.js +432 -0
  101. package/dist/electron/electron/gateway/channels/discord.js +975 -0
  102. package/dist/electron/electron/gateway/channels/email-client.js +593 -0
  103. package/dist/electron/electron/gateway/channels/email.js +443 -0
  104. package/dist/electron/electron/gateway/channels/google-chat.js +631 -0
  105. package/dist/electron/electron/gateway/channels/imessage-client.js +363 -0
  106. package/dist/electron/electron/gateway/channels/imessage.js +465 -0
  107. package/dist/electron/electron/gateway/channels/index.js +36 -0
  108. package/dist/electron/electron/gateway/channels/line-client.js +470 -0
  109. package/dist/electron/electron/gateway/channels/line.js +479 -0
  110. package/dist/electron/electron/gateway/channels/matrix-client.js +432 -0
  111. package/dist/electron/electron/gateway/channels/matrix.js +592 -0
  112. package/dist/electron/electron/gateway/channels/mattermost-client.js +394 -0
  113. package/dist/electron/electron/gateway/channels/mattermost.js +496 -0
  114. package/dist/electron/electron/gateway/channels/signal-client.js +500 -0
  115. package/dist/electron/electron/gateway/channels/signal.js +582 -0
  116. package/dist/electron/electron/gateway/channels/slack.js +415 -0
  117. package/dist/electron/electron/gateway/channels/teams.js +596 -0
  118. package/dist/electron/electron/gateway/channels/telegram.js +1390 -0
  119. package/dist/electron/electron/gateway/channels/twitch-client.js +502 -0
  120. package/dist/electron/electron/gateway/channels/twitch.js +396 -0
  121. package/dist/electron/electron/gateway/channels/types.js +8 -0
  122. package/dist/electron/electron/gateway/channels/whatsapp.js +953 -0
  123. package/dist/electron/electron/gateway/context-policy.js +268 -0
  124. package/dist/electron/electron/gateway/index.js +1063 -0
  125. package/dist/electron/electron/gateway/infrastructure.js +496 -0
  126. package/dist/electron/electron/gateway/router.js +2700 -0
  127. package/dist/electron/electron/gateway/security.js +375 -0
  128. package/dist/electron/electron/gateway/session.js +115 -0
  129. package/dist/electron/electron/gateway/tunnel.js +503 -0
  130. package/dist/electron/electron/guardrails/guardrail-manager.js +348 -0
  131. package/dist/electron/electron/hooks/gmail-watcher.js +300 -0
  132. package/dist/electron/electron/hooks/index.js +46 -0
  133. package/dist/electron/electron/hooks/mappings.js +381 -0
  134. package/dist/electron/electron/hooks/server.js +480 -0
  135. package/dist/electron/electron/hooks/settings.js +447 -0
  136. package/dist/electron/electron/hooks/types.js +41 -0
  137. package/dist/electron/electron/ipc/canvas-handlers.js +158 -0
  138. package/dist/electron/electron/ipc/handlers.js +3138 -0
  139. package/dist/electron/electron/ipc/mission-control-handlers.js +141 -0
  140. package/dist/electron/electron/main.js +448 -0
  141. package/dist/electron/electron/mcp/client/MCPClientManager.js +330 -0
  142. package/dist/electron/electron/mcp/client/MCPServerConnection.js +437 -0
  143. package/dist/electron/electron/mcp/client/transports/SSETransport.js +304 -0
  144. package/dist/electron/electron/mcp/client/transports/StdioTransport.js +307 -0
  145. package/dist/electron/electron/mcp/client/transports/WebSocketTransport.js +329 -0
  146. package/dist/electron/electron/mcp/host/MCPHostServer.js +354 -0
  147. package/dist/electron/electron/mcp/host/ToolAdapter.js +100 -0
  148. package/dist/electron/electron/mcp/registry/MCPRegistryManager.js +497 -0
  149. package/dist/electron/electron/mcp/settings.js +446 -0
  150. package/dist/electron/electron/mcp/types.js +59 -0
  151. package/dist/electron/electron/memory/MemoryService.js +435 -0
  152. package/dist/electron/electron/notifications/index.js +17 -0
  153. package/dist/electron/electron/notifications/service.js +118 -0
  154. package/dist/electron/electron/notifications/store.js +144 -0
  155. package/dist/electron/electron/preload.js +842 -0
  156. package/dist/electron/electron/reports/StandupReportService.js +272 -0
  157. package/dist/electron/electron/security/concurrency.js +293 -0
  158. package/dist/electron/electron/security/index.js +15 -0
  159. package/dist/electron/electron/security/policy-manager.js +435 -0
  160. package/dist/electron/electron/settings/appearance-manager.js +193 -0
  161. package/dist/electron/electron/settings/personality-manager.js +724 -0
  162. package/dist/electron/electron/settings/x-manager.js +58 -0
  163. package/dist/electron/electron/tailscale/exposure.js +188 -0
  164. package/dist/electron/electron/tailscale/index.js +28 -0
  165. package/dist/electron/electron/tailscale/settings.js +205 -0
  166. package/dist/electron/electron/tailscale/tailscale.js +355 -0
  167. package/dist/electron/electron/tray/QuickInputWindow.js +568 -0
  168. package/dist/electron/electron/tray/TrayManager.js +895 -0
  169. package/dist/electron/electron/tray/index.js +9 -0
  170. package/dist/electron/electron/updater/index.js +6 -0
  171. package/dist/electron/electron/updater/update-manager.js +418 -0
  172. package/dist/electron/electron/utils/env-migration.js +209 -0
  173. package/dist/electron/electron/utils/process.js +102 -0
  174. package/dist/electron/electron/utils/rate-limiter.js +104 -0
  175. package/dist/electron/electron/utils/validation.js +419 -0
  176. package/dist/electron/electron/utils/x-cli.js +177 -0
  177. package/dist/electron/electron/voice/VoiceService.js +507 -0
  178. package/dist/electron/electron/voice/index.js +14 -0
  179. package/dist/electron/electron/voice/voice-settings-manager.js +359 -0
  180. package/dist/electron/shared/channelMessages.js +170 -0
  181. package/dist/electron/shared/types.js +1185 -0
  182. package/package.json +159 -0
  183. package/resources/skills/1password.json +10 -0
  184. package/resources/skills/add-documentation.json +31 -0
  185. package/resources/skills/analyze-csv.json +17 -0
  186. package/resources/skills/apple-notes.json +10 -0
  187. package/resources/skills/apple-reminders.json +10 -0
  188. package/resources/skills/auto-commenter.json +10 -0
  189. package/resources/skills/bear-notes.json +10 -0
  190. package/resources/skills/bird.json +35 -0
  191. package/resources/skills/blogwatcher.json +10 -0
  192. package/resources/skills/blucli.json +10 -0
  193. package/resources/skills/bluebubbles.json +10 -0
  194. package/resources/skills/camsnap.json +10 -0
  195. package/resources/skills/clean-imports.json +18 -0
  196. package/resources/skills/code-review.json +18 -0
  197. package/resources/skills/coding-agent.json +10 -0
  198. package/resources/skills/compare-files.json +23 -0
  199. package/resources/skills/convert-code.json +34 -0
  200. package/resources/skills/create-changelog.json +24 -0
  201. package/resources/skills/debug-error.json +17 -0
  202. package/resources/skills/dependency-check.json +10 -0
  203. package/resources/skills/discord.json +10 -0
  204. package/resources/skills/eightctl.json +10 -0
  205. package/resources/skills/explain-code.json +29 -0
  206. package/resources/skills/extract-todos.json +18 -0
  207. package/resources/skills/food-order.json +10 -0
  208. package/resources/skills/gemini.json +10 -0
  209. package/resources/skills/generate-readme.json +10 -0
  210. package/resources/skills/gifgrep.json +10 -0
  211. package/resources/skills/git-commit.json +10 -0
  212. package/resources/skills/github.json +10 -0
  213. package/resources/skills/gog.json +10 -0
  214. package/resources/skills/goplaces.json +10 -0
  215. package/resources/skills/himalaya.json +10 -0
  216. package/resources/skills/imsg.json +10 -0
  217. package/resources/skills/karpathy-guidelines.json +12 -0
  218. package/resources/skills/last30days.json +26 -0
  219. package/resources/skills/local-places.json +10 -0
  220. package/resources/skills/mcporter.json +10 -0
  221. package/resources/skills/model-usage.json +10 -0
  222. package/resources/skills/nano-banana-pro.json +10 -0
  223. package/resources/skills/nano-pdf.json +10 -0
  224. package/resources/skills/notion.json +10 -0
  225. package/resources/skills/obsidian.json +10 -0
  226. package/resources/skills/openai-image-gen.json +10 -0
  227. package/resources/skills/openai-whisper-api.json +10 -0
  228. package/resources/skills/openai-whisper.json +10 -0
  229. package/resources/skills/openhue.json +10 -0
  230. package/resources/skills/oracle.json +10 -0
  231. package/resources/skills/ordercli.json +10 -0
  232. package/resources/skills/peekaboo.json +10 -0
  233. package/resources/skills/project-structure.json +10 -0
  234. package/resources/skills/proofread.json +17 -0
  235. package/resources/skills/refactor-code.json +31 -0
  236. package/resources/skills/rename-symbol.json +23 -0
  237. package/resources/skills/sag.json +10 -0
  238. package/resources/skills/security-audit.json +18 -0
  239. package/resources/skills/session-logs.json +10 -0
  240. package/resources/skills/sherpa-onnx-tts.json +10 -0
  241. package/resources/skills/skill-creator.json +15 -0
  242. package/resources/skills/skill-hub.json +29 -0
  243. package/resources/skills/slack.json +10 -0
  244. package/resources/skills/songsee.json +10 -0
  245. package/resources/skills/sonoscli.json +10 -0
  246. package/resources/skills/spotify-player.json +10 -0
  247. package/resources/skills/startup-cfo.json +55 -0
  248. package/resources/skills/summarize-folder.json +18 -0
  249. package/resources/skills/summarize.json +10 -0
  250. package/resources/skills/things-mac.json +10 -0
  251. package/resources/skills/tmux.json +10 -0
  252. package/resources/skills/translate.json +36 -0
  253. package/resources/skills/trello.json +10 -0
  254. package/resources/skills/video-frames.json +10 -0
  255. package/resources/skills/voice-call.json +10 -0
  256. package/resources/skills/wacli.json +10 -0
  257. package/resources/skills/weather.json +10 -0
  258. package/resources/skills/write-tests.json +31 -0
  259. package/src/electron/activity/ActivityRepository.ts +238 -0
  260. package/src/electron/agent/browser/browser-service.ts +721 -0
  261. package/src/electron/agent/context-manager.ts +257 -0
  262. package/src/electron/agent/custom-skill-loader.ts +634 -0
  263. package/src/electron/agent/daemon.ts +1097 -0
  264. package/src/electron/agent/executor.ts +4017 -0
  265. package/src/electron/agent/llm/anthropic-provider.ts +175 -0
  266. package/src/electron/agent/llm/bedrock-provider.ts +236 -0
  267. package/src/electron/agent/llm/gemini-provider.ts +422 -0
  268. package/src/electron/agent/llm/index.ts +9 -0
  269. package/src/electron/agent/llm/ollama-provider.ts +347 -0
  270. package/src/electron/agent/llm/openai-oauth.ts +127 -0
  271. package/src/electron/agent/llm/openai-provider.ts +686 -0
  272. package/src/electron/agent/llm/openrouter-provider.ts +273 -0
  273. package/src/electron/agent/llm/pricing.ts +180 -0
  274. package/src/electron/agent/llm/provider-factory.ts +971 -0
  275. package/src/electron/agent/llm/types.ts +291 -0
  276. package/src/electron/agent/queue-manager.ts +408 -0
  277. package/src/electron/agent/sandbox/docker-sandbox.ts +453 -0
  278. package/src/electron/agent/sandbox/macos-sandbox.ts +426 -0
  279. package/src/electron/agent/sandbox/runner.ts +453 -0
  280. package/src/electron/agent/sandbox/sandbox-factory.ts +337 -0
  281. package/src/electron/agent/sandbox/security-utils.ts +251 -0
  282. package/src/electron/agent/search/brave-provider.ts +141 -0
  283. package/src/electron/agent/search/google-provider.ts +131 -0
  284. package/src/electron/agent/search/index.ts +6 -0
  285. package/src/electron/agent/search/provider-factory.ts +450 -0
  286. package/src/electron/agent/search/serpapi-provider.ts +138 -0
  287. package/src/electron/agent/search/tavily-provider.ts +108 -0
  288. package/src/electron/agent/search/types.ts +118 -0
  289. package/src/electron/agent/security/index.ts +20 -0
  290. package/src/electron/agent/security/input-sanitizer.ts +380 -0
  291. package/src/electron/agent/security/output-filter.ts +259 -0
  292. package/src/electron/agent/skill-eligibility.ts +334 -0
  293. package/src/electron/agent/skill-registry.ts +457 -0
  294. package/src/electron/agent/skills/document.ts +1070 -0
  295. package/src/electron/agent/skills/image-generator.ts +272 -0
  296. package/src/electron/agent/skills/organizer.ts +131 -0
  297. package/src/electron/agent/skills/presentation.ts +418 -0
  298. package/src/electron/agent/skills/spreadsheet.ts +166 -0
  299. package/src/electron/agent/tools/browser-tools.ts +546 -0
  300. package/src/electron/agent/tools/builtin-settings.ts +422 -0
  301. package/src/electron/agent/tools/canvas-tools.ts +572 -0
  302. package/src/electron/agent/tools/cron-tools.ts +723 -0
  303. package/src/electron/agent/tools/edit-tools.ts +196 -0
  304. package/src/electron/agent/tools/file-tools.ts +811 -0
  305. package/src/electron/agent/tools/glob-tools.ts +303 -0
  306. package/src/electron/agent/tools/grep-tools.ts +432 -0
  307. package/src/electron/agent/tools/image-tools.ts +126 -0
  308. package/src/electron/agent/tools/mention-tools.ts +371 -0
  309. package/src/electron/agent/tools/node-tools.ts +550 -0
  310. package/src/electron/agent/tools/registry.ts +3052 -0
  311. package/src/electron/agent/tools/search-tools.ts +111 -0
  312. package/src/electron/agent/tools/shell-tools.ts +651 -0
  313. package/src/electron/agent/tools/skill-tools.ts +340 -0
  314. package/src/electron/agent/tools/system-tools.ts +665 -0
  315. package/src/electron/agent/tools/web-fetch-tools.ts +528 -0
  316. package/src/electron/agent/tools/x-tools.ts +267 -0
  317. package/src/electron/agents/AgentRoleRepository.ts +557 -0
  318. package/src/electron/agents/HeartbeatService.ts +469 -0
  319. package/src/electron/agents/MentionRepository.ts +242 -0
  320. package/src/electron/agents/TaskSubscriptionRepository.ts +231 -0
  321. package/src/electron/agents/WorkingStateRepository.ts +278 -0
  322. package/src/electron/canvas/canvas-manager.ts +818 -0
  323. package/src/electron/canvas/canvas-preload.ts +102 -0
  324. package/src/electron/canvas/canvas-protocol.ts +174 -0
  325. package/src/electron/canvas/canvas-store.ts +200 -0
  326. package/src/electron/canvas/index.ts +8 -0
  327. package/src/electron/control-plane/client.ts +527 -0
  328. package/src/electron/control-plane/handlers.ts +723 -0
  329. package/src/electron/control-plane/index.ts +51 -0
  330. package/src/electron/control-plane/node-manager.ts +322 -0
  331. package/src/electron/control-plane/protocol.ts +269 -0
  332. package/src/electron/control-plane/remote-client.ts +517 -0
  333. package/src/electron/control-plane/server.ts +853 -0
  334. package/src/electron/control-plane/settings.ts +401 -0
  335. package/src/electron/control-plane/ssh-tunnel.ts +624 -0
  336. package/src/electron/cron/index.ts +9 -0
  337. package/src/electron/cron/schedule.ts +217 -0
  338. package/src/electron/cron/service.ts +743 -0
  339. package/src/electron/cron/store.ts +165 -0
  340. package/src/electron/cron/types.ts +291 -0
  341. package/src/electron/cron/webhook.ts +303 -0
  342. package/src/electron/database/SecureSettingsRepository.ts +514 -0
  343. package/src/electron/database/TaskLabelRepository.ts +148 -0
  344. package/src/electron/database/repositories.ts +2397 -0
  345. package/src/electron/database/schema.ts +1017 -0
  346. package/src/electron/extensions/index.ts +18 -0
  347. package/src/electron/extensions/loader.ts +336 -0
  348. package/src/electron/extensions/registry.ts +546 -0
  349. package/src/electron/extensions/types.ts +372 -0
  350. package/src/electron/gateway/channel-registry.ts +1267 -0
  351. package/src/electron/gateway/channels/bluebubbles-client.ts +641 -0
  352. package/src/electron/gateway/channels/bluebubbles.ts +509 -0
  353. package/src/electron/gateway/channels/discord.ts +1150 -0
  354. package/src/electron/gateway/channels/email-client.ts +708 -0
  355. package/src/electron/gateway/channels/email.ts +516 -0
  356. package/src/electron/gateway/channels/google-chat.ts +760 -0
  357. package/src/electron/gateway/channels/imessage-client.ts +473 -0
  358. package/src/electron/gateway/channels/imessage.ts +520 -0
  359. package/src/electron/gateway/channels/index.ts +21 -0
  360. package/src/electron/gateway/channels/line-client.ts +598 -0
  361. package/src/electron/gateway/channels/line.ts +559 -0
  362. package/src/electron/gateway/channels/matrix-client.ts +632 -0
  363. package/src/electron/gateway/channels/matrix.ts +655 -0
  364. package/src/electron/gateway/channels/mattermost-client.ts +526 -0
  365. package/src/electron/gateway/channels/mattermost.ts +550 -0
  366. package/src/electron/gateway/channels/signal-client.ts +722 -0
  367. package/src/electron/gateway/channels/signal.ts +666 -0
  368. package/src/electron/gateway/channels/slack.ts +458 -0
  369. package/src/electron/gateway/channels/teams.ts +681 -0
  370. package/src/electron/gateway/channels/telegram.ts +1727 -0
  371. package/src/electron/gateway/channels/twitch-client.ts +665 -0
  372. package/src/electron/gateway/channels/twitch.ts +468 -0
  373. package/src/electron/gateway/channels/types.ts +1002 -0
  374. package/src/electron/gateway/channels/whatsapp.ts +1101 -0
  375. package/src/electron/gateway/context-policy.ts +382 -0
  376. package/src/electron/gateway/index.ts +1274 -0
  377. package/src/electron/gateway/infrastructure.ts +645 -0
  378. package/src/electron/gateway/router.ts +3206 -0
  379. package/src/electron/gateway/security.ts +422 -0
  380. package/src/electron/gateway/session.ts +144 -0
  381. package/src/electron/gateway/tunnel.ts +626 -0
  382. package/src/electron/guardrails/guardrail-manager.ts +380 -0
  383. package/src/electron/hooks/gmail-watcher.ts +355 -0
  384. package/src/electron/hooks/index.ts +30 -0
  385. package/src/electron/hooks/mappings.ts +404 -0
  386. package/src/electron/hooks/server.ts +574 -0
  387. package/src/electron/hooks/settings.ts +466 -0
  388. package/src/electron/hooks/types.ts +245 -0
  389. package/src/electron/ipc/canvas-handlers.ts +223 -0
  390. package/src/electron/ipc/handlers.ts +3661 -0
  391. package/src/electron/ipc/mission-control-handlers.ts +182 -0
  392. package/src/electron/main.ts +496 -0
  393. package/src/electron/mcp/client/MCPClientManager.ts +406 -0
  394. package/src/electron/mcp/client/MCPServerConnection.ts +514 -0
  395. package/src/electron/mcp/client/transports/SSETransport.ts +360 -0
  396. package/src/electron/mcp/client/transports/StdioTransport.ts +355 -0
  397. package/src/electron/mcp/client/transports/WebSocketTransport.ts +384 -0
  398. package/src/electron/mcp/host/MCPHostServer.ts +388 -0
  399. package/src/electron/mcp/host/ToolAdapter.ts +140 -0
  400. package/src/electron/mcp/registry/MCPRegistryManager.ts +565 -0
  401. package/src/electron/mcp/settings.ts +468 -0
  402. package/src/electron/mcp/types.ts +371 -0
  403. package/src/electron/memory/MemoryService.ts +523 -0
  404. package/src/electron/notifications/index.ts +16 -0
  405. package/src/electron/notifications/service.ts +161 -0
  406. package/src/electron/notifications/store.ts +163 -0
  407. package/src/electron/preload.ts +2845 -0
  408. package/src/electron/reports/StandupReportService.ts +356 -0
  409. package/src/electron/security/concurrency.ts +333 -0
  410. package/src/electron/security/index.ts +17 -0
  411. package/src/electron/security/policy-manager.ts +539 -0
  412. package/src/electron/settings/appearance-manager.ts +182 -0
  413. package/src/electron/settings/personality-manager.ts +800 -0
  414. package/src/electron/settings/x-manager.ts +62 -0
  415. package/src/electron/tailscale/exposure.ts +262 -0
  416. package/src/electron/tailscale/index.ts +34 -0
  417. package/src/electron/tailscale/settings.ts +218 -0
  418. package/src/electron/tailscale/tailscale.ts +379 -0
  419. package/src/electron/tray/QuickInputWindow.ts +609 -0
  420. package/src/electron/tray/TrayManager.ts +1005 -0
  421. package/src/electron/tray/index.ts +6 -0
  422. package/src/electron/updater/index.ts +1 -0
  423. package/src/electron/updater/update-manager.ts +447 -0
  424. package/src/electron/utils/env-migration.ts +203 -0
  425. package/src/electron/utils/process.ts +124 -0
  426. package/src/electron/utils/rate-limiter.ts +130 -0
  427. package/src/electron/utils/validation.ts +493 -0
  428. package/src/electron/utils/x-cli.ts +198 -0
  429. package/src/electron/voice/VoiceService.ts +583 -0
  430. package/src/electron/voice/index.ts +9 -0
  431. package/src/electron/voice/voice-settings-manager.ts +403 -0
  432. package/src/renderer/App.tsx +775 -0
  433. package/src/renderer/components/ActivityFeed.tsx +407 -0
  434. package/src/renderer/components/ActivityFeedItem.tsx +285 -0
  435. package/src/renderer/components/AgentRoleCard.tsx +343 -0
  436. package/src/renderer/components/AgentRoleEditor.tsx +805 -0
  437. package/src/renderer/components/AgentSquadSettings.tsx +295 -0
  438. package/src/renderer/components/AgentWorkingStatePanel.tsx +411 -0
  439. package/src/renderer/components/AppearanceSettings.tsx +122 -0
  440. package/src/renderer/components/ApprovalDialog.tsx +100 -0
  441. package/src/renderer/components/BlueBubblesSettings.tsx +505 -0
  442. package/src/renderer/components/BuiltinToolsSettings.tsx +307 -0
  443. package/src/renderer/components/CanvasPreview.tsx +1189 -0
  444. package/src/renderer/components/CommandOutput.tsx +202 -0
  445. package/src/renderer/components/ContextPolicySettings.tsx +523 -0
  446. package/src/renderer/components/ControlPlaneSettings.tsx +1134 -0
  447. package/src/renderer/components/DisclaimerModal.tsx +124 -0
  448. package/src/renderer/components/DiscordSettings.tsx +436 -0
  449. package/src/renderer/components/EmailSettings.tsx +606 -0
  450. package/src/renderer/components/ExtensionsSettings.tsx +542 -0
  451. package/src/renderer/components/FileViewer.tsx +224 -0
  452. package/src/renderer/components/GoogleChatSettings.tsx +535 -0
  453. package/src/renderer/components/GuardrailSettings.tsx +487 -0
  454. package/src/renderer/components/HooksSettings.tsx +581 -0
  455. package/src/renderer/components/ImessageSettings.tsx +484 -0
  456. package/src/renderer/components/LineSettings.tsx +483 -0
  457. package/src/renderer/components/MCPRegistryBrowser.tsx +386 -0
  458. package/src/renderer/components/MCPSettings.tsx +943 -0
  459. package/src/renderer/components/MainContent.tsx +2433 -0
  460. package/src/renderer/components/MatrixSettings.tsx +510 -0
  461. package/src/renderer/components/MattermostSettings.tsx +473 -0
  462. package/src/renderer/components/MemorySettings.tsx +247 -0
  463. package/src/renderer/components/MentionBadge.tsx +87 -0
  464. package/src/renderer/components/MentionInput.tsx +409 -0
  465. package/src/renderer/components/MentionList.tsx +476 -0
  466. package/src/renderer/components/MissionControlPanel.tsx +1995 -0
  467. package/src/renderer/components/NodesSettings.tsx +316 -0
  468. package/src/renderer/components/NotificationPanel.tsx +481 -0
  469. package/src/renderer/components/Onboarding/AwakeningOrb.tsx +44 -0
  470. package/src/renderer/components/Onboarding/Onboarding.tsx +443 -0
  471. package/src/renderer/components/Onboarding/TypewriterText.tsx +102 -0
  472. package/src/renderer/components/Onboarding/index.ts +3 -0
  473. package/src/renderer/components/OnboardingModal.tsx +698 -0
  474. package/src/renderer/components/PairingCodeDisplay.tsx +324 -0
  475. package/src/renderer/components/PersonalitySettings.tsx +597 -0
  476. package/src/renderer/components/QueueSettings.tsx +119 -0
  477. package/src/renderer/components/QuickTaskFAB.tsx +71 -0
  478. package/src/renderer/components/RightPanel.tsx +413 -0
  479. package/src/renderer/components/ScheduledTasksSettings.tsx +1328 -0
  480. package/src/renderer/components/SearchSettings.tsx +328 -0
  481. package/src/renderer/components/Settings.tsx +1504 -0
  482. package/src/renderer/components/Sidebar.tsx +344 -0
  483. package/src/renderer/components/SignalSettings.tsx +673 -0
  484. package/src/renderer/components/SkillHubBrowser.tsx +458 -0
  485. package/src/renderer/components/SkillParameterModal.tsx +185 -0
  486. package/src/renderer/components/SkillsSettings.tsx +451 -0
  487. package/src/renderer/components/SlackSettings.tsx +442 -0
  488. package/src/renderer/components/StandupReportViewer.tsx +614 -0
  489. package/src/renderer/components/TaskBoard.tsx +498 -0
  490. package/src/renderer/components/TaskBoardCard.tsx +357 -0
  491. package/src/renderer/components/TaskBoardColumn.tsx +211 -0
  492. package/src/renderer/components/TaskLabelManager.tsx +472 -0
  493. package/src/renderer/components/TaskQueuePanel.tsx +144 -0
  494. package/src/renderer/components/TaskQuickActions.tsx +492 -0
  495. package/src/renderer/components/TaskTimeline.tsx +216 -0
  496. package/src/renderer/components/TaskView.tsx +162 -0
  497. package/src/renderer/components/TeamsSettings.tsx +518 -0
  498. package/src/renderer/components/TelegramSettings.tsx +421 -0
  499. package/src/renderer/components/Toast.tsx +76 -0
  500. package/src/renderer/components/TraySettings.tsx +189 -0
  501. package/src/renderer/components/TwitchSettings.tsx +511 -0
  502. package/src/renderer/components/UpdateSettings.tsx +295 -0
  503. package/src/renderer/components/VoiceIndicator.tsx +270 -0
  504. package/src/renderer/components/VoiceSettings.tsx +867 -0
  505. package/src/renderer/components/WhatsAppSettings.tsx +721 -0
  506. package/src/renderer/components/WorkingStateEditor.tsx +309 -0
  507. package/src/renderer/components/WorkingStateHistory.tsx +481 -0
  508. package/src/renderer/components/WorkspaceSelector.tsx +150 -0
  509. package/src/renderer/components/XSettings.tsx +311 -0
  510. package/src/renderer/global.d.ts +9 -0
  511. package/src/renderer/hooks/useAgentContext.ts +153 -0
  512. package/src/renderer/hooks/useOnboardingFlow.ts +548 -0
  513. package/src/renderer/hooks/useVoiceInput.ts +268 -0
  514. package/src/renderer/index.html +12 -0
  515. package/src/renderer/main.tsx +10 -0
  516. package/src/renderer/public/cowork-os-logo.png +0 -0
  517. package/src/renderer/quick-input.html +164 -0
  518. package/src/renderer/styles/index.css +14504 -0
  519. package/src/renderer/utils/agentMessages.ts +749 -0
  520. package/src/renderer/utils/voice-directives.ts +169 -0
  521. package/src/shared/channelMessages.ts +213 -0
  522. package/src/shared/types.ts +3608 -0
  523. package/tsconfig.electron.json +26 -0
  524. package/tsconfig.json +26 -0
  525. package/tsconfig.node.json +10 -0
  526. package/vite.config.ts +23 -0
@@ -0,0 +1,760 @@
1
+ /**
2
+ * Google Chat Channel Adapter
3
+ *
4
+ * Implements the ChannelAdapter interface using Google Chat API.
5
+ * Supports direct messages, spaces, and threaded conversations.
6
+ *
7
+ * Authentication: Service Account with Domain-Wide Delegation
8
+ * Message reception: HTTP webhook or Pub/Sub subscription
9
+ * Message sending: Google Chat REST API
10
+ */
11
+
12
+ import * as http from 'http';
13
+ import * as https from 'https';
14
+ import * as fs from 'fs';
15
+ import * as path from 'path';
16
+ import * as crypto from 'crypto';
17
+ import {
18
+ ChannelAdapter,
19
+ ChannelStatus,
20
+ IncomingMessage,
21
+ OutgoingMessage,
22
+ MessageHandler,
23
+ ErrorHandler,
24
+ StatusHandler,
25
+ ChannelInfo,
26
+ GoogleChatConfig,
27
+ } from './types';
28
+
29
+ /**
30
+ * Google Chat event types
31
+ */
32
+ interface GoogleChatEvent {
33
+ type: 'MESSAGE' | 'ADDED_TO_SPACE' | 'REMOVED_FROM_SPACE' | 'CARD_CLICKED';
34
+ eventTime: string;
35
+ token?: string;
36
+ message?: GoogleChatMessage;
37
+ user?: GoogleChatUser;
38
+ space?: GoogleChatSpace;
39
+ action?: GoogleChatAction;
40
+ configCompleteRedirectUrl?: string;
41
+ }
42
+
43
+ interface GoogleChatMessage {
44
+ name: string;
45
+ sender: GoogleChatUser;
46
+ createTime: string;
47
+ text?: string;
48
+ formattedText?: string;
49
+ thread?: { name: string };
50
+ space: GoogleChatSpace;
51
+ argumentText?: string;
52
+ attachment?: GoogleChatAttachment[];
53
+ slashCommand?: { commandId: string };
54
+ }
55
+
56
+ interface GoogleChatUser {
57
+ name: string;
58
+ displayName: string;
59
+ avatarUrl?: string;
60
+ email?: string;
61
+ type: 'HUMAN' | 'BOT';
62
+ domainId?: string;
63
+ }
64
+
65
+ interface GoogleChatSpace {
66
+ name: string;
67
+ type: 'ROOM' | 'DM' | 'SPACE';
68
+ displayName?: string;
69
+ spaceThreadingState?: string;
70
+ singleUserBotDm?: boolean;
71
+ }
72
+
73
+ interface GoogleChatAttachment {
74
+ name: string;
75
+ contentName: string;
76
+ contentType: string;
77
+ thumbnailUri?: string;
78
+ downloadUri?: string;
79
+ source: 'DRIVE_FILE' | 'UPLOADED_CONTENT';
80
+ }
81
+
82
+ interface GoogleChatAction {
83
+ actionMethodName: string;
84
+ parameters?: Array<{ key: string; value: string }>;
85
+ }
86
+
87
+ /**
88
+ * Simple TTL cache for message deduplication
89
+ */
90
+ class MessageDeduplicationCache {
91
+ private cache: Map<string, number> = new Map();
92
+ private readonly ttlMs: number;
93
+ private cleanupInterval: NodeJS.Timeout;
94
+
95
+ constructor(ttlMs: number = 60000) {
96
+ this.ttlMs = ttlMs;
97
+ this.cleanupInterval = setInterval(() => this.cleanup(), 60000);
98
+ }
99
+
100
+ has(messageId: string): boolean {
101
+ const timestamp = this.cache.get(messageId);
102
+ if (!timestamp) return false;
103
+ if (Date.now() - timestamp > this.ttlMs) {
104
+ this.cache.delete(messageId);
105
+ return false;
106
+ }
107
+ return true;
108
+ }
109
+
110
+ add(messageId: string): void {
111
+ this.cache.set(messageId, Date.now());
112
+ }
113
+
114
+ private cleanup(): void {
115
+ const now = Date.now();
116
+ for (const [key, timestamp] of this.cache.entries()) {
117
+ if (now - timestamp > this.ttlMs) {
118
+ this.cache.delete(key);
119
+ }
120
+ }
121
+ }
122
+
123
+ destroy(): void {
124
+ clearInterval(this.cleanupInterval);
125
+ this.cache.clear();
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Google OAuth2 token manager for service account
131
+ */
132
+ class GoogleAuthManager {
133
+ private accessToken: string | null = null;
134
+ private tokenExpiry: number = 0;
135
+ private credentials: {
136
+ client_email: string;
137
+ private_key: string;
138
+ project_id: string;
139
+ };
140
+
141
+ constructor(credentials: { client_email: string; private_key: string; project_id: string }) {
142
+ this.credentials = credentials;
143
+ }
144
+
145
+ async getAccessToken(): Promise<string> {
146
+ if (this.accessToken && Date.now() < this.tokenExpiry - 60000) {
147
+ return this.accessToken;
148
+ }
149
+
150
+ const jwt = this.createJWT();
151
+ const token = await this.exchangeJWTForToken(jwt);
152
+ this.accessToken = token.access_token;
153
+ this.tokenExpiry = Date.now() + (token.expires_in * 1000);
154
+ return this.accessToken;
155
+ }
156
+
157
+ private createJWT(): string {
158
+ const header = {
159
+ alg: 'RS256',
160
+ typ: 'JWT',
161
+ };
162
+
163
+ const now = Math.floor(Date.now() / 1000);
164
+ const payload = {
165
+ iss: this.credentials.client_email,
166
+ scope: 'https://www.googleapis.com/auth/chat.bot',
167
+ aud: 'https://oauth2.googleapis.com/token',
168
+ iat: now,
169
+ exp: now + 3600,
170
+ };
171
+
172
+ const encodedHeader = Buffer.from(JSON.stringify(header)).toString('base64url');
173
+ const encodedPayload = Buffer.from(JSON.stringify(payload)).toString('base64url');
174
+ const signatureInput = `${encodedHeader}.${encodedPayload}`;
175
+
176
+ const sign = crypto.createSign('RSA-SHA256');
177
+ sign.update(signatureInput);
178
+ const signature = sign.sign(this.credentials.private_key, 'base64url');
179
+
180
+ return `${signatureInput}.${signature}`;
181
+ }
182
+
183
+ private async exchangeJWTForToken(jwt: string): Promise<{ access_token: string; expires_in: number }> {
184
+ return new Promise((resolve, reject) => {
185
+ const postData = new URLSearchParams({
186
+ grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
187
+ assertion: jwt,
188
+ }).toString();
189
+
190
+ const options = {
191
+ hostname: 'oauth2.googleapis.com',
192
+ port: 443,
193
+ path: '/token',
194
+ method: 'POST',
195
+ headers: {
196
+ 'Content-Type': 'application/x-www-form-urlencoded',
197
+ 'Content-Length': Buffer.byteLength(postData),
198
+ },
199
+ };
200
+
201
+ const req = https.request(options, (res) => {
202
+ let data = '';
203
+ res.on('data', (chunk) => { data += chunk; });
204
+ res.on('end', () => {
205
+ try {
206
+ const parsed = JSON.parse(data);
207
+ if (parsed.error) {
208
+ reject(new Error(`Token exchange failed: ${parsed.error_description || parsed.error}`));
209
+ } else {
210
+ resolve(parsed);
211
+ }
212
+ } catch (e) {
213
+ reject(new Error(`Failed to parse token response: ${data}`));
214
+ }
215
+ });
216
+ });
217
+
218
+ req.on('error', reject);
219
+ req.write(postData);
220
+ req.end();
221
+ });
222
+ }
223
+ }
224
+
225
+ export class GoogleChatAdapter implements ChannelAdapter {
226
+ readonly type = 'googlechat' as const;
227
+
228
+ private server: http.Server | null = null;
229
+ private authManager: GoogleAuthManager | null = null;
230
+ private _status: ChannelStatus = 'disconnected';
231
+ private _botUsername?: string;
232
+ private messageHandlers: MessageHandler[] = [];
233
+ private errorHandlers: ErrorHandler[] = [];
234
+ private statusHandlers: StatusHandler[] = [];
235
+ private config: GoogleChatConfig;
236
+ private deduplicationCache: MessageDeduplicationCache;
237
+ private reconnectAttempts = 0;
238
+ private reconnectTimer: NodeJS.Timeout | null = null;
239
+
240
+ constructor(config: GoogleChatConfig) {
241
+ this.config = config;
242
+ this.deduplicationCache = new MessageDeduplicationCache();
243
+ }
244
+
245
+ get status(): ChannelStatus {
246
+ return this._status;
247
+ }
248
+
249
+ get botUsername(): string | undefined {
250
+ return this._botUsername;
251
+ }
252
+
253
+ /**
254
+ * Connect to Google Chat via webhook server
255
+ */
256
+ async connect(): Promise<void> {
257
+ if (this._status === 'connected' || this._status === 'connecting') {
258
+ return;
259
+ }
260
+
261
+ this.setStatus('connecting');
262
+
263
+ try {
264
+ // Load service account credentials
265
+ const credentials = await this.loadCredentials();
266
+ this.authManager = new GoogleAuthManager(credentials);
267
+
268
+ // Verify credentials by getting a token
269
+ await this.authManager.getAccessToken();
270
+
271
+ // Set bot info from config
272
+ this._botUsername = this.config.displayName || 'Google Chat Bot';
273
+
274
+ // Start the HTTP server for receiving webhooks
275
+ await this.startWebhookServer();
276
+
277
+ console.log(`Google Chat bot "${this._botUsername}" is connected on port ${this.config.webhookPort || 3979}`);
278
+ this.setStatus('connected');
279
+ this.reconnectAttempts = 0;
280
+ } catch (error) {
281
+ const err = error instanceof Error ? error : new Error(String(error));
282
+ this.setStatus('error', err);
283
+ this.scheduleReconnect();
284
+ throw err;
285
+ }
286
+ }
287
+
288
+ /**
289
+ * Load service account credentials
290
+ */
291
+ private async loadCredentials(): Promise<{ client_email: string; private_key: string; project_id: string }> {
292
+ // Check for inline credentials first
293
+ if (this.config.serviceAccountKey) {
294
+ return this.config.serviceAccountKey;
295
+ }
296
+
297
+ // Check for key file path
298
+ if (this.config.serviceAccountKeyPath) {
299
+ const keyPath = this.config.serviceAccountKeyPath;
300
+ if (!fs.existsSync(keyPath)) {
301
+ throw new Error(`Service account key file not found: ${keyPath}`);
302
+ }
303
+ const keyContent = fs.readFileSync(keyPath, 'utf-8');
304
+ const key = JSON.parse(keyContent);
305
+ return {
306
+ client_email: key.client_email,
307
+ private_key: key.private_key,
308
+ project_id: key.project_id,
309
+ };
310
+ }
311
+
312
+ // Check for GOOGLE_APPLICATION_CREDENTIALS environment variable
313
+ const envPath = process.env.GOOGLE_APPLICATION_CREDENTIALS;
314
+ if (envPath && fs.existsSync(envPath)) {
315
+ const keyContent = fs.readFileSync(envPath, 'utf-8');
316
+ const key = JSON.parse(keyContent);
317
+ return {
318
+ client_email: key.client_email,
319
+ private_key: key.private_key,
320
+ project_id: key.project_id,
321
+ };
322
+ }
323
+
324
+ throw new Error('Google Chat credentials not configured. Provide serviceAccountKey or serviceAccountKeyPath.');
325
+ }
326
+
327
+ /**
328
+ * Start the webhook server to receive messages from Google Chat
329
+ */
330
+ private async startWebhookServer(): Promise<void> {
331
+ const port = this.config.webhookPort || 3979;
332
+ const webhookPath = this.config.webhookPath || '/googlechat/webhook';
333
+
334
+ return new Promise((resolve, reject) => {
335
+ this.server = http.createServer(async (req, res) => {
336
+ if (req.method === 'POST' && req.url === webhookPath) {
337
+ let body = '';
338
+ req.on('data', chunk => {
339
+ body += chunk.toString();
340
+ });
341
+ req.on('end', async () => {
342
+ try {
343
+ await this.processIncomingEvent(req, res, body);
344
+ } catch (error) {
345
+ console.error('Error processing Google Chat event:', error);
346
+ res.writeHead(500);
347
+ res.end(JSON.stringify({ error: 'Internal Server Error' }));
348
+ }
349
+ });
350
+ } else if (req.method === 'GET' && req.url === '/health') {
351
+ // Health check endpoint
352
+ res.writeHead(200, { 'Content-Type': 'application/json' });
353
+ res.end(JSON.stringify({ status: 'ok', bot: this._botUsername }));
354
+ } else {
355
+ res.writeHead(404);
356
+ res.end('Not Found');
357
+ }
358
+ });
359
+
360
+ this.server.on('error', (error: NodeJS.ErrnoException) => {
361
+ if (error.code === 'EADDRINUSE') {
362
+ reject(new Error(`Port ${port} is already in use. Please choose a different webhook port.`));
363
+ } else if (error.code === 'EACCES') {
364
+ reject(new Error(`Permission denied to use port ${port}. Try a port above 1024.`));
365
+ } else {
366
+ reject(error);
367
+ }
368
+ });
369
+
370
+ this.server.listen(port, () => {
371
+ console.log(`Google Chat webhook server listening on port ${port} at ${webhookPath}`);
372
+ resolve();
373
+ });
374
+ });
375
+ }
376
+
377
+ /**
378
+ * Process incoming event from Google Chat
379
+ */
380
+ private async processIncomingEvent(
381
+ req: http.IncomingMessage,
382
+ res: http.ServerResponse,
383
+ body: string
384
+ ): Promise<void> {
385
+ try {
386
+ const event: GoogleChatEvent = JSON.parse(body);
387
+
388
+ // Handle different event types
389
+ switch (event.type) {
390
+ case 'MESSAGE':
391
+ await this.handleMessage(event);
392
+ // Respond with empty JSON to acknowledge
393
+ res.writeHead(200, { 'Content-Type': 'application/json' });
394
+ res.end('{}');
395
+ break;
396
+
397
+ case 'ADDED_TO_SPACE':
398
+ console.log(`Bot added to space: ${event.space?.displayName || event.space?.name}`);
399
+ res.writeHead(200, { 'Content-Type': 'application/json' });
400
+ res.end(JSON.stringify({
401
+ text: `Hello! I'm ${this._botUsername}. How can I help you today?`,
402
+ }));
403
+ break;
404
+
405
+ case 'REMOVED_FROM_SPACE':
406
+ console.log(`Bot removed from space: ${event.space?.displayName || event.space?.name}`);
407
+ res.writeHead(200, { 'Content-Type': 'application/json' });
408
+ res.end('{}');
409
+ break;
410
+
411
+ case 'CARD_CLICKED':
412
+ // Handle interactive card clicks
413
+ console.log(`Card clicked: ${event.action?.actionMethodName}`);
414
+ res.writeHead(200, { 'Content-Type': 'application/json' });
415
+ res.end('{}');
416
+ break;
417
+
418
+ default:
419
+ console.log(`Unhandled Google Chat event type: ${event.type}`);
420
+ res.writeHead(200, { 'Content-Type': 'application/json' });
421
+ res.end('{}');
422
+ }
423
+ } catch (error) {
424
+ console.error('Error parsing Google Chat event:', error);
425
+ res.writeHead(400);
426
+ res.end(JSON.stringify({ error: 'Bad Request' }));
427
+ }
428
+ }
429
+
430
+ /**
431
+ * Handle incoming message event
432
+ */
433
+ private async handleMessage(event: GoogleChatEvent): Promise<void> {
434
+ const message = event.message;
435
+ if (!message) {
436
+ return;
437
+ }
438
+
439
+ // Skip bot's own messages
440
+ if (message.sender?.type === 'BOT') {
441
+ return;
442
+ }
443
+
444
+ // Get message text (use argumentText for @mentions, otherwise use text)
445
+ const text = message.argumentText?.trim() || message.text?.trim();
446
+ if (!text) {
447
+ return;
448
+ }
449
+
450
+ // Extract message ID from name (format: spaces/{space}/messages/{message})
451
+ const messageId = message.name.split('/').pop() || message.name;
452
+
453
+ // Deduplication check
454
+ if (this.config.deduplicationEnabled !== false && this.deduplicationCache.has(messageId)) {
455
+ console.log(`Skipping duplicate Google Chat message: ${messageId}`);
456
+ return;
457
+ }
458
+ this.deduplicationCache.add(messageId);
459
+
460
+ // Extract space ID (format: spaces/{space})
461
+ const spaceId = message.space.name;
462
+
463
+ // Get user info
464
+ const userName = message.sender?.displayName || 'Unknown User';
465
+ const userId = message.sender?.name || '';
466
+
467
+ // Map to IncomingMessage format
468
+ const incomingMessage: IncomingMessage = {
469
+ messageId: messageId,
470
+ channel: 'googlechat',
471
+ userId: userId,
472
+ userName: userName,
473
+ chatId: spaceId,
474
+ text: text,
475
+ timestamp: message.createTime ? new Date(message.createTime) : new Date(),
476
+ threadId: message.thread?.name,
477
+ raw: event,
478
+ };
479
+
480
+ console.log(`Processing Google Chat message from ${userName}: ${text.slice(0, 50)}...`);
481
+ await this.handleIncomingMessage(incomingMessage);
482
+ }
483
+
484
+ /**
485
+ * Schedule reconnection with exponential backoff
486
+ */
487
+ private scheduleReconnect(): void {
488
+ if (!this.config.autoReconnect) return;
489
+
490
+ const maxAttempts = this.config.maxReconnectAttempts || 5;
491
+ if (this.reconnectAttempts >= maxAttempts) {
492
+ console.error(`Google Chat: Max reconnection attempts (${maxAttempts}) reached`);
493
+ return;
494
+ }
495
+
496
+ const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
497
+ this.reconnectAttempts++;
498
+
499
+ console.log(`Google Chat: Scheduling reconnection attempt ${this.reconnectAttempts} in ${delay}ms`);
500
+
501
+ if (this.reconnectTimer) {
502
+ clearTimeout(this.reconnectTimer);
503
+ }
504
+
505
+ this.reconnectTimer = setTimeout(async () => {
506
+ try {
507
+ await this.connect();
508
+ } catch (error) {
509
+ console.error('Google Chat reconnection failed:', error);
510
+ }
511
+ }, delay);
512
+ }
513
+
514
+ /**
515
+ * Disconnect from Google Chat
516
+ */
517
+ async disconnect(): Promise<void> {
518
+ if (this.reconnectTimer) {
519
+ clearTimeout(this.reconnectTimer);
520
+ this.reconnectTimer = null;
521
+ }
522
+
523
+ if (this.server) {
524
+ await new Promise<void>((resolve) => {
525
+ this.server!.close(() => resolve());
526
+ });
527
+ this.server = null;
528
+ }
529
+
530
+ this.deduplicationCache.destroy();
531
+ this.authManager = null;
532
+ this._botUsername = undefined;
533
+ this.setStatus('disconnected');
534
+ }
535
+
536
+ /**
537
+ * Send a message to a Google Chat space
538
+ */
539
+ async sendMessage(message: OutgoingMessage): Promise<string> {
540
+ if (!this.authManager || this._status !== 'connected') {
541
+ throw new Error('Google Chat bot is not connected');
542
+ }
543
+
544
+ const accessToken = await this.authManager.getAccessToken();
545
+
546
+ // Prepare message payload
547
+ const payload: Record<string, unknown> = {
548
+ text: message.text,
549
+ };
550
+
551
+ // Add thread if specified
552
+ if (message.threadId) {
553
+ payload.thread = { name: message.threadId };
554
+ }
555
+
556
+ // Build the API URL
557
+ // Format: spaces/{space}/messages
558
+ const apiUrl = `https://chat.googleapis.com/v1/${message.chatId}/messages`;
559
+
560
+ try {
561
+ const response = await this.makeApiRequest('POST', apiUrl, accessToken, payload);
562
+ return (response.name as string) || '';
563
+ } catch (error: unknown) {
564
+ const errorMessage = error instanceof Error ? error.message : String(error);
565
+ console.error('Error sending Google Chat message:', errorMessage);
566
+ throw error;
567
+ }
568
+ }
569
+
570
+ /**
571
+ * Make an authenticated API request to Google Chat
572
+ */
573
+ private async makeApiRequest(
574
+ method: string,
575
+ url: string,
576
+ accessToken: string,
577
+ body?: Record<string, unknown>
578
+ ): Promise<Record<string, unknown>> {
579
+ return new Promise((resolve, reject) => {
580
+ const urlObj = new URL(url);
581
+ const postData = body ? JSON.stringify(body) : undefined;
582
+
583
+ const options: https.RequestOptions = {
584
+ hostname: urlObj.hostname,
585
+ port: 443,
586
+ path: urlObj.pathname + urlObj.search,
587
+ method: method,
588
+ headers: {
589
+ 'Authorization': `Bearer ${accessToken}`,
590
+ 'Content-Type': 'application/json',
591
+ },
592
+ };
593
+
594
+ if (postData) {
595
+ (options.headers as Record<string, string | number>)['Content-Length'] = Buffer.byteLength(postData);
596
+ }
597
+
598
+ const req = https.request(options, (res) => {
599
+ let data = '';
600
+ res.on('data', (chunk) => { data += chunk; });
601
+ res.on('end', () => {
602
+ try {
603
+ const parsed = JSON.parse(data);
604
+ if (res.statusCode && res.statusCode >= 400) {
605
+ reject(new Error(`API error ${res.statusCode}: ${parsed.error?.message || data}`));
606
+ } else {
607
+ resolve(parsed);
608
+ }
609
+ } catch (e) {
610
+ if (res.statusCode && res.statusCode >= 400) {
611
+ reject(new Error(`API error ${res.statusCode}: ${data}`));
612
+ } else {
613
+ resolve({});
614
+ }
615
+ }
616
+ });
617
+ });
618
+
619
+ req.on('error', reject);
620
+ if (postData) {
621
+ req.write(postData);
622
+ }
623
+ req.end();
624
+ });
625
+ }
626
+
627
+ /**
628
+ * Edit an existing message
629
+ */
630
+ async editMessage(chatId: string, messageId: string, text: string): Promise<void> {
631
+ if (!this.authManager || this._status !== 'connected') {
632
+ throw new Error('Google Chat bot is not connected');
633
+ }
634
+
635
+ const accessToken = await this.authManager.getAccessToken();
636
+
637
+ // Build the message name (format: spaces/{space}/messages/{message})
638
+ const messageName = messageId.includes('/') ? messageId : `${chatId}/messages/${messageId}`;
639
+ const apiUrl = `https://chat.googleapis.com/v1/${messageName}?updateMask=text`;
640
+
641
+ const payload = { text };
642
+
643
+ await this.makeApiRequest('PATCH', apiUrl, accessToken, payload);
644
+ }
645
+
646
+ /**
647
+ * Delete a message
648
+ */
649
+ async deleteMessage(chatId: string, messageId: string): Promise<void> {
650
+ if (!this.authManager || this._status !== 'connected') {
651
+ throw new Error('Google Chat bot is not connected');
652
+ }
653
+
654
+ const accessToken = await this.authManager.getAccessToken();
655
+
656
+ // Build the message name
657
+ const messageName = messageId.includes('/') ? messageId : `${chatId}/messages/${messageId}`;
658
+ const apiUrl = `https://chat.googleapis.com/v1/${messageName}`;
659
+
660
+ await this.makeApiRequest('DELETE', apiUrl, accessToken);
661
+ }
662
+
663
+ /**
664
+ * Send a document/file to a space
665
+ * Note: Google Chat has limited file attachment support via API
666
+ */
667
+ async sendDocument(chatId: string, filePath: string, caption?: string): Promise<string> {
668
+ // Google Chat API doesn't support direct file uploads
669
+ // Files must be uploaded to Google Drive first
670
+ // For now, send a message with the file path/name
671
+ const fileName = path.basename(filePath);
672
+ const message = caption ? `${caption}\nšŸ“Ž ${fileName}` : `šŸ“Ž ${fileName}`;
673
+
674
+ return this.sendMessage({
675
+ chatId,
676
+ text: message,
677
+ });
678
+ }
679
+
680
+ /**
681
+ * Register a message handler
682
+ */
683
+ onMessage(handler: MessageHandler): void {
684
+ this.messageHandlers.push(handler);
685
+ }
686
+
687
+ /**
688
+ * Register an error handler
689
+ */
690
+ onError(handler: ErrorHandler): void {
691
+ this.errorHandlers.push(handler);
692
+ }
693
+
694
+ /**
695
+ * Register a status change handler
696
+ */
697
+ onStatusChange(handler: StatusHandler): void {
698
+ this.statusHandlers.push(handler);
699
+ }
700
+
701
+ /**
702
+ * Get channel info
703
+ */
704
+ async getInfo(): Promise<ChannelInfo> {
705
+ return {
706
+ type: 'googlechat',
707
+ status: this._status,
708
+ botUsername: this._botUsername,
709
+ botDisplayName: this._botUsername,
710
+ };
711
+ }
712
+
713
+ // Private helper methods
714
+
715
+ private async handleIncomingMessage(message: IncomingMessage): Promise<void> {
716
+ for (const handler of this.messageHandlers) {
717
+ try {
718
+ await handler(message);
719
+ } catch (error) {
720
+ console.error('Error in message handler:', error);
721
+ this.handleError(
722
+ error instanceof Error ? error : new Error(String(error)),
723
+ 'messageHandler'
724
+ );
725
+ }
726
+ }
727
+ }
728
+
729
+ private handleError(error: Error, context?: string): void {
730
+ for (const handler of this.errorHandlers) {
731
+ try {
732
+ handler(error, context);
733
+ } catch (e) {
734
+ console.error('Error in error handler:', e);
735
+ }
736
+ }
737
+ }
738
+
739
+ private setStatus(status: ChannelStatus, error?: Error): void {
740
+ this._status = status;
741
+ for (const handler of this.statusHandlers) {
742
+ try {
743
+ handler(status, error);
744
+ } catch (e) {
745
+ console.error('Error in status handler:', e);
746
+ }
747
+ }
748
+ }
749
+ }
750
+
751
+ /**
752
+ * Create a Google Chat adapter from configuration
753
+ */
754
+ export function createGoogleChatAdapter(config: GoogleChatConfig): GoogleChatAdapter {
755
+ // At least one credential source must be provided
756
+ if (!config.serviceAccountKey && !config.serviceAccountKeyPath && !process.env.GOOGLE_APPLICATION_CREDENTIALS) {
757
+ throw new Error('Google Chat requires service account credentials (serviceAccountKey, serviceAccountKeyPath, or GOOGLE_APPLICATION_CREDENTIALS)');
758
+ }
759
+ return new GoogleChatAdapter(config);
760
+ }