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,422 @@
1
+ /**
2
+ * Security Manager
3
+ *
4
+ * Handles user authorization for channel access.
5
+ * Supports three modes:
6
+ * - open: Anyone can use the bot
7
+ * - allowlist: Only pre-approved users
8
+ * - pairing: Users must enter a pairing code generated in the desktop app
9
+ *
10
+ * Implements concurrent access safety using mutex locks to prevent race conditions
11
+ * in pairing operations.
12
+ */
13
+
14
+ import * as crypto from 'crypto';
15
+ import Database from 'better-sqlite3';
16
+ import {
17
+ ChannelUserRepository,
18
+ ChannelUser,
19
+ Channel,
20
+ } from '../database/repositories';
21
+ import { IncomingMessage } from './channels/types';
22
+ import { pairingMutex, IdempotencyManager } from '../security/concurrency';
23
+ import { ContextPolicyManager } from './context-policy';
24
+ import { ContextType, SecurityMode } from '../../shared/types';
25
+
26
+ export interface AccessCheckResult {
27
+ allowed: boolean;
28
+ user?: ChannelUser;
29
+ reason?: string;
30
+ pairingRequired?: boolean;
31
+ /** The context type (dm or group) for this message */
32
+ contextType?: ContextType;
33
+ /** Tools denied in this context */
34
+ deniedTools?: string[];
35
+ }
36
+
37
+ export interface PairingResult {
38
+ success: boolean;
39
+ user?: ChannelUser;
40
+ error?: string;
41
+ }
42
+
43
+ export class SecurityManager {
44
+ private userRepo: ChannelUserRepository;
45
+ private pairingIdempotency: IdempotencyManager;
46
+ private contextPolicyManager: ContextPolicyManager;
47
+
48
+ // Channels that don't support group chats - always use DM context
49
+ private static readonly DM_ONLY_CHANNELS = ['email', 'imessage', 'bluebubbles'];
50
+
51
+ constructor(db: Database.Database) {
52
+ this.userRepo = new ChannelUserRepository(db);
53
+ this.pairingIdempotency = new IdempotencyManager(5 * 60 * 1000); // 5 min TTL
54
+ this.contextPolicyManager = new ContextPolicyManager(db);
55
+ }
56
+
57
+ /**
58
+ * Get the context policy manager for direct access to context policies
59
+ */
60
+ getContextPolicyManager(): ContextPolicyManager {
61
+ return this.contextPolicyManager;
62
+ }
63
+
64
+ /**
65
+ * Check if a message sender is allowed to interact
66
+ * Supports per-context (DM vs group) security policies
67
+ */
68
+ async checkAccess(
69
+ channel: Channel,
70
+ message: IncomingMessage,
71
+ isGroup?: boolean
72
+ ): Promise<AccessCheckResult> {
73
+ const securityConfig = channel.securityConfig;
74
+
75
+ // Determine context type
76
+ // Priority: 1) Explicit isGroup parameter, 2) Channel type check, 3) Inference from IDs
77
+ let contextType: ContextType;
78
+
79
+ if (isGroup !== undefined) {
80
+ // Explicit parameter takes precedence
81
+ contextType = isGroup ? 'group' : 'dm';
82
+ } else if (SecurityManager.DM_ONLY_CHANNELS.includes(channel.type)) {
83
+ // Channels that don't support groups always use DM context
84
+ contextType = 'dm';
85
+ } else {
86
+ // Infer from message - chatId different from userId typically means group
87
+ // This works for Telegram, Discord, Slack, etc.
88
+ contextType = message.chatId !== message.userId ? 'group' : 'dm';
89
+ }
90
+
91
+ // Get context-specific policy (creates default if doesn't exist)
92
+ const contextPolicy = this.contextPolicyManager.getPolicy(
93
+ channel.id,
94
+ contextType
95
+ );
96
+
97
+ // Use context policy's security mode, falling back to channel default
98
+ const mode: SecurityMode = contextPolicy.securityMode || securityConfig.mode;
99
+
100
+ // Get denied tools for this context
101
+ const deniedTools = contextPolicy.toolRestrictions || [];
102
+
103
+ // Get or create user record
104
+ let user = this.userRepo.findByChannelUserId(channel.id, message.userId);
105
+
106
+ if (!user) {
107
+ // Create new user record
108
+ user = this.userRepo.create({
109
+ channelId: channel.id,
110
+ channelUserId: message.userId,
111
+ displayName: message.userName,
112
+ allowed: mode === 'open', // Auto-allow in open mode
113
+ });
114
+ } else {
115
+ // Update display name if changed
116
+ if (user.displayName !== message.userName) {
117
+ this.userRepo.update(user.id, { displayName: message.userName });
118
+ }
119
+ }
120
+
121
+ // Check based on security mode
122
+ switch (mode) {
123
+ case 'open':
124
+ // Everyone is allowed
125
+ return { allowed: true, user, contextType, deniedTools };
126
+
127
+ case 'allowlist': {
128
+ // Check if user is in allowlist
129
+ if (user.allowed) {
130
+ return { allowed: true, user, contextType, deniedTools };
131
+ }
132
+ // Check if user ID is in config allowlist
133
+ const allowedUsers = securityConfig.allowedUsers || [];
134
+ if (allowedUsers.includes(message.userId)) {
135
+ // Add to allowed users
136
+ this.userRepo.update(user.id, { allowed: true });
137
+ return {
138
+ allowed: true,
139
+ user: { ...user, allowed: true },
140
+ contextType,
141
+ deniedTools,
142
+ };
143
+ }
144
+ return {
145
+ allowed: false,
146
+ user,
147
+ reason: 'User not in allowlist',
148
+ contextType,
149
+ deniedTools,
150
+ };
151
+ }
152
+
153
+ case 'pairing':
154
+ // Check if user has been paired
155
+ if (user.allowed) {
156
+ return { allowed: true, user, contextType, deniedTools };
157
+ }
158
+ return {
159
+ allowed: false,
160
+ user,
161
+ reason: 'Pairing required',
162
+ pairingRequired: true,
163
+ contextType,
164
+ deniedTools,
165
+ };
166
+
167
+ default:
168
+ return {
169
+ allowed: false,
170
+ reason: `Unknown security mode: ${mode}`,
171
+ contextType,
172
+ deniedTools,
173
+ };
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Generate a pairing code for a channel
179
+ * Creates a placeholder entry that can be claimed by any user who enters the code
180
+ * Uses mutex to prevent race conditions in concurrent code generation
181
+ */
182
+ generatePairingCode(channel: Channel, _userId?: string, _displayName?: string): string {
183
+ // Use synchronous mutex key for this channel to prevent concurrent generation issues
184
+ const mutexKey = `pairing:generate:${channel.id}`;
185
+
186
+ // Generate code (synchronous operation, but we track idempotency)
187
+ const code = this.createPairingCode();
188
+ const ttl = channel.securityConfig.pairingCodeTTL || 300; // 5 minutes default
189
+ const expiresAt = Date.now() + ttl * 1000;
190
+
191
+ // Create a placeholder user entry with the pairing code
192
+ // Use a unique placeholder ID so multiple codes can exist
193
+ const placeholderId = `pending_${code}_${Date.now()}`;
194
+
195
+ this.userRepo.create({
196
+ channelId: channel.id,
197
+ channelUserId: placeholderId,
198
+ displayName: 'Pending User',
199
+ allowed: false,
200
+ pairingCode: code,
201
+ pairingExpiresAt: expiresAt,
202
+ });
203
+
204
+ return code;
205
+ }
206
+
207
+ /**
208
+ * Verify a pairing code
209
+ * Looks up the code across all users in the channel and grants access to the caller
210
+ * Uses idempotency to prevent double-verification race conditions
211
+ */
212
+ async verifyPairingCode(
213
+ channel: Channel,
214
+ userId: string,
215
+ code: string
216
+ ): Promise<PairingResult> {
217
+ // Generate idempotency key for this verification attempt
218
+ const idempotencyKey = IdempotencyManager.generateKey(
219
+ 'pairing:verify',
220
+ channel.id,
221
+ userId,
222
+ code.toUpperCase()
223
+ );
224
+
225
+ // Check if this exact verification is already in progress or completed
226
+ const existing = this.pairingIdempotency.check(idempotencyKey);
227
+ if (existing.exists && existing.status === 'completed') {
228
+ return existing.result as PairingResult;
229
+ }
230
+
231
+ // Use mutex to ensure only one verification happens at a time per channel
232
+ const mutexKey = `pairing:verify:${channel.id}`;
233
+
234
+ return await pairingMutex.withLock(mutexKey, async () => {
235
+ // Double-check idempotency after acquiring lock
236
+ const recheck = this.pairingIdempotency.check(idempotencyKey);
237
+ if (recheck.exists && recheck.status === 'completed') {
238
+ return recheck.result as PairingResult;
239
+ }
240
+
241
+ // Start tracking this operation
242
+ this.pairingIdempotency.start(idempotencyKey);
243
+
244
+ try {
245
+ const result = await this.doVerifyPairingCode(channel, userId, code);
246
+ this.pairingIdempotency.complete(idempotencyKey, result);
247
+ return result;
248
+ } catch (error) {
249
+ this.pairingIdempotency.fail(idempotencyKey, error);
250
+ throw error;
251
+ }
252
+ });
253
+ }
254
+
255
+ /**
256
+ * Internal pairing verification logic (called within mutex)
257
+ */
258
+ // Maximum pairing attempts before lockout (brute-force protection)
259
+ private static readonly MAX_PAIRING_ATTEMPTS = 5;
260
+ // Lockout duration in milliseconds (15 minutes)
261
+ private static readonly PAIRING_LOCKOUT_MS = 15 * 60 * 1000;
262
+
263
+ private async doVerifyPairingCode(
264
+ channel: Channel,
265
+ userId: string,
266
+ code: string
267
+ ): Promise<PairingResult> {
268
+ // First check if user is already allowed
269
+ const existingUser = this.userRepo.findByChannelUserId(channel.id, userId);
270
+ if (existingUser?.allowed) {
271
+ return { success: true, user: existingUser };
272
+ }
273
+
274
+ // Brute-force protection: Check if user is locked out due to too many failed attempts
275
+ if (existingUser && existingUser.pairingAttempts >= SecurityManager.MAX_PAIRING_ATTEMPTS) {
276
+ // Check if lockout period has passed (use dedicated lockoutUntil field)
277
+ const lockoutUntil = existingUser.lockoutUntil;
278
+ if (lockoutUntil && Date.now() < lockoutUntil) {
279
+ const remainingMinutes = Math.ceil((lockoutUntil - Date.now()) / 60000);
280
+ return {
281
+ success: false,
282
+ error: `Too many failed attempts. Please wait ${remainingMinutes} minute(s) before trying again.`,
283
+ };
284
+ }
285
+ // Lockout expired - reset attempts (keep pairingExpiresAt unchanged)
286
+ this.userRepo.update(existingUser.id, {
287
+ pairingAttempts: 0,
288
+ lockoutUntil: undefined,
289
+ });
290
+ }
291
+
292
+ // Look up the pairing code across all users in the channel
293
+ const codeOwner = this.userRepo.findByPairingCode(channel.id, code.toUpperCase());
294
+
295
+ if (!codeOwner) {
296
+ // Code not found - increment attempts on the requesting user if they exist
297
+ if (existingUser) {
298
+ const newAttempts = existingUser.pairingAttempts + 1;
299
+ const updates: { pairingAttempts: number; lockoutUntil?: number } = {
300
+ pairingAttempts: newAttempts,
301
+ };
302
+ // Set lockout timestamp if max attempts reached (uses dedicated lockoutUntil field)
303
+ if (newAttempts >= SecurityManager.MAX_PAIRING_ATTEMPTS) {
304
+ updates.lockoutUntil = Date.now() + SecurityManager.PAIRING_LOCKOUT_MS;
305
+ }
306
+ this.userRepo.update(existingUser.id, updates);
307
+
308
+ // Warn user about remaining attempts
309
+ const remaining = SecurityManager.MAX_PAIRING_ATTEMPTS - newAttempts;
310
+ if (remaining > 0) {
311
+ return { success: false, error: `Invalid pairing code. ${remaining} attempt(s) remaining.` };
312
+ } else {
313
+ return { success: false, error: 'Too many failed attempts. Please wait 15 minutes before trying again.' };
314
+ }
315
+ }
316
+ return { success: false, error: 'Invalid pairing code' };
317
+ }
318
+
319
+ // Check expiration
320
+ if (codeOwner.pairingExpiresAt && Date.now() > codeOwner.pairingExpiresAt) {
321
+ // Clear expired code
322
+ this.userRepo.update(codeOwner.id, {
323
+ pairingCode: undefined,
324
+ pairingExpiresAt: undefined,
325
+ });
326
+ return { success: false, error: 'Pairing code has expired. Please request a new one.' };
327
+ }
328
+
329
+ // Code is valid! Grant access to the requesting user
330
+ if (existingUser) {
331
+ // Update existing user to be allowed
332
+ this.userRepo.update(existingUser.id, {
333
+ allowed: true,
334
+ pairingCode: undefined,
335
+ pairingExpiresAt: undefined,
336
+ pairingAttempts: 0,
337
+ lockoutUntil: undefined,
338
+ });
339
+ // Clear the code from wherever it was stored
340
+ if (codeOwner.id !== existingUser.id) {
341
+ this.userRepo.update(codeOwner.id, {
342
+ pairingCode: undefined,
343
+ pairingExpiresAt: undefined,
344
+ });
345
+ }
346
+ return { success: true, user: { ...existingUser, allowed: true } };
347
+ } else {
348
+ // This shouldn't happen since checkAccess creates the user, but handle it
349
+ return { success: false, error: 'User record not found' };
350
+ }
351
+ }
352
+
353
+ /**
354
+ * Revoke a user's access
355
+ */
356
+ revokeAccess(channelId: string, userId: string): void {
357
+ const user = this.userRepo.findByChannelUserId(channelId, userId);
358
+ if (user) {
359
+ this.userRepo.update(user.id, { allowed: false });
360
+ }
361
+ }
362
+
363
+ /**
364
+ * Grant a user access directly (for allowlist management)
365
+ */
366
+ grantAccess(channelId: string, userId: string, displayName?: string): void {
367
+ let user = this.userRepo.findByChannelUserId(channelId, userId);
368
+
369
+ if (user) {
370
+ this.userRepo.update(user.id, { allowed: true });
371
+ } else if (displayName) {
372
+ this.userRepo.create({
373
+ channelId,
374
+ channelUserId: userId,
375
+ displayName,
376
+ allowed: true,
377
+ });
378
+ }
379
+ }
380
+
381
+ /**
382
+ * Get all users for a channel
383
+ * Automatically cleans up expired pending pairing entries before returning
384
+ */
385
+ getChannelUsers(channelId: string): ChannelUser[] {
386
+ // Cleanup expired pending entries first
387
+ this.cleanupExpiredPending(channelId);
388
+ return this.userRepo.findByChannelId(channelId);
389
+ }
390
+
391
+ /**
392
+ * Cleanup expired pending pairing entries for a channel
393
+ * These are placeholder entries created when generating pairing codes that have expired
394
+ * Returns the number of deleted entries
395
+ */
396
+ cleanupExpiredPending(channelId: string): number {
397
+ return this.userRepo.deleteExpiredPending(channelId);
398
+ }
399
+
400
+ /**
401
+ * Get allowed users for a channel
402
+ */
403
+ getAllowedUsers(channelId: string): ChannelUser[] {
404
+ return this.userRepo.findAllowedByChannelId(channelId);
405
+ }
406
+
407
+ // Private methods
408
+
409
+ /**
410
+ * Create a random pairing code
411
+ */
412
+ private createPairingCode(): string {
413
+ // Generate 6-character alphanumeric code
414
+ const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; // Exclude similar chars (I, O, 1, 0)
415
+ let code = '';
416
+ const randomBytes = crypto.randomBytes(6);
417
+ for (let i = 0; i < 6; i++) {
418
+ code += chars[randomBytes[i] % chars.length];
419
+ }
420
+ return code;
421
+ }
422
+ }
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Session Manager
3
+ *
4
+ * Manages channel sessions linking chats to CoWork tasks.
5
+ */
6
+
7
+ import Database from 'better-sqlite3';
8
+ import {
9
+ ChannelSessionRepository,
10
+ ChannelSession,
11
+ Channel,
12
+ } from '../database/repositories';
13
+
14
+ export class SessionManager {
15
+ private sessionRepo: ChannelSessionRepository;
16
+
17
+ constructor(db: Database.Database) {
18
+ this.sessionRepo = new ChannelSessionRepository(db);
19
+ }
20
+
21
+ /**
22
+ * Get or create a session for a chat
23
+ */
24
+ async getOrCreateSession(
25
+ channel: Channel,
26
+ chatId: string,
27
+ userId?: string,
28
+ defaultWorkspaceId?: string
29
+ ): Promise<ChannelSession> {
30
+ // Look for existing session
31
+ let session = this.sessionRepo.findByChatId(channel.id, chatId);
32
+
33
+ if (session) {
34
+ // Update last activity
35
+ this.sessionRepo.update(session.id, {
36
+ lastActivityAt: Date.now(),
37
+ });
38
+ return { ...session, lastActivityAt: Date.now() };
39
+ }
40
+
41
+ // Create new session
42
+ session = this.sessionRepo.create({
43
+ channelId: channel.id,
44
+ chatId,
45
+ userId,
46
+ workspaceId: defaultWorkspaceId,
47
+ state: 'idle',
48
+ });
49
+
50
+ return session;
51
+ }
52
+
53
+ /**
54
+ * Get session by ID
55
+ */
56
+ getSession(sessionId: string): ChannelSession | undefined {
57
+ return this.sessionRepo.findById(sessionId);
58
+ }
59
+
60
+ /**
61
+ * Get session by task ID
62
+ */
63
+ getSessionByTaskId(taskId: string): ChannelSession | undefined {
64
+ return this.sessionRepo.findByTaskId(taskId);
65
+ }
66
+
67
+ /**
68
+ * Update session state
69
+ */
70
+ updateSessionState(
71
+ sessionId: string,
72
+ state: 'idle' | 'active' | 'waiting_approval'
73
+ ): void {
74
+ this.sessionRepo.update(sessionId, {
75
+ state,
76
+ lastActivityAt: Date.now(),
77
+ });
78
+ }
79
+
80
+ /**
81
+ * Link a session to a task
82
+ */
83
+ linkSessionToTask(sessionId: string, taskId: string): void {
84
+ this.sessionRepo.update(sessionId, {
85
+ taskId,
86
+ state: 'active',
87
+ lastActivityAt: Date.now(),
88
+ });
89
+ }
90
+
91
+ /**
92
+ * Unlink session from task
93
+ */
94
+ unlinkSessionFromTask(sessionId: string): void {
95
+ this.sessionRepo.update(sessionId, {
96
+ taskId: undefined,
97
+ state: 'idle',
98
+ lastActivityAt: Date.now(),
99
+ });
100
+ }
101
+
102
+ /**
103
+ * Set session workspace
104
+ */
105
+ setSessionWorkspace(sessionId: string, workspaceId: string): void {
106
+ this.sessionRepo.update(sessionId, {
107
+ workspaceId,
108
+ lastActivityAt: Date.now(),
109
+ });
110
+ }
111
+
112
+ /**
113
+ * Update session context
114
+ */
115
+ updateSessionContext(
116
+ sessionId: string,
117
+ context: Record<string, unknown>
118
+ ): void {
119
+ const session = this.sessionRepo.findById(sessionId);
120
+ if (session) {
121
+ const mergedContext = { ...session.context, ...context };
122
+ this.sessionRepo.update(sessionId, {
123
+ context: mergedContext,
124
+ lastActivityAt: Date.now(),
125
+ });
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Get active sessions for a channel
131
+ */
132
+ getActiveSessions(channelId: string): ChannelSession[] {
133
+ return this.sessionRepo.findActiveByChannelId(channelId);
134
+ }
135
+
136
+ /**
137
+ * Clean up old idle sessions
138
+ */
139
+ cleanupOldSessions(maxAgeMs: number = 24 * 60 * 60 * 1000): void {
140
+ // This would require a new method in the repository
141
+ // For now, we'll skip automated cleanup
142
+ console.log('Session cleanup not yet implemented');
143
+ }
144
+ }