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,450 @@
1
+ import { app } from 'electron';
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import {
5
+ SearchProvider,
6
+ SearchProviderConfig,
7
+ SearchProviderType,
8
+ SearchType,
9
+ SearchQuery,
10
+ SearchResponse,
11
+ SEARCH_PROVIDER_INFO,
12
+ } from './types';
13
+ import { TavilyProvider } from './tavily-provider';
14
+ import { BraveProvider } from './brave-provider';
15
+ import { SerpApiProvider } from './serpapi-provider';
16
+ import { GoogleProvider } from './google-provider';
17
+ import { SecureSettingsRepository } from '../../database/SecureSettingsRepository';
18
+
19
+ const LEGACY_SETTINGS_FILE = 'search-settings.json';
20
+
21
+ /**
22
+ * Stored settings for Search provider
23
+ */
24
+ export interface SearchSettings {
25
+ primaryProvider: SearchProviderType | null;
26
+ fallbackProvider: SearchProviderType | null;
27
+ tavily?: {
28
+ apiKey?: string;
29
+ };
30
+ brave?: {
31
+ apiKey?: string;
32
+ };
33
+ serpapi?: {
34
+ apiKey?: string;
35
+ };
36
+ google?: {
37
+ apiKey?: string;
38
+ searchEngineId?: string;
39
+ };
40
+ }
41
+
42
+ const DEFAULT_SETTINGS: SearchSettings = {
43
+ primaryProvider: null,
44
+ fallbackProvider: null,
45
+ };
46
+
47
+ /**
48
+ * Factory for creating Search providers with fallback support
49
+ */
50
+ export class SearchProviderFactory {
51
+ private static legacySettingsPath: string;
52
+ private static cachedSettings: SearchSettings | null = null;
53
+ private static migrationCompleted = false;
54
+
55
+ /**
56
+ * Initialize the factory
57
+ */
58
+ static initialize(): void {
59
+ const userDataPath = app.getPath('userData');
60
+ this.legacySettingsPath = path.join(userDataPath, LEGACY_SETTINGS_FILE);
61
+
62
+ // Migrate from legacy JSON file to encrypted database
63
+ this.migrateFromLegacyFile();
64
+ }
65
+
66
+ /**
67
+ * Migrate settings from legacy JSON file to encrypted database
68
+ */
69
+ private static migrateFromLegacyFile(): void {
70
+ if (this.migrationCompleted) return;
71
+
72
+ try {
73
+ // Check if SecureSettingsRepository is initialized
74
+ if (!SecureSettingsRepository.isInitialized()) {
75
+ console.log('[SearchProviderFactory] SecureSettingsRepository not yet initialized, skipping migration');
76
+ return;
77
+ }
78
+
79
+ const repository = SecureSettingsRepository.getInstance();
80
+
81
+ // Check if already migrated to database
82
+ if (repository.exists('search')) {
83
+ this.migrationCompleted = true;
84
+ return;
85
+ }
86
+
87
+ // Check if legacy file exists
88
+ if (!fs.existsSync(this.legacySettingsPath)) {
89
+ console.log('[SearchProviderFactory] No legacy settings file found');
90
+ this.migrationCompleted = true;
91
+ return;
92
+ }
93
+
94
+ console.log('[SearchProviderFactory] Migrating settings from legacy JSON file to encrypted database...');
95
+
96
+ // Create backup before migration
97
+ const backupPath = this.legacySettingsPath + '.migration-backup';
98
+ fs.copyFileSync(this.legacySettingsPath, backupPath);
99
+
100
+ try {
101
+ // Read legacy settings
102
+ const data = fs.readFileSync(this.legacySettingsPath, 'utf-8');
103
+ const parsed = JSON.parse(data);
104
+
105
+ // Handle migration from old format (providerType -> primaryProvider)
106
+ if (parsed.providerType && !parsed.primaryProvider) {
107
+ parsed.primaryProvider = parsed.providerType;
108
+ delete parsed.providerType;
109
+ }
110
+
111
+ const legacySettings = { ...DEFAULT_SETTINGS, ...parsed };
112
+
113
+ // Save to encrypted database
114
+ repository.save('search', legacySettings);
115
+ console.log('[SearchProviderFactory] Settings migrated to encrypted database');
116
+
117
+ // Migration successful - delete backup and original
118
+ fs.unlinkSync(backupPath);
119
+ fs.unlinkSync(this.legacySettingsPath);
120
+ console.log('[SearchProviderFactory] Migration complete, cleaned up legacy files');
121
+
122
+ this.migrationCompleted = true;
123
+ } catch (migrationError) {
124
+ console.error('[SearchProviderFactory] Migration failed, backup preserved at:', backupPath);
125
+ throw migrationError;
126
+ }
127
+ } catch (error) {
128
+ console.error('[SearchProviderFactory] Migration failed:', error);
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Get the path to legacy settings file (for testing)
134
+ */
135
+ static getSettingsPath(): string {
136
+ return this.legacySettingsPath;
137
+ }
138
+
139
+ /**
140
+ * Load settings from encrypted database
141
+ */
142
+ static loadSettings(): SearchSettings {
143
+ if (this.cachedSettings) {
144
+ return this.cachedSettings;
145
+ }
146
+
147
+ let settings: SearchSettings = { ...DEFAULT_SETTINGS };
148
+
149
+ try {
150
+ // Try to load from encrypted database
151
+ if (SecureSettingsRepository.isInitialized()) {
152
+ const repository = SecureSettingsRepository.getInstance();
153
+ const stored = repository.load<SearchSettings>('search');
154
+ if (stored) {
155
+ settings = { ...DEFAULT_SETTINGS, ...stored };
156
+ }
157
+ }
158
+ } catch (error) {
159
+ console.error('[SearchProviderFactory] Failed to load settings from database:', error);
160
+ }
161
+
162
+ // Auto-detect and select providers if primaryProvider is not set
163
+ if (!settings.primaryProvider) {
164
+ const configuredProviders = this.getConfiguredProvidersFromSettings(settings);
165
+ if (configuredProviders.length > 0) {
166
+ settings.primaryProvider = configuredProviders[0];
167
+ console.log(`[SearchProviderFactory] Auto-selected primary provider: ${configuredProviders[0]}`);
168
+ if (configuredProviders.length > 1 && !settings.fallbackProvider) {
169
+ settings.fallbackProvider = configuredProviders[1];
170
+ console.log(`[SearchProviderFactory] Auto-selected fallback provider: ${configuredProviders[1]}`);
171
+ }
172
+ }
173
+ }
174
+
175
+ this.cachedSettings = settings;
176
+ return settings;
177
+ }
178
+
179
+ /**
180
+ * Get list of configured provider types from settings only
181
+ * Note: Environment variables are no longer used for security reasons.
182
+ */
183
+ private static getConfiguredProvidersFromSettings(settings: SearchSettings): SearchProviderType[] {
184
+ const configured: SearchProviderType[] = [];
185
+
186
+ // Check Tavily
187
+ if (settings.tavily?.apiKey) {
188
+ configured.push('tavily');
189
+ }
190
+ // Check Brave
191
+ if (settings.brave?.apiKey) {
192
+ configured.push('brave');
193
+ }
194
+ // Check SerpAPI
195
+ if (settings.serpapi?.apiKey) {
196
+ configured.push('serpapi');
197
+ }
198
+ // Check Google (requires both API key and Search Engine ID)
199
+ if (settings.google?.apiKey && settings.google?.searchEngineId) {
200
+ configured.push('google');
201
+ }
202
+
203
+ return configured;
204
+ }
205
+
206
+ /**
207
+ * Save settings to encrypted database
208
+ */
209
+ static saveSettings(settings: SearchSettings): void {
210
+ try {
211
+ if (!SecureSettingsRepository.isInitialized()) {
212
+ throw new Error('SecureSettingsRepository not initialized');
213
+ }
214
+
215
+ const repository = SecureSettingsRepository.getInstance();
216
+
217
+ // Load existing settings to preserve API keys that weren't changed
218
+ let existingSettings: SearchSettings = { ...DEFAULT_SETTINGS };
219
+ const stored = repository.load<SearchSettings>('search');
220
+ if (stored) {
221
+ existingSettings = stored;
222
+ }
223
+
224
+ // Merge settings, preserving existing API keys if new ones aren't provided
225
+ const settingsToSave: SearchSettings = {
226
+ primaryProvider: settings.primaryProvider,
227
+ fallbackProvider: settings.fallbackProvider,
228
+ tavily: settings.tavily?.apiKey
229
+ ? settings.tavily
230
+ : existingSettings.tavily,
231
+ brave: settings.brave?.apiKey
232
+ ? settings.brave
233
+ : existingSettings.brave,
234
+ serpapi: settings.serpapi?.apiKey
235
+ ? settings.serpapi
236
+ : existingSettings.serpapi,
237
+ google: settings.google?.apiKey || settings.google?.searchEngineId
238
+ ? { ...existingSettings.google, ...settings.google }
239
+ : existingSettings.google,
240
+ };
241
+
242
+ // Save to encrypted database
243
+ repository.save('search', settingsToSave);
244
+ this.cachedSettings = settingsToSave;
245
+
246
+ console.log('[SearchProviderFactory] Settings saved to encrypted database');
247
+ } catch (error) {
248
+ console.error('[SearchProviderFactory] Failed to save settings:', error);
249
+ throw error;
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Clear cached settings
255
+ */
256
+ static clearCache(): void {
257
+ this.cachedSettings = null;
258
+ }
259
+
260
+ /**
261
+ * Get the config for creating a provider
262
+ * Note: All credentials must be configured via the Settings UI.
263
+ */
264
+ private static getProviderConfig(providerType: SearchProviderType): SearchProviderConfig {
265
+ const settings = this.loadSettings();
266
+ return {
267
+ type: providerType,
268
+ tavilyApiKey: settings.tavily?.apiKey,
269
+ braveApiKey: settings.brave?.apiKey,
270
+ serpApiKey: settings.serpapi?.apiKey,
271
+ googleApiKey: settings.google?.apiKey,
272
+ googleSearchEngineId: settings.google?.searchEngineId,
273
+ };
274
+ }
275
+
276
+ /**
277
+ * Create a provider based on current settings or override
278
+ */
279
+ static createProvider(overrideType?: SearchProviderType): SearchProvider {
280
+ const settings = this.loadSettings();
281
+ const providerType = overrideType || settings.primaryProvider;
282
+
283
+ if (!providerType) {
284
+ throw new Error('No search provider configured');
285
+ }
286
+
287
+ const config = this.getProviderConfig(providerType);
288
+ return this.createProviderFromConfig(config);
289
+ }
290
+
291
+ /**
292
+ * Create provider from explicit config
293
+ */
294
+ static createProviderFromConfig(config: SearchProviderConfig): SearchProvider {
295
+ switch (config.type) {
296
+ case 'tavily':
297
+ return new TavilyProvider(config);
298
+ case 'brave':
299
+ return new BraveProvider(config);
300
+ case 'serpapi':
301
+ return new SerpApiProvider(config);
302
+ case 'google':
303
+ return new GoogleProvider(config);
304
+ default:
305
+ throw new Error(`Unknown search provider type: ${config.type}`);
306
+ }
307
+ }
308
+
309
+ /**
310
+ * Execute a search with automatic fallback on failure
311
+ */
312
+ static async searchWithFallback(query: SearchQuery): Promise<SearchResponse> {
313
+ const settings = this.loadSettings();
314
+ const primaryType = query.provider || settings.primaryProvider;
315
+
316
+ if (!primaryType) {
317
+ throw new Error('No search provider configured');
318
+ }
319
+
320
+ // Try primary provider
321
+ try {
322
+ const primaryConfig = this.getProviderConfig(primaryType);
323
+ const primaryProvider = this.createProviderFromConfig(primaryConfig);
324
+ return await primaryProvider.search(query);
325
+ } catch (primaryError: any) {
326
+ console.error(`Primary search provider (${primaryType}) failed:`, primaryError.message);
327
+
328
+ // If a specific provider was requested, don't fallback
329
+ if (query.provider) {
330
+ throw primaryError;
331
+ }
332
+
333
+ // Try fallback provider if configured
334
+ const fallbackType = settings.fallbackProvider;
335
+ if (fallbackType && fallbackType !== primaryType) {
336
+ console.log(`Attempting fallback to ${fallbackType}...`);
337
+ try {
338
+ const fallbackConfig = this.getProviderConfig(fallbackType);
339
+ const fallbackProvider = this.createProviderFromConfig(fallbackConfig);
340
+ const response = await fallbackProvider.search(query);
341
+ // Indicate this came from fallback
342
+ console.log(`Fallback search with ${fallbackType} succeeded`);
343
+ return response;
344
+ } catch (fallbackError: any) {
345
+ console.error(`Fallback search provider (${fallbackType}) also failed:`, fallbackError.message);
346
+ // Throw the original error
347
+ throw new Error(
348
+ `Primary provider (${primaryType}) failed: ${primaryError.message}. ` +
349
+ `Fallback provider (${fallbackType}) also failed: ${fallbackError.message}`
350
+ );
351
+ }
352
+ }
353
+
354
+ throw primaryError;
355
+ }
356
+ }
357
+
358
+ /**
359
+ * Get available providers based on saved configuration
360
+ * Note: Environment variables are no longer checked for security reasons.
361
+ */
362
+ static getAvailableProviders(): Array<{
363
+ type: SearchProviderType;
364
+ name: string;
365
+ description: string;
366
+ configured: boolean;
367
+ supportedTypes: SearchType[];
368
+ }> {
369
+ const settings = this.loadSettings();
370
+ return [
371
+ {
372
+ type: 'tavily',
373
+ name: SEARCH_PROVIDER_INFO.tavily.displayName,
374
+ description: SEARCH_PROVIDER_INFO.tavily.description,
375
+ configured: !!settings.tavily?.apiKey,
376
+ supportedTypes: [...SEARCH_PROVIDER_INFO.tavily.supportedTypes],
377
+ },
378
+ {
379
+ type: 'brave',
380
+ name: SEARCH_PROVIDER_INFO.brave.displayName,
381
+ description: SEARCH_PROVIDER_INFO.brave.description,
382
+ configured: !!settings.brave?.apiKey,
383
+ supportedTypes: [...SEARCH_PROVIDER_INFO.brave.supportedTypes],
384
+ },
385
+ {
386
+ type: 'serpapi',
387
+ name: SEARCH_PROVIDER_INFO.serpapi.displayName,
388
+ description: SEARCH_PROVIDER_INFO.serpapi.description,
389
+ configured: !!settings.serpapi?.apiKey,
390
+ supportedTypes: [...SEARCH_PROVIDER_INFO.serpapi.supportedTypes],
391
+ },
392
+ {
393
+ type: 'google',
394
+ name: SEARCH_PROVIDER_INFO.google.displayName,
395
+ description: SEARCH_PROVIDER_INFO.google.description,
396
+ configured: !!(settings.google?.apiKey && settings.google?.searchEngineId),
397
+ supportedTypes: [...SEARCH_PROVIDER_INFO.google.supportedTypes],
398
+ },
399
+ ];
400
+ }
401
+
402
+ /**
403
+ * Check if any search provider is configured
404
+ */
405
+ static isAnyProviderConfigured(): boolean {
406
+ return this.getAvailableProviders().some((p) => p.configured);
407
+ }
408
+
409
+ /**
410
+ * Get current configuration status
411
+ */
412
+ static getConfigStatus(): {
413
+ primaryProvider: SearchProviderType | null;
414
+ fallbackProvider: SearchProviderType | null;
415
+ providers: Array<{
416
+ type: SearchProviderType;
417
+ name: string;
418
+ description: string;
419
+ configured: boolean;
420
+ supportedTypes: SearchType[];
421
+ }>;
422
+ isConfigured: boolean;
423
+ } {
424
+ const settings = this.loadSettings();
425
+ return {
426
+ primaryProvider: settings.primaryProvider,
427
+ fallbackProvider: settings.fallbackProvider,
428
+ providers: this.getAvailableProviders(),
429
+ isConfigured: this.isAnyProviderConfigured(),
430
+ };
431
+ }
432
+
433
+ /**
434
+ * Test a provider configuration
435
+ */
436
+ static async testProvider(
437
+ providerType: SearchProviderType
438
+ ): Promise<{ success: boolean; error?: string }> {
439
+ try {
440
+ const config = this.getProviderConfig(providerType);
441
+ const provider = this.createProviderFromConfig(config);
442
+ return await provider.testConnection();
443
+ } catch (error: any) {
444
+ return {
445
+ success: false,
446
+ error: error.message || 'Failed to create provider',
447
+ };
448
+ }
449
+ }
450
+ }
@@ -0,0 +1,138 @@
1
+ import {
2
+ SearchProvider,
3
+ SearchProviderConfig,
4
+ SearchQuery,
5
+ SearchResponse,
6
+ SearchResult,
7
+ SearchType,
8
+ } from './types';
9
+
10
+ /**
11
+ * SerpAPI provider - aggregates multiple search engines
12
+ * https://serpapi.com/
13
+ */
14
+ export class SerpApiProvider implements SearchProvider {
15
+ readonly type = 'serpapi' as const;
16
+ readonly supportedSearchTypes: SearchType[] = ['web', 'news', 'images'];
17
+
18
+ private apiKey: string;
19
+ private baseUrl = 'https://serpapi.com/search.json';
20
+
21
+ constructor(config: SearchProviderConfig) {
22
+ const apiKey = config.serpApiKey;
23
+ if (!apiKey) {
24
+ throw new Error('SerpAPI key is required. Configure it in Settings or get one from https://serpapi.com/');
25
+ }
26
+ this.apiKey = apiKey;
27
+ }
28
+
29
+ async search(query: SearchQuery): Promise<SearchResponse> {
30
+ const searchType = query.searchType || 'web';
31
+
32
+ if (!this.supportedSearchTypes.includes(searchType)) {
33
+ throw new Error(`SerpAPI does not support ${searchType} search`);
34
+ }
35
+
36
+ const params = new URLSearchParams({
37
+ api_key: this.apiKey,
38
+ q: query.query,
39
+ engine: 'google',
40
+ num: String(query.maxResults || 10),
41
+ ...(query.region && { gl: query.region }),
42
+ ...(query.language && { hl: query.language }),
43
+ ...(query.safeSearch !== undefined && {
44
+ safe: query.safeSearch ? 'active' : 'off',
45
+ }),
46
+ ...(searchType === 'images' && { tbm: 'isch' }),
47
+ ...(searchType === 'news' && { tbm: 'nws' }),
48
+ ...(query.dateRange && { tbs: this.mapDateRange(query.dateRange) }),
49
+ });
50
+
51
+ const response = await fetch(`${this.baseUrl}?${params}`);
52
+
53
+ if (!response.ok) {
54
+ const error = await response.text();
55
+ throw new Error(`SerpAPI error: ${response.status} - ${error}`);
56
+ }
57
+
58
+ const data = await response.json() as {
59
+ error?: string;
60
+ search_information?: { total_results?: number };
61
+ organic_results?: any[];
62
+ news_results?: any[];
63
+ images_results?: any[];
64
+ };
65
+
66
+ if (data.error) {
67
+ throw new Error(`SerpAPI error: ${data.error}`);
68
+ }
69
+
70
+ return {
71
+ results: this.mapResults(data, searchType),
72
+ query: query.query,
73
+ searchType,
74
+ totalResults: data.search_information?.total_results,
75
+ provider: 'serpapi',
76
+ };
77
+ }
78
+
79
+ async testConnection(): Promise<{ success: boolean; error?: string }> {
80
+ try {
81
+ await this.search({ query: 'test', maxResults: 1 });
82
+ return { success: true };
83
+ } catch (error: any) {
84
+ return {
85
+ success: false,
86
+ error: error.message || 'Failed to connect to SerpAPI',
87
+ };
88
+ }
89
+ }
90
+
91
+ private mapDateRange(range: string): string {
92
+ switch (range) {
93
+ case 'day':
94
+ return 'qdr:d';
95
+ case 'week':
96
+ return 'qdr:w';
97
+ case 'month':
98
+ return 'qdr:m';
99
+ case 'year':
100
+ return 'qdr:y';
101
+ default:
102
+ return 'qdr:w';
103
+ }
104
+ }
105
+
106
+ private mapResults(data: any, searchType: SearchType): SearchResult[] {
107
+ if (searchType === 'images') {
108
+ return (data.images_results || []).map((r: any) => ({
109
+ title: r.title || '',
110
+ url: r.link || r.original || '',
111
+ snippet: r.snippet || '',
112
+ thumbnailUrl: r.thumbnail,
113
+ imageUrl: r.original,
114
+ width: r.original_width,
115
+ height: r.original_height,
116
+ source: r.source,
117
+ }));
118
+ }
119
+
120
+ if (searchType === 'news') {
121
+ return (data.news_results || []).map((r: any) => ({
122
+ title: r.title || '',
123
+ url: r.link || '',
124
+ snippet: r.snippet || '',
125
+ publishedDate: r.date,
126
+ source: r.source,
127
+ }));
128
+ }
129
+
130
+ // Web (organic) results
131
+ return (data.organic_results || []).map((r: any) => ({
132
+ title: r.title || '',
133
+ url: r.link || '',
134
+ snippet: r.snippet || '',
135
+ source: r.displayed_link,
136
+ }));
137
+ }
138
+ }
@@ -0,0 +1,108 @@
1
+ import {
2
+ SearchProvider,
3
+ SearchProviderConfig,
4
+ SearchQuery,
5
+ SearchResponse,
6
+ SearchResult,
7
+ SearchType,
8
+ } from './types';
9
+
10
+ /**
11
+ * Tavily Search API provider
12
+ * https://docs.tavily.com/
13
+ */
14
+ export class TavilyProvider implements SearchProvider {
15
+ readonly type = 'tavily' as const;
16
+ readonly supportedSearchTypes: SearchType[] = ['web', 'news'];
17
+
18
+ private apiKey: string;
19
+ private baseUrl = 'https://api.tavily.com';
20
+
21
+ constructor(config: SearchProviderConfig) {
22
+ const apiKey = config.tavilyApiKey;
23
+ if (!apiKey) {
24
+ throw new Error('Tavily API key is required. Configure it in Settings or get one from https://tavily.com/');
25
+ }
26
+ this.apiKey = apiKey;
27
+ }
28
+
29
+ async search(query: SearchQuery): Promise<SearchResponse> {
30
+ const searchType = query.searchType || 'web';
31
+
32
+ if (!this.supportedSearchTypes.includes(searchType)) {
33
+ throw new Error(
34
+ `Tavily does not support ${searchType} search. Supported: ${this.supportedSearchTypes.join(', ')}`
35
+ );
36
+ }
37
+
38
+ const response = await fetch(`${this.baseUrl}/search`, {
39
+ method: 'POST',
40
+ headers: {
41
+ 'Content-Type': 'application/json',
42
+ },
43
+ body: JSON.stringify({
44
+ api_key: this.apiKey,
45
+ query: query.query,
46
+ search_depth: 'advanced',
47
+ max_results: query.maxResults || 10,
48
+ include_answer: false,
49
+ include_raw_content: false,
50
+ // Tavily-specific: topic for news
51
+ topic: searchType === 'news' ? 'news' : 'general',
52
+ // Date filter if specified
53
+ ...(query.dateRange && { days: this.dateRangeToDays(query.dateRange) }),
54
+ }),
55
+ });
56
+
57
+ if (!response.ok) {
58
+ const error = await response.text();
59
+ throw new Error(`Tavily API error: ${response.status} - ${error}`);
60
+ }
61
+
62
+ const data = await response.json() as { results?: any[] };
63
+
64
+ return {
65
+ results: this.mapResults(data.results || []),
66
+ query: query.query,
67
+ searchType,
68
+ provider: 'tavily',
69
+ };
70
+ }
71
+
72
+ async testConnection(): Promise<{ success: boolean; error?: string }> {
73
+ try {
74
+ await this.search({ query: 'test', maxResults: 1 });
75
+ return { success: true };
76
+ } catch (error: any) {
77
+ return {
78
+ success: false,
79
+ error: error.message || 'Failed to connect to Tavily API',
80
+ };
81
+ }
82
+ }
83
+
84
+ private mapResults(results: any[]): SearchResult[] {
85
+ return results.map((r) => ({
86
+ title: r.title || '',
87
+ url: r.url || '',
88
+ snippet: r.content || r.snippet || '',
89
+ publishedDate: r.published_date,
90
+ source: r.source,
91
+ }));
92
+ }
93
+
94
+ private dateRangeToDays(range: string): number {
95
+ switch (range) {
96
+ case 'day':
97
+ return 1;
98
+ case 'week':
99
+ return 7;
100
+ case 'month':
101
+ return 30;
102
+ case 'year':
103
+ return 365;
104
+ default:
105
+ return 7;
106
+ }
107
+ }
108
+ }