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,878 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.DocumentBuilder = void 0;
40
+ const fs = __importStar(require("fs"));
41
+ const fsPromises = __importStar(require("fs/promises"));
42
+ const path = __importStar(require("path"));
43
+ const docx_1 = require("docx");
44
+ const pdfkit_1 = __importDefault(require("pdfkit"));
45
+ const mammoth = __importStar(require("mammoth"));
46
+ const jszip_1 = __importDefault(require("jszip"));
47
+ /**
48
+ * DocumentBuilder creates Word documents (.docx) and PDFs using docx and pdfkit
49
+ */
50
+ class DocumentBuilder {
51
+ constructor(workspace) {
52
+ this.workspace = workspace;
53
+ }
54
+ async create(outputPath, format, content, options = {}) {
55
+ // Normalize content to always be an array
56
+ const normalizedContent = this.normalizeContent(content);
57
+ const ext = path.extname(outputPath).toLowerCase();
58
+ // Allow format override via extension
59
+ if (ext === '.md' || format === 'md') {
60
+ await this.createMarkdown(outputPath, normalizedContent);
61
+ return;
62
+ }
63
+ if (ext === '.pdf' || format === 'pdf') {
64
+ await this.createPDF(outputPath, normalizedContent, options);
65
+ return;
66
+ }
67
+ // Default to Word document
68
+ await this.createDocx(outputPath, normalizedContent, options);
69
+ }
70
+ /**
71
+ * Normalizes content input to always be an array of ContentBlocks
72
+ * Throws an error if content is empty or invalid to prevent creating empty documents
73
+ */
74
+ normalizeContent(content) {
75
+ // Handle undefined/null - FAIL instead of creating empty document
76
+ if (!content) {
77
+ throw new Error('Document content is required. Please provide content as an array of blocks ' +
78
+ '(e.g., [{ type: "paragraph", text: "Your text here" }]) or as a string.');
79
+ }
80
+ // Handle string input - convert to a single paragraph
81
+ if (typeof content === 'string') {
82
+ if (content.trim().length === 0) {
83
+ throw new Error('Document content cannot be empty. Please provide text content.');
84
+ }
85
+ return [{ type: 'paragraph', text: content }];
86
+ }
87
+ // Handle single object (not an array)
88
+ if (!Array.isArray(content)) {
89
+ if (!content.text || content.text.trim().length === 0) {
90
+ throw new Error('Content block must have non-empty text. ' +
91
+ `Received block with type "${content.type}" but empty or missing text.`);
92
+ }
93
+ return [content];
94
+ }
95
+ // Already an array - ensure it's not empty
96
+ if (content.length === 0) {
97
+ throw new Error('Document content array cannot be empty. ' +
98
+ 'Please provide at least one content block (e.g., [{ type: "paragraph", text: "Your text" }]).');
99
+ }
100
+ // Validate each block has content
101
+ const emptyBlocks = content.filter(block => !block.text || block.text.trim().length === 0);
102
+ if (emptyBlocks.length > 0) {
103
+ console.warn(`[DocumentBuilder] Found ${emptyBlocks.length} empty content blocks, filtering them out`);
104
+ const validBlocks = content.filter(block => block.text && block.text.trim().length > 0);
105
+ if (validBlocks.length === 0) {
106
+ throw new Error('All content blocks have empty text. Please provide content blocks with actual text. ' +
107
+ `Received ${content.length} blocks but all had empty or missing text fields.`);
108
+ }
109
+ return validBlocks;
110
+ }
111
+ return content;
112
+ }
113
+ /**
114
+ * Creates a Word document (.docx)
115
+ */
116
+ async createDocx(outputPath, content, options) {
117
+ const children = [];
118
+ for (const block of content) {
119
+ switch (block.type) {
120
+ case 'heading': {
121
+ const level = Math.min(Math.max(block.level || 1, 1), 6);
122
+ const headingLevel = this.getHeadingLevel(level);
123
+ children.push(new docx_1.Paragraph({
124
+ text: block.text,
125
+ heading: headingLevel,
126
+ spacing: { before: 240, after: 120 }
127
+ }));
128
+ break;
129
+ }
130
+ case 'paragraph':
131
+ children.push(new docx_1.Paragraph({
132
+ children: [new docx_1.TextRun({ text: block.text, size: (options.fontSize || 12) * 2 })],
133
+ spacing: { after: 200 }
134
+ }));
135
+ break;
136
+ case 'list': {
137
+ const items = block.items || block.text.split('\n').filter(line => line.trim());
138
+ for (const item of items) {
139
+ children.push(new docx_1.Paragraph({
140
+ children: [new docx_1.TextRun({ text: item, size: (options.fontSize || 12) * 2 })],
141
+ bullet: { level: 0 },
142
+ spacing: { after: 100 }
143
+ }));
144
+ }
145
+ break;
146
+ }
147
+ case 'table': {
148
+ if (block.rows && block.rows.length > 0) {
149
+ const table = new docx_1.Table({
150
+ width: { size: 100, type: docx_1.WidthType.PERCENTAGE },
151
+ rows: block.rows.map((row, rowIndex) => new docx_1.TableRow({
152
+ children: row.map(cell => new docx_1.TableCell({
153
+ children: [
154
+ new docx_1.Paragraph({
155
+ children: [
156
+ new docx_1.TextRun({
157
+ text: cell,
158
+ bold: rowIndex === 0,
159
+ size: (options.fontSize || 12) * 2
160
+ })
161
+ ]
162
+ })
163
+ ],
164
+ borders: {
165
+ top: { style: docx_1.BorderStyle.SINGLE, size: 1 },
166
+ bottom: { style: docx_1.BorderStyle.SINGLE, size: 1 },
167
+ left: { style: docx_1.BorderStyle.SINGLE, size: 1 },
168
+ right: { style: docx_1.BorderStyle.SINGLE, size: 1 }
169
+ }
170
+ }))
171
+ }))
172
+ });
173
+ children.push(new docx_1.Paragraph({ children: [] })); // Spacing before table
174
+ children.push(table);
175
+ children.push(new docx_1.Paragraph({ children: [] })); // Spacing after table
176
+ }
177
+ break;
178
+ }
179
+ case 'code':
180
+ children.push(new docx_1.Paragraph({
181
+ children: [
182
+ new docx_1.TextRun({
183
+ text: block.text,
184
+ font: 'Courier New',
185
+ size: 20, // 10pt
186
+ shading: { fill: 'F0F0F0' }
187
+ })
188
+ ],
189
+ spacing: { before: 200, after: 200 }
190
+ }));
191
+ break;
192
+ default:
193
+ children.push(new docx_1.Paragraph({
194
+ children: [new docx_1.TextRun({ text: block.text, size: (options.fontSize || 12) * 2 })]
195
+ }));
196
+ }
197
+ }
198
+ const doc = new docx_1.Document({
199
+ creator: options.author || 'CoWork OS',
200
+ title: options.title,
201
+ subject: options.subject,
202
+ sections: [
203
+ {
204
+ properties: {
205
+ page: {
206
+ margin: {
207
+ top: (options.margins?.top || 1) * 1440, // Convert inches to twips
208
+ bottom: (options.margins?.bottom || 1) * 1440,
209
+ left: (options.margins?.left || 1) * 1440,
210
+ right: (options.margins?.right || 1) * 1440
211
+ }
212
+ }
213
+ },
214
+ children
215
+ }
216
+ ]
217
+ });
218
+ const buffer = await docx_1.Packer.toBuffer(doc);
219
+ await fsPromises.writeFile(outputPath, buffer);
220
+ }
221
+ /**
222
+ * Creates a PDF document
223
+ */
224
+ async createPDF(outputPath, content, options) {
225
+ return new Promise((resolve, reject) => {
226
+ const doc = new pdfkit_1.default({
227
+ size: 'LETTER',
228
+ margins: {
229
+ top: (options.margins?.top || 1) * 72,
230
+ bottom: (options.margins?.bottom || 1) * 72,
231
+ left: (options.margins?.left || 1) * 72,
232
+ right: (options.margins?.right || 1) * 72
233
+ },
234
+ info: {
235
+ Title: options.title || '',
236
+ Author: options.author || 'CoWork OS',
237
+ Subject: options.subject || ''
238
+ }
239
+ });
240
+ const stream = fs.createWriteStream(outputPath);
241
+ doc.pipe(stream);
242
+ const baseFontSize = options.fontSize || 12;
243
+ for (const block of content) {
244
+ switch (block.type) {
245
+ case 'heading': {
246
+ const level = Math.min(Math.max(block.level || 1, 1), 6);
247
+ const fontSize = baseFontSize + (7 - level) * 2; // h1 = base+12, h6 = base+2
248
+ doc
249
+ .font('Helvetica-Bold')
250
+ .fontSize(fontSize)
251
+ .text(block.text, { paragraphGap: 10 });
252
+ doc.moveDown(0.5);
253
+ break;
254
+ }
255
+ case 'paragraph':
256
+ doc
257
+ .font('Helvetica')
258
+ .fontSize(baseFontSize)
259
+ .text(block.text, { paragraphGap: 8, lineGap: 4 });
260
+ doc.moveDown(0.5);
261
+ break;
262
+ case 'list': {
263
+ const items = block.items || block.text.split('\n').filter(line => line.trim());
264
+ doc.font('Helvetica').fontSize(baseFontSize);
265
+ for (const item of items) {
266
+ doc.text(`• ${item}`, { indent: 20, paragraphGap: 4 });
267
+ }
268
+ doc.moveDown(0.5);
269
+ break;
270
+ }
271
+ case 'table': {
272
+ if (block.rows && block.rows.length > 0) {
273
+ doc.font('Helvetica').fontSize(baseFontSize - 1);
274
+ const columnCount = block.rows[0].length;
275
+ const pageWidth = doc.page.width - doc.page.margins.left - doc.page.margins.right;
276
+ const colWidth = pageWidth / columnCount;
277
+ for (let rowIndex = 0; rowIndex < block.rows.length; rowIndex++) {
278
+ const row = block.rows[rowIndex];
279
+ const startY = doc.y;
280
+ // Draw cells
281
+ for (let colIndex = 0; colIndex < row.length; colIndex++) {
282
+ const x = doc.page.margins.left + colIndex * colWidth;
283
+ doc.font(rowIndex === 0 ? 'Helvetica-Bold' : 'Helvetica');
284
+ doc.text(row[colIndex], x, startY, {
285
+ width: colWidth - 10,
286
+ continued: false
287
+ });
288
+ }
289
+ // Draw horizontal line
290
+ doc
291
+ .moveTo(doc.page.margins.left, doc.y + 5)
292
+ .lineTo(doc.page.margins.left + pageWidth, doc.y + 5)
293
+ .stroke();
294
+ doc.moveDown(0.3);
295
+ }
296
+ doc.moveDown(0.5);
297
+ }
298
+ break;
299
+ }
300
+ case 'code':
301
+ doc
302
+ .font('Courier')
303
+ .fontSize(baseFontSize - 2)
304
+ .fillColor('#333333')
305
+ .text(block.text, { paragraphGap: 8 });
306
+ doc.fillColor('#000000');
307
+ doc.moveDown(0.5);
308
+ break;
309
+ default:
310
+ doc
311
+ .font('Helvetica')
312
+ .fontSize(baseFontSize)
313
+ .text(block.text);
314
+ doc.moveDown(0.5);
315
+ }
316
+ }
317
+ doc.end();
318
+ stream.on('finish', resolve);
319
+ stream.on('error', reject);
320
+ });
321
+ }
322
+ /**
323
+ * Creates a Markdown document (fallback)
324
+ */
325
+ async createMarkdown(outputPath, content) {
326
+ const markdown = content
327
+ .map(block => {
328
+ switch (block.type) {
329
+ case 'heading': {
330
+ const level = Math.min(Math.max(block.level || 1, 1), 6);
331
+ return `${'#'.repeat(level)} ${block.text}\n`;
332
+ }
333
+ case 'paragraph':
334
+ return `${block.text}\n`;
335
+ case 'list': {
336
+ const items = block.items || block.text.split('\n').filter(line => line.trim());
337
+ return items.map(item => `- ${item}`).join('\n') + '\n';
338
+ }
339
+ case 'table': {
340
+ if (!block.rows || block.rows.length === 0)
341
+ return '';
342
+ const header = block.rows[0];
343
+ const separator = header.map(() => '---').join(' | ');
344
+ const rows = block.rows.map(row => row.join(' | ')).join('\n');
345
+ return `${header.join(' | ')}\n${separator}\n${block.rows.slice(1).map(row => row.join(' | ')).join('\n')}\n`;
346
+ }
347
+ case 'code':
348
+ return `\`\`\`${block.language || ''}\n${block.text}\n\`\`\`\n`;
349
+ default:
350
+ return `${block.text}\n`;
351
+ }
352
+ })
353
+ .join('\n');
354
+ await fsPromises.writeFile(outputPath, markdown, 'utf-8');
355
+ }
356
+ getHeadingLevel(level) {
357
+ switch (level) {
358
+ case 1: return docx_1.HeadingLevel.HEADING_1;
359
+ case 2: return docx_1.HeadingLevel.HEADING_2;
360
+ case 3: return docx_1.HeadingLevel.HEADING_3;
361
+ case 4: return docx_1.HeadingLevel.HEADING_4;
362
+ case 5: return docx_1.HeadingLevel.HEADING_5;
363
+ case 6: return docx_1.HeadingLevel.HEADING_6;
364
+ default: return docx_1.HeadingLevel.HEADING_1;
365
+ }
366
+ }
367
+ /**
368
+ * Reads an existing DOCX file and extracts its content as HTML
369
+ */
370
+ async readDocument(inputPath) {
371
+ const buffer = await fsPromises.readFile(inputPath);
372
+ const result = await mammoth.convertToHtml({ buffer });
373
+ const textResult = await mammoth.extractRawText({ buffer });
374
+ return {
375
+ html: result.value,
376
+ text: textResult.value,
377
+ messages: result.messages.map(m => m.message)
378
+ };
379
+ }
380
+ /**
381
+ * Appends new content sections to an existing DOCX file.
382
+ * This method directly manipulates the DOCX XML structure to preserve
383
+ * the original document formatting while adding new content at the end.
384
+ */
385
+ async appendToDocument(inputPath, outputPath, newContent, options = {}) {
386
+ console.log(`[DocumentBuilder] appendToDocument: ${inputPath} -> ${outputPath}, ${newContent.length} blocks`);
387
+ // Read the DOCX file as a ZIP
388
+ const docxBuffer = await fsPromises.readFile(inputPath);
389
+ const zip = await jszip_1.default.loadAsync(docxBuffer);
390
+ // Get the main document.xml
391
+ const documentXml = zip.file('word/document.xml');
392
+ if (!documentXml) {
393
+ throw new Error('Invalid DOCX file: missing word/document.xml');
394
+ }
395
+ let xmlContent = await documentXml.async('text');
396
+ // Generate OOXML for the new content
397
+ const newXmlContent = this.contentBlocksToOoxml(newContent);
398
+ // Find the insertion point - before </w:body> or before <w:sectPr
399
+ // The sectPr element contains section properties and must stay at the end
400
+ const sectPrMatch = xmlContent.match(/<w:sectPr[^>]*>[\s\S]*?<\/w:sectPr>/);
401
+ const bodyEndMatch = xmlContent.match(/<\/w:body>/);
402
+ if (sectPrMatch && sectPrMatch.index !== undefined) {
403
+ // Insert before sectPr
404
+ xmlContent =
405
+ xmlContent.slice(0, sectPrMatch.index) +
406
+ newXmlContent +
407
+ xmlContent.slice(sectPrMatch.index);
408
+ console.log(`[DocumentBuilder] Inserted content before <w:sectPr>`);
409
+ }
410
+ else if (bodyEndMatch && bodyEndMatch.index !== undefined) {
411
+ // Insert before </w:body>
412
+ xmlContent =
413
+ xmlContent.slice(0, bodyEndMatch.index) +
414
+ newXmlContent +
415
+ xmlContent.slice(bodyEndMatch.index);
416
+ console.log(`[DocumentBuilder] Inserted content before </w:body>`);
417
+ }
418
+ else {
419
+ throw new Error('Could not find insertion point in document.xml');
420
+ }
421
+ // Update the document.xml in the ZIP
422
+ zip.file('word/document.xml', xmlContent);
423
+ // Write the modified DOCX
424
+ const outputBuffer = await zip.generateAsync({
425
+ type: 'nodebuffer',
426
+ compression: 'DEFLATE',
427
+ compressionOptions: { level: 9 }
428
+ });
429
+ await fsPromises.writeFile(outputPath, outputBuffer);
430
+ console.log(`[DocumentBuilder] Successfully appended ${newContent.length} sections to ${outputPath}`);
431
+ return {
432
+ success: true,
433
+ sectionsAdded: newContent.length
434
+ };
435
+ }
436
+ /**
437
+ * Converts ContentBlocks to OOXML (Office Open XML) format
438
+ * This creates proper Word paragraph/table elements
439
+ */
440
+ contentBlocksToOoxml(blocks) {
441
+ const xmlParts = [];
442
+ for (const block of blocks) {
443
+ switch (block.type) {
444
+ case 'heading': {
445
+ const level = Math.min(Math.max(block.level || 1, 1), 6);
446
+ // Word heading styles are "Heading1" through "Heading6"
447
+ const styleId = `Heading${level}`;
448
+ xmlParts.push(this.createOoxmlParagraph(block.text, styleId));
449
+ break;
450
+ }
451
+ case 'paragraph':
452
+ xmlParts.push(this.createOoxmlParagraph(block.text));
453
+ break;
454
+ case 'list': {
455
+ const items = block.items || block.text.split('\n').filter(line => line.trim());
456
+ for (const item of items) {
457
+ xmlParts.push(this.createOoxmlListItem(item));
458
+ }
459
+ break;
460
+ }
461
+ case 'table': {
462
+ if (block.rows && block.rows.length > 0) {
463
+ xmlParts.push(this.createOoxmlTable(block.rows));
464
+ }
465
+ break;
466
+ }
467
+ default:
468
+ xmlParts.push(this.createOoxmlParagraph(block.text));
469
+ }
470
+ }
471
+ return xmlParts.join('\n');
472
+ }
473
+ /**
474
+ * Creates an OOXML paragraph element
475
+ */
476
+ createOoxmlParagraph(text, styleId) {
477
+ const escapedText = this.escapeXml(text);
478
+ const styleXml = styleId ? `<w:pPr><w:pStyle w:val="${styleId}"/></w:pPr>` : '';
479
+ return `<w:p>${styleXml}<w:r><w:t>${escapedText}</w:t></w:r></w:p>`;
480
+ }
481
+ /**
482
+ * Creates an OOXML list item (bullet point)
483
+ */
484
+ createOoxmlListItem(text) {
485
+ const escapedText = this.escapeXml(text);
486
+ // Simple bullet using a bullet character - more compatible than numPr
487
+ return `<w:p><w:pPr><w:ind w:left="720"/></w:pPr><w:r><w:t>• ${escapedText}</w:t></w:r></w:p>`;
488
+ }
489
+ /**
490
+ * Creates an OOXML table element
491
+ */
492
+ createOoxmlTable(rows) {
493
+ const tableRows = rows.map((row, rowIndex) => {
494
+ const cells = row.map(cellText => {
495
+ const escapedText = this.escapeXml(cellText);
496
+ const boldStyle = rowIndex === 0 ? '<w:rPr><w:b/></w:rPr>' : '';
497
+ return `<w:tc><w:tcPr><w:tcW w:w="0" w:type="auto"/><w:tcBorders><w:top w:val="single" w:sz="4"/><w:left w:val="single" w:sz="4"/><w:bottom w:val="single" w:sz="4"/><w:right w:val="single" w:sz="4"/></w:tcBorders></w:tcPr><w:p><w:r>${boldStyle}<w:t>${escapedText}</w:t></w:r></w:p></w:tc>`;
498
+ }).join('');
499
+ return `<w:tr>${cells}</w:tr>`;
500
+ }).join('');
501
+ return `<w:tbl><w:tblPr><w:tblW w:w="5000" w:type="pct"/><w:tblBorders><w:top w:val="single" w:sz="4"/><w:left w:val="single" w:sz="4"/><w:bottom w:val="single" w:sz="4"/><w:right w:val="single" w:sz="4"/><w:insideH w:val="single" w:sz="4"/><w:insideV w:val="single" w:sz="4"/></w:tblBorders></w:tblPr>${tableRows}</w:tbl>`;
502
+ }
503
+ /**
504
+ * Escapes special XML characters
505
+ */
506
+ escapeXml(text) {
507
+ return text
508
+ .replace(/&/g, '&amp;')
509
+ .replace(/</g, '&lt;')
510
+ .replace(/>/g, '&gt;')
511
+ .replace(/"/g, '&quot;')
512
+ .replace(/'/g, '&apos;');
513
+ }
514
+ /**
515
+ * Parses the document.xml content and identifies sections based on headings.
516
+ * Sections are delimited by heading paragraphs (Heading1, Heading2, etc.)
517
+ */
518
+ parseSections(xmlContent) {
519
+ const sections = [];
520
+ // Find all paragraphs that are headings (have w:pStyle with Heading1-6)
521
+ // Pattern: <w:p ...>...<w:pStyle w:val="Heading[1-6]"/>...</w:p>
522
+ const paragraphRegex = /<w:p\b[^>]*>[\s\S]*?<\/w:p>/g;
523
+ const headingStyleRegex = /<w:pStyle\s+w:val="Heading([1-6])"\s*\/>/;
524
+ const textRegex = /<w:t[^>]*>([^<]*)<\/w:t>/g;
525
+ let match;
526
+ const headingPositions = [];
527
+ // Find all heading paragraphs
528
+ while ((match = paragraphRegex.exec(xmlContent)) !== null) {
529
+ const paragraph = match[0];
530
+ const styleMatch = paragraph.match(headingStyleRegex);
531
+ if (styleMatch) {
532
+ const level = parseInt(styleMatch[1], 10);
533
+ // Extract text from the paragraph
534
+ let text = '';
535
+ let textMatch;
536
+ const textRegexLocal = /<w:t[^>]*>([^<]*)<\/w:t>/g;
537
+ while ((textMatch = textRegexLocal.exec(paragraph)) !== null) {
538
+ text += textMatch[1];
539
+ }
540
+ // Try to extract section number (e.g., "8. " or "8 ")
541
+ const sectionNumMatch = text.match(/^(\d+(?:\.\d+)*)[.\s]/);
542
+ const sectionNumber = sectionNumMatch ? sectionNumMatch[1] : undefined;
543
+ headingPositions.push({
544
+ level,
545
+ text: text.trim(),
546
+ sectionNumber,
547
+ startIndex: match.index,
548
+ endIndex: match.index + paragraph.length
549
+ });
550
+ }
551
+ }
552
+ // Now create sections from heading positions
553
+ // Each section spans from its heading to the next same-level or higher-level heading
554
+ for (let i = 0; i < headingPositions.length; i++) {
555
+ const current = headingPositions[i];
556
+ let endIndex;
557
+ // Find the end of this section
558
+ // It ends at the next heading of same or higher level (lower number)
559
+ // Or at the sectPr element, or end of body
560
+ let nextSectionStart;
561
+ for (let j = i + 1; j < headingPositions.length; j++) {
562
+ if (headingPositions[j].level <= current.level) {
563
+ nextSectionStart = headingPositions[j].startIndex;
564
+ break;
565
+ }
566
+ }
567
+ if (nextSectionStart !== undefined) {
568
+ endIndex = nextSectionStart;
569
+ }
570
+ else {
571
+ // This is the last section at this level
572
+ // End at sectPr or end of body
573
+ const sectPrMatch = xmlContent.match(/<w:sectPr[^>]*>/);
574
+ const bodyEndMatch = xmlContent.match(/<\/w:body>/);
575
+ if (sectPrMatch && sectPrMatch.index !== undefined) {
576
+ endIndex = sectPrMatch.index;
577
+ }
578
+ else if (bodyEndMatch && bodyEndMatch.index !== undefined) {
579
+ endIndex = bodyEndMatch.index;
580
+ }
581
+ else {
582
+ endIndex = xmlContent.length;
583
+ }
584
+ }
585
+ sections.push({
586
+ headingLevel: current.level,
587
+ headingText: current.text,
588
+ sectionNumber: current.sectionNumber,
589
+ startIndex: current.startIndex,
590
+ endIndex,
591
+ xmlContent: xmlContent.slice(current.startIndex, endIndex)
592
+ });
593
+ }
594
+ return sections;
595
+ }
596
+ /**
597
+ * Moves a section to a new position in the document.
598
+ * @param inputPath Path to the source DOCX file
599
+ * @param outputPath Path to save the modified DOCX file
600
+ * @param sectionIdentifier The section to move (can be section number like "8" or heading text)
601
+ * @param afterSection The section after which to place it (section number or heading text)
602
+ */
603
+ async moveSectionAfter(inputPath, outputPath, sectionIdentifier, afterSection) {
604
+ console.log(`[DocumentBuilder] moveSectionAfter: Moving "${sectionIdentifier}" after "${afterSection}"`);
605
+ // Read the DOCX file
606
+ const docxBuffer = await fsPromises.readFile(inputPath);
607
+ const zip = await jszip_1.default.loadAsync(docxBuffer);
608
+ const documentXml = zip.file('word/document.xml');
609
+ if (!documentXml) {
610
+ throw new Error('Invalid DOCX file: missing word/document.xml');
611
+ }
612
+ let xmlContent = await documentXml.async('text');
613
+ // Parse sections
614
+ const sections = this.parseSections(xmlContent);
615
+ console.log(`[DocumentBuilder] Found ${sections.length} sections:`, sections.map(s => `${s.sectionNumber || 'N/A'}: ${s.headingText.substring(0, 50)}`));
616
+ // Find the section to move
617
+ const sectionToMove = this.findSection(sections, sectionIdentifier);
618
+ if (!sectionToMove) {
619
+ return {
620
+ success: false,
621
+ message: `Could not find section "${sectionIdentifier}". Available sections: ${sections.map(s => s.sectionNumber || s.headingText).join(', ')}`
622
+ };
623
+ }
624
+ // Find the target section (after which to insert)
625
+ const targetSection = this.findSection(sections, afterSection);
626
+ if (!targetSection) {
627
+ return {
628
+ success: false,
629
+ message: `Could not find target section "${afterSection}". Available sections: ${sections.map(s => s.sectionNumber || s.headingText).join(', ')}`
630
+ };
631
+ }
632
+ // Check if move is needed
633
+ if (sectionToMove.startIndex === targetSection.endIndex) {
634
+ return { success: true, message: 'Section is already in the correct position' };
635
+ }
636
+ // Perform the move
637
+ const sectionContent = sectionToMove.xmlContent;
638
+ // Remove the section from its current position
639
+ let newXmlContent;
640
+ if (sectionToMove.startIndex > targetSection.endIndex) {
641
+ // Section is after target - remove it first, then insert
642
+ newXmlContent =
643
+ xmlContent.slice(0, sectionToMove.startIndex) +
644
+ xmlContent.slice(sectionToMove.endIndex);
645
+ // Insert at target position (unchanged since it's before the removed section)
646
+ newXmlContent =
647
+ newXmlContent.slice(0, targetSection.endIndex) +
648
+ sectionContent +
649
+ newXmlContent.slice(targetSection.endIndex);
650
+ }
651
+ else {
652
+ // Section is before target - need to adjust indices
653
+ // First, calculate where target ends after section removal
654
+ const sectionLength = sectionToMove.endIndex - sectionToMove.startIndex;
655
+ const adjustedTargetEnd = targetSection.endIndex - sectionLength;
656
+ // Remove section first
657
+ newXmlContent =
658
+ xmlContent.slice(0, sectionToMove.startIndex) +
659
+ xmlContent.slice(sectionToMove.endIndex);
660
+ // Insert at adjusted target position
661
+ newXmlContent =
662
+ newXmlContent.slice(0, adjustedTargetEnd) +
663
+ sectionContent +
664
+ newXmlContent.slice(adjustedTargetEnd);
665
+ }
666
+ // Update the document.xml in the ZIP
667
+ zip.file('word/document.xml', newXmlContent);
668
+ // Write the modified DOCX
669
+ const outputBuffer = await zip.generateAsync({
670
+ type: 'nodebuffer',
671
+ compression: 'DEFLATE',
672
+ compressionOptions: { level: 9 }
673
+ });
674
+ await fsPromises.writeFile(outputPath, outputBuffer);
675
+ console.log(`[DocumentBuilder] Successfully moved section "${sectionIdentifier}" after "${afterSection}"`);
676
+ return {
677
+ success: true,
678
+ message: `Moved section "${sectionToMove.headingText}" after "${targetSection.headingText}"`
679
+ };
680
+ }
681
+ /**
682
+ * Finds a section by its number or heading text
683
+ */
684
+ findSection(sections, identifier) {
685
+ const normalizedId = identifier.trim().toLowerCase();
686
+ // First try exact section number match
687
+ const byNumber = sections.find(s => s.sectionNumber === identifier ||
688
+ s.sectionNumber === normalizedId);
689
+ if (byNumber)
690
+ return byNumber;
691
+ // Try with "Section " prefix
692
+ const withPrefix = sections.find(s => s.headingText.toLowerCase().startsWith(`section ${normalizedId}`) ||
693
+ s.headingText.toLowerCase().startsWith(`${normalizedId}.`) ||
694
+ s.headingText.toLowerCase().startsWith(`${normalizedId} `));
695
+ if (withPrefix)
696
+ return withPrefix;
697
+ // Try partial heading text match
698
+ const byText = sections.find(s => s.headingText.toLowerCase().includes(normalizedId));
699
+ if (byText)
700
+ return byText;
701
+ return undefined;
702
+ }
703
+ /**
704
+ * Inserts new content after a specific section in the document.
705
+ * @param inputPath Path to the source DOCX file
706
+ * @param outputPath Path to save the modified DOCX file
707
+ * @param afterSection Section identifier (number or heading text) after which to insert
708
+ * @param newContent Content blocks to insert
709
+ */
710
+ async insertAfterSection(inputPath, outputPath, afterSection, newContent) {
711
+ console.log(`[DocumentBuilder] insertAfterSection: After "${afterSection}", inserting ${newContent.length} blocks`);
712
+ // Read the DOCX file
713
+ const docxBuffer = await fsPromises.readFile(inputPath);
714
+ const zip = await jszip_1.default.loadAsync(docxBuffer);
715
+ const documentXml = zip.file('word/document.xml');
716
+ if (!documentXml) {
717
+ throw new Error('Invalid DOCX file: missing word/document.xml');
718
+ }
719
+ let xmlContent = await documentXml.async('text');
720
+ // Parse sections
721
+ const sections = this.parseSections(xmlContent);
722
+ // Find the target section
723
+ const targetSection = this.findSection(sections, afterSection);
724
+ if (!targetSection) {
725
+ return {
726
+ success: false,
727
+ message: `Could not find section "${afterSection}". Available sections: ${sections.map(s => s.sectionNumber || s.headingText).join(', ')}`,
728
+ sectionsAdded: 0
729
+ };
730
+ }
731
+ // Generate OOXML for the new content
732
+ const newXmlContent = this.contentBlocksToOoxml(newContent);
733
+ // Insert after the target section
734
+ const insertionPoint = targetSection.endIndex;
735
+ xmlContent =
736
+ xmlContent.slice(0, insertionPoint) +
737
+ newXmlContent +
738
+ xmlContent.slice(insertionPoint);
739
+ // Update the document.xml in the ZIP
740
+ zip.file('word/document.xml', xmlContent);
741
+ // Write the modified DOCX
742
+ const outputBuffer = await zip.generateAsync({
743
+ type: 'nodebuffer',
744
+ compression: 'DEFLATE',
745
+ compressionOptions: { level: 9 }
746
+ });
747
+ await fsPromises.writeFile(outputPath, outputBuffer);
748
+ console.log(`[DocumentBuilder] Successfully inserted ${newContent.length} blocks after section "${afterSection}"`);
749
+ return {
750
+ success: true,
751
+ message: `Inserted ${newContent.length} content blocks after "${targetSection.headingText}"`,
752
+ sectionsAdded: newContent.length
753
+ };
754
+ }
755
+ /**
756
+ * Lists all sections in a document
757
+ */
758
+ async listSections(inputPath) {
759
+ const docxBuffer = await fsPromises.readFile(inputPath);
760
+ const zip = await jszip_1.default.loadAsync(docxBuffer);
761
+ const documentXml = zip.file('word/document.xml');
762
+ if (!documentXml) {
763
+ throw new Error('Invalid DOCX file: missing word/document.xml');
764
+ }
765
+ const xmlContent = await documentXml.async('text');
766
+ const sections = this.parseSections(xmlContent);
767
+ return sections.map(s => ({
768
+ number: s.sectionNumber,
769
+ title: s.headingText,
770
+ level: s.headingLevel
771
+ }));
772
+ }
773
+ /**
774
+ * Converts HTML from mammoth to ContentBlocks
775
+ * This is a simplified conversion that preserves basic structure
776
+ */
777
+ htmlToContentBlocks(html) {
778
+ const blocks = [];
779
+ // Simple regex-based HTML parsing for common elements
780
+ // Match headings
781
+ const headingRegex = /<h([1-6])[^>]*>([\s\S]*?)<\/h\1>/gi;
782
+ // Match paragraphs
783
+ const paragraphRegex = /<p[^>]*>([\s\S]*?)<\/p>/gi;
784
+ // Match list items
785
+ const listRegex = /<ul[^>]*>([\s\S]*?)<\/ul>/gi;
786
+ const listItemRegex = /<li[^>]*>([\s\S]*?)<\/li>/gi;
787
+ // Match tables
788
+ const tableRegex = /<table[^>]*>([\s\S]*?)<\/table>/gi;
789
+ const trRegex = /<tr[^>]*>([\s\S]*?)<\/tr>/gi;
790
+ const tdThRegex = /<t[dh][^>]*>([\s\S]*?)<\/t[dh]>/gi;
791
+ // Helper to strip HTML tags
792
+ const stripTags = (str) => str.replace(/<[^>]*>/g, '').trim();
793
+ // Process in order of appearance
794
+ let lastIndex = 0;
795
+ const processedRanges = [];
796
+ // Find all headings
797
+ let match;
798
+ while ((match = headingRegex.exec(html)) !== null) {
799
+ const text = stripTags(match[2]);
800
+ if (text) {
801
+ blocks.push({
802
+ type: 'heading',
803
+ text,
804
+ level: parseInt(match[1], 10)
805
+ });
806
+ processedRanges.push({ start: match.index, end: match.index + match[0].length });
807
+ }
808
+ }
809
+ // Find all paragraphs
810
+ paragraphRegex.lastIndex = 0;
811
+ while ((match = paragraphRegex.exec(html)) !== null) {
812
+ // Skip if this range overlaps with an already processed element
813
+ const overlaps = processedRanges.some(r => (match.index >= r.start && match.index < r.end) ||
814
+ (match.index + match[0].length > r.start && match.index + match[0].length <= r.end));
815
+ if (overlaps)
816
+ continue;
817
+ const text = stripTags(match[1]);
818
+ if (text) {
819
+ blocks.push({
820
+ type: 'paragraph',
821
+ text
822
+ });
823
+ processedRanges.push({ start: match.index, end: match.index + match[0].length });
824
+ }
825
+ }
826
+ // Find all lists
827
+ listRegex.lastIndex = 0;
828
+ while ((match = listRegex.exec(html)) !== null) {
829
+ const listHtml = match[1];
830
+ const items = [];
831
+ let itemMatch;
832
+ const itemRegex = /<li[^>]*>([\s\S]*?)<\/li>/gi;
833
+ while ((itemMatch = itemRegex.exec(listHtml)) !== null) {
834
+ const itemText = stripTags(itemMatch[1]);
835
+ if (itemText)
836
+ items.push(itemText);
837
+ }
838
+ if (items.length > 0) {
839
+ blocks.push({
840
+ type: 'list',
841
+ text: items.join('\n'),
842
+ items
843
+ });
844
+ }
845
+ }
846
+ // Find all tables
847
+ tableRegex.lastIndex = 0;
848
+ while ((match = tableRegex.exec(html)) !== null) {
849
+ const tableHtml = match[1];
850
+ const rows = [];
851
+ let rowMatch;
852
+ const rowRegex = /<tr[^>]*>([\s\S]*?)<\/tr>/gi;
853
+ while ((rowMatch = rowRegex.exec(tableHtml)) !== null) {
854
+ const rowHtml = rowMatch[1];
855
+ const cells = [];
856
+ let cellMatch;
857
+ const cellRegex = /<t[dh][^>]*>([\s\S]*?)<\/t[dh]>/gi;
858
+ while ((cellMatch = cellRegex.exec(rowHtml)) !== null) {
859
+ cells.push(stripTags(cellMatch[1]));
860
+ }
861
+ if (cells.length > 0)
862
+ rows.push(cells);
863
+ }
864
+ if (rows.length > 0) {
865
+ blocks.push({
866
+ type: 'table',
867
+ text: '',
868
+ rows
869
+ });
870
+ }
871
+ }
872
+ // Sort blocks by their original position would require more complex tracking
873
+ // For now, we return them in the order found (headings, then paragraphs, then lists, then tables)
874
+ // This may not preserve exact document order
875
+ return blocks;
876
+ }
877
+ }
878
+ exports.DocumentBuilder = DocumentBuilder;