wizards 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (308) hide show
  1. package/LICENSE +110 -0
  2. package/README.md +170 -0
  3. package/apps/runtime/dist/agents/architect.js +453 -0
  4. package/apps/runtime/dist/agents/project-assistant.js +354 -0
  5. package/apps/runtime/dist/agents/subscription.js +206 -0
  6. package/apps/runtime/dist/agents/thought.js +21 -0
  7. package/apps/runtime/dist/agents/website.js +197 -0
  8. package/apps/runtime/dist/app.js +153 -0
  9. package/apps/runtime/dist/auth/account.js +121 -0
  10. package/apps/runtime/dist/auth/index.js +329 -0
  11. package/apps/runtime/dist/auth/keys.js +55 -0
  12. package/apps/runtime/dist/auth/local-ticket.js +23 -0
  13. package/apps/runtime/dist/authoring/example.js +73 -0
  14. package/apps/runtime/dist/authoring/guide.js +259 -0
  15. package/apps/runtime/dist/authoring/ops.js +117 -0
  16. package/apps/runtime/dist/cli/autostart.js +134 -0
  17. package/apps/runtime/dist/cli/clients.js +44 -0
  18. package/apps/runtime/dist/cli/environment.js +81 -0
  19. package/apps/runtime/dist/cli/exec.js +45 -0
  20. package/apps/runtime/dist/cli/home.js +74 -0
  21. package/apps/runtime/dist/cli/machine.js +145 -0
  22. package/apps/runtime/dist/cli/main.js +125 -0
  23. package/apps/runtime/dist/cli/setup.js +273 -0
  24. package/apps/runtime/dist/cli/start.js +190 -0
  25. package/apps/runtime/dist/cli/status.js +120 -0
  26. package/apps/runtime/dist/cli/ui.js +12 -0
  27. package/apps/runtime/dist/cli/update.js +58 -0
  28. package/apps/runtime/dist/connectors/builtin.js +70 -0
  29. package/apps/runtime/dist/connectors/external.js +400 -0
  30. package/apps/runtime/dist/connectors/gmail.js +107 -0
  31. package/apps/runtime/dist/connectors/imap.js +217 -0
  32. package/apps/runtime/dist/connectors/index.js +266 -0
  33. package/apps/runtime/dist/connectors/mail.js +72 -0
  34. package/apps/runtime/dist/connectors/outlook.js +90 -0
  35. package/apps/runtime/dist/credits/credits.js +103 -0
  36. package/apps/runtime/dist/credits/estimate.js +132 -0
  37. package/apps/runtime/dist/db/adopt.js +149 -0
  38. package/apps/runtime/dist/db/client.js +146 -0
  39. package/apps/runtime/dist/db/control-schema.js +70 -0
  40. package/apps/runtime/dist/db/migrations/control/0000_last_absorbing_man.sql +47 -0
  41. package/apps/runtime/dist/db/migrations/control/0001_gorgeous_nicolaos.sql +41 -0
  42. package/apps/runtime/dist/db/migrations/control/0002_secret_penance.sql +1 -0
  43. package/apps/runtime/dist/db/migrations/control/0003_nasty_mordo.sql +3 -0
  44. package/apps/runtime/dist/db/migrations/control/0004_breezy_eddie_brock.sql +6 -0
  45. package/apps/runtime/dist/db/migrations/control/meta/0000_snapshot.json +311 -0
  46. package/apps/runtime/dist/db/migrations/control/meta/0001_snapshot.json +599 -0
  47. package/apps/runtime/dist/db/migrations/control/meta/0002_snapshot.json +607 -0
  48. package/apps/runtime/dist/db/migrations/control/meta/0003_snapshot.json +311 -0
  49. package/apps/runtime/dist/db/migrations/control/meta/0004_snapshot.json +350 -0
  50. package/apps/runtime/dist/db/migrations/control/meta/_journal.json +41 -0
  51. package/apps/runtime/dist/db/migrations/tenant/0000_eminent_freak.sql +188 -0
  52. package/apps/runtime/dist/db/migrations/tenant/0001_salty_master_chief.sql +1 -0
  53. package/apps/runtime/dist/db/migrations/tenant/0002_cloudy_firebird.sql +1 -0
  54. package/apps/runtime/dist/db/migrations/tenant/0003_brown_warbound.sql +52 -0
  55. package/apps/runtime/dist/db/migrations/tenant/0004_romantic_nocturne.sql +6 -0
  56. package/apps/runtime/dist/db/migrations/tenant/meta/0000_snapshot.json +1323 -0
  57. package/apps/runtime/dist/db/migrations/tenant/meta/0001_snapshot.json +1330 -0
  58. package/apps/runtime/dist/db/migrations/tenant/meta/0002_snapshot.json +1337 -0
  59. package/apps/runtime/dist/db/migrations/tenant/meta/0003_snapshot.json +1605 -0
  60. package/apps/runtime/dist/db/migrations/tenant/meta/0004_snapshot.json +1645 -0
  61. package/apps/runtime/dist/db/migrations/tenant/meta/_journal.json +41 -0
  62. package/apps/runtime/dist/db/schema.js +278 -0
  63. package/apps/runtime/dist/documents/parse.js +230 -0
  64. package/apps/runtime/dist/engenty/connections-external/build-connector.js +193 -0
  65. package/apps/runtime/dist/engenty/connections-external/errors.js +11 -0
  66. package/apps/runtime/dist/engenty/connections-external/import-service.js +270 -0
  67. package/apps/runtime/dist/engenty/connections-external/importer/classify.js +34 -0
  68. package/apps/runtime/dist/engenty/connections-external/importer/map-auth.js +185 -0
  69. package/apps/runtime/dist/engenty/connections-external/importer/normalize-mcp.js +63 -0
  70. package/apps/runtime/dist/engenty/connections-external/importer/normalize-openapi.js +130 -0
  71. package/apps/runtime/dist/engenty/connections-external/invoke/http-invoker.js +143 -0
  72. package/apps/runtime/dist/engenty/connections-external/invoke/mcp-client.js +143 -0
  73. package/apps/runtime/dist/engenty/connections-external/net/guarded-fetch.js +119 -0
  74. package/apps/runtime/dist/engenty/connections-external/oauth-dcr.js +88 -0
  75. package/apps/runtime/dist/engenty/connections-external/registry-client.js +336 -0
  76. package/apps/runtime/dist/engenty/connections-external/registry-source.js +82 -0
  77. package/apps/runtime/dist/engenty/connections-external/types.js +7 -0
  78. package/apps/runtime/dist/engenty/connections-github/connector.js +209 -0
  79. package/apps/runtime/dist/engenty/connections-google/connectors/calendar.js +359 -0
  80. package/apps/runtime/dist/engenty/connections-google/connectors/contacts.js +94 -0
  81. package/apps/runtime/dist/engenty/connections-google/connectors/drive.js +290 -0
  82. package/apps/runtime/dist/engenty/connections-google/connectors/gmail.js +567 -0
  83. package/apps/runtime/dist/engenty/connections-google/definitions.js +11 -0
  84. package/apps/runtime/dist/engenty/connections-google/shared.js +73 -0
  85. package/apps/runtime/dist/engenty/connections-hubspot/connector.js +140 -0
  86. package/apps/runtime/dist/engenty/connections-microsoft/action.js +17 -0
  87. package/apps/runtime/dist/engenty/connections-microsoft/graph.js +114 -0
  88. package/apps/runtime/dist/engenty/connections-microsoft/onedrive.js +286 -0
  89. package/apps/runtime/dist/engenty/connections-microsoft/outlook.js +464 -0
  90. package/apps/runtime/dist/engenty/connections-s3/s3.js +328 -0
  91. package/apps/runtime/dist/engenty/connections-sdk/files-capability.js +86 -0
  92. package/apps/runtime/dist/engenty/connections-sdk/oauth2.js +150 -0
  93. package/apps/runtime/dist/engenty/connections-sdk/registry.js +89 -0
  94. package/apps/runtime/dist/engenty/connections-sdk/storage-capability.js +84 -0
  95. package/apps/runtime/dist/engenty/connections-sdk/types.js +30 -0
  96. package/apps/runtime/dist/engenty/connections-slack/connector.js +360 -0
  97. package/apps/runtime/dist/engenty/csv-import/csv-matrix.js +178 -0
  98. package/apps/runtime/dist/engenty/csv-import/import-sources.js +87 -0
  99. package/apps/runtime/dist/engenty/csv-import/parse-csv.js +129 -0
  100. package/apps/runtime/dist/engenty/csv-import/serialize-csv.js +20 -0
  101. package/apps/runtime/dist/engenty/csv-import/types.js +1 -0
  102. package/apps/runtime/dist/engenty/csv-import/xlsx-workbook.js +159 -0
  103. package/apps/runtime/dist/engenty/doc-converter/interface.js +9 -0
  104. package/apps/runtime/dist/engenty/doc-converter/page-break.js +92 -0
  105. package/apps/runtime/dist/engenty/doc-converter/providers/local/index.js +72 -0
  106. package/apps/runtime/dist/engenty/document-scanner/schemas/classifier.js +30 -0
  107. package/apps/runtime/dist/engenty/document-scanner/schemas/index.js +4 -0
  108. package/apps/runtime/dist/engenty/document-scanner/schemas/invoice.js +73 -0
  109. package/apps/runtime/dist/engenty/document-scanner/schemas/receipt.js +63 -0
  110. package/apps/runtime/dist/engenty/document-scanner/schemas/shared.js +40 -0
  111. package/apps/runtime/dist/engenty/search-index/chunks.js +164 -0
  112. package/apps/runtime/dist/engenty/search-index/contracts.js +9 -0
  113. package/apps/runtime/dist/engenty/shims/connections-sdk.js +39 -0
  114. package/apps/runtime/dist/engenty/shims/plugin-sdk.js +1 -0
  115. package/apps/runtime/dist/engenty/shims/web-ingest.js +3 -0
  116. package/apps/runtime/dist/engenty/web-ingest/ssrf.js +277 -0
  117. package/apps/runtime/dist/engine/asks.js +60 -0
  118. package/apps/runtime/dist/engine/events.js +46 -0
  119. package/apps/runtime/dist/engine/input.js +170 -0
  120. package/apps/runtime/dist/engine/prepare.js +51 -0
  121. package/apps/runtime/dist/engine/requirements.js +124 -0
  122. package/apps/runtime/dist/engine/resources.js +89 -0
  123. package/apps/runtime/dist/engine/runner.js +560 -0
  124. package/apps/runtime/dist/engine/steps.js +598 -0
  125. package/apps/runtime/dist/engine/template.js +183 -0
  126. package/apps/runtime/dist/engine/types.js +3 -0
  127. package/apps/runtime/dist/env.js +224 -0
  128. package/apps/runtime/dist/files/blobs.js +22 -0
  129. package/apps/runtime/dist/files/objects.js +86 -0
  130. package/apps/runtime/dist/files/storage.js +95 -0
  131. package/apps/runtime/dist/geocode.js +58 -0
  132. package/apps/runtime/dist/harness/claude.js +138 -0
  133. package/apps/runtime/dist/harness/codex.js +228 -0
  134. package/apps/runtime/dist/harness/cursor.js +93 -0
  135. package/apps/runtime/dist/harness/env.js +124 -0
  136. package/apps/runtime/dist/harness/gemini.js +126 -0
  137. package/apps/runtime/dist/harness/index.js +99 -0
  138. package/apps/runtime/dist/harness/model.js +267 -0
  139. package/apps/runtime/dist/harness/prompt.js +121 -0
  140. package/apps/runtime/dist/harness/terminal.js +193 -0
  141. package/apps/runtime/dist/harness/types.js +7 -0
  142. package/apps/runtime/dist/index.js +76 -0
  143. package/apps/runtime/dist/limits.js +57 -0
  144. package/apps/runtime/dist/link-preview.js +59 -0
  145. package/apps/runtime/dist/manage.js +151 -0
  146. package/apps/runtime/dist/mcp/auth.js +102 -0
  147. package/apps/runtime/dist/mcp/bridge.js +59 -0
  148. package/apps/runtime/dist/mcp/handler.js +42 -0
  149. package/apps/runtime/dist/mcp/scopes.js +5 -0
  150. package/apps/runtime/dist/mcp/tools.js +401 -0
  151. package/apps/runtime/dist/media/ffmpeg.js +121 -0
  152. package/apps/runtime/dist/media/generate.js +120 -0
  153. package/apps/runtime/dist/media/marking.js +121 -0
  154. package/apps/runtime/dist/media/transcribe.js +30 -0
  155. package/apps/runtime/dist/model-errors.js +3 -0
  156. package/apps/runtime/dist/models.js +489 -0
  157. package/apps/runtime/dist/plugin.js +61 -0
  158. package/apps/runtime/dist/render/chromium.js +85 -0
  159. package/apps/runtime/dist/render/convert.js +161 -0
  160. package/apps/runtime/dist/render/downloads.js +277 -0
  161. package/apps/runtime/dist/render/guard.js +25 -0
  162. package/apps/runtime/dist/routes/api.js +74 -0
  163. package/apps/runtime/dist/routes/delivery.js +52 -0
  164. package/apps/runtime/dist/routes/internal.js +49 -0
  165. package/apps/runtime/dist/routes/marketplace.js +36 -0
  166. package/apps/runtime/dist/routes/runs.js +805 -0
  167. package/apps/runtime/dist/routes/studio.js +702 -0
  168. package/apps/runtime/dist/routes/wizard-stream.js +30 -0
  169. package/apps/runtime/dist/running.js +39 -0
  170. package/apps/runtime/dist/sandbox/agentos.js +191 -0
  171. package/apps/runtime/dist/sandbox/docker.js +77 -0
  172. package/apps/runtime/dist/sandbox/index.js +30 -0
  173. package/apps/runtime/dist/sandbox/types.js +1 -0
  174. package/apps/runtime/dist/secrets/crypto.js +28 -0
  175. package/apps/runtime/dist/secrets/signing.js +34 -0
  176. package/apps/runtime/dist/secrets/vault.js +59 -0
  177. package/apps/runtime/dist/services/architect.js +92 -0
  178. package/apps/runtime/dist/services/brand.js +20 -0
  179. package/apps/runtime/dist/services/cloud.js +52 -0
  180. package/apps/runtime/dist/services/draft-events.js +27 -0
  181. package/apps/runtime/dist/services/errors.js +24 -0
  182. package/apps/runtime/dist/services/files.js +129 -0
  183. package/apps/runtime/dist/services/links.js +16 -0
  184. package/apps/runtime/dist/services/marketplace.js +292 -0
  185. package/apps/runtime/dist/services/package.js +202 -0
  186. package/apps/runtime/dist/services/profile.js +23 -0
  187. package/apps/runtime/dist/services/project-files.js +266 -0
  188. package/apps/runtime/dist/services/project-index.js +109 -0
  189. package/apps/runtime/dist/services/projects.js +181 -0
  190. package/apps/runtime/dist/services/runs.js +224 -0
  191. package/apps/runtime/dist/services/shares.js +129 -0
  192. package/apps/runtime/dist/services/widgets.js +34 -0
  193. package/apps/runtime/dist/services/wizards.js +342 -0
  194. package/apps/runtime/dist/settings.js +16 -0
  195. package/apps/runtime/dist/store/index.js +279 -0
  196. package/apps/runtime/dist/tenants/control.js +106 -0
  197. package/apps/runtime/dist/tenants/tenant.js +26 -0
  198. package/apps/runtime/dist/tools/browser.js +380 -0
  199. package/apps/runtime/dist/tools/connector.js +204 -0
  200. package/apps/runtime/dist/tools/index.js +234 -0
  201. package/apps/runtime/dist/tools/mail.js +104 -0
  202. package/apps/runtime/dist/tools/net-guard.js +59 -0
  203. package/apps/runtime/dist/tools/project.js +53 -0
  204. package/apps/runtime/dist/tools/shared.js +42 -0
  205. package/apps/runtime/dist/tools/store.js +238 -0
  206. package/apps/runtime/dist/update.js +118 -0
  207. package/apps/runtime/dist/widgets/bundle.js +99 -0
  208. package/apps/runtime/dist/widgets/render.js +312 -0
  209. package/apps/runtime/dist/widgets/runtime.js +84 -0
  210. package/apps/runtime/dist/widgets/script.js +30 -0
  211. package/apps/runtime/dist/widgets/step.js +192 -0
  212. package/apps/web/dist/ai-labels/ai-black.svg +20 -0
  213. package/apps/web/dist/ai-labels/ai-generated-black.svg +31 -0
  214. package/apps/web/dist/ai-labels/ai-generated-white.svg +32 -0
  215. package/apps/web/dist/ai-labels/ai-modified-black.svg +30 -0
  216. package/apps/web/dist/ai-labels/ai-modified-white.svg +31 -0
  217. package/apps/web/dist/ai-labels/ai-white.svg +21 -0
  218. package/apps/web/dist/assets/ChangelogDialog-O00vEnbm.js +1 -0
  219. package/apps/web/dist/assets/ChatPanel-CH9ry0Hp.js +1 -0
  220. package/apps/web/dist/assets/CreditsDialog-DRzecwUw.js +1 -0
  221. package/apps/web/dist/assets/EditorPage-BRnBf02Q.js +2 -0
  222. package/apps/web/dist/assets/FlowPreview-DDOj8IAH.js +1 -0
  223. package/apps/web/dist/assets/HomePage-CAGTTG3N.js +1 -0
  224. package/apps/web/dist/assets/ImportWizard-0RRrAnX3.js +1 -0
  225. package/apps/web/dist/assets/LocalRuntime-BrP-ENHg.css +1 -0
  226. package/apps/web/dist/assets/LocalRuntime-C-TBYrzs.js +16 -0
  227. package/apps/web/dist/assets/NewWizardPage-kI90h3_9.js +2 -0
  228. package/apps/web/dist/assets/PublicRunner-CKRDeIVd.js +1 -0
  229. package/apps/web/dist/assets/RunnerView-Rc4H8wYH.js +2 -0
  230. package/apps/web/dist/assets/SettingsPage-CESMuZxq.js +2 -0
  231. package/apps/web/dist/assets/SetupPage-D1rrYU14.js +1 -0
  232. package/apps/web/dist/assets/SharePage-COTAmOgh.js +1 -0
  233. package/apps/web/dist/assets/api-BY28cjGY.js +10 -0
  234. package/apps/web/dist/assets/arrow-left-UpjOBfQp.js +1 -0
  235. package/apps/web/dist/assets/arrow-right-CucQycEy.js +1 -0
  236. package/apps/web/dist/assets/changelog-p3wsaDDS.js +1 -0
  237. package/apps/web/dist/assets/check-Bjx41xbB.js +1 -0
  238. package/apps/web/dist/assets/createLucideIcon-DBu2Ovxj.js +1 -0
  239. package/apps/web/dist/assets/definition-cy7uvY5L.js +55 -0
  240. package/apps/web/dist/assets/engenty-CWIFpgKq.js +1 -0
  241. package/apps/web/dist/assets/esm-D1OEhjTK.js +7 -0
  242. package/apps/web/dist/assets/external-link-CRg6DVdO.js +1 -0
  243. package/apps/web/dist/assets/fluffy-engenty-CJ3wCQgT.js +456 -0
  244. package/apps/web/dist/assets/index-CQ_q9iMO.css +1 -0
  245. package/apps/web/dist/assets/index-D0gxh90t.js +10 -0
  246. package/apps/web/dist/assets/jsx-runtime-Dk72oS4N.js +1 -0
  247. package/apps/web/dist/assets/meta-BbSdtTUa.js +1 -0
  248. package/apps/web/dist/assets/mic-CeqQ89Dj.js +1 -0
  249. package/apps/web/dist/assets/oss-credits-cPuZUFHD.js +1 -0
  250. package/apps/web/dist/assets/outputs-D6pxH6ve.js +69 -0
  251. package/apps/web/dist/assets/plus-HA210wXA.js +1 -0
  252. package/apps/web/dist/assets/preload-helper-CNEI7N0N.js +1 -0
  253. package/apps/web/dist/assets/qr-scanner-worker.min-CJaXo3yu.js +98 -0
  254. package/apps/web/dist/assets/qr-scanner.min-C_UI0gH-.js +1 -0
  255. package/apps/web/dist/assets/type-BzYgVWKL.js +1 -0
  256. package/apps/web/dist/assets/ui-Ddwl35A6.js +1 -0
  257. package/apps/web/dist/assets/upload-DsgLfQ3U.js +1 -0
  258. package/apps/web/dist/assets/video-Dk1fuTDl.js +1 -0
  259. package/apps/web/dist/assets/wand-sparkles-Bf97fs8T.js +1 -0
  260. package/apps/web/dist/embed.js +102 -0
  261. package/apps/web/dist/favicon.svg +1 -0
  262. package/apps/web/dist/icons/apple-touch-icon.png +0 -0
  263. package/apps/web/dist/icons/icon-192.png +0 -0
  264. package/apps/web/dist/icons/icon-512.png +0 -0
  265. package/apps/web/dist/icons/icon-maskable-512.png +0 -0
  266. package/apps/web/dist/index.html +46 -0
  267. package/apps/web/dist/install.sh +532 -0
  268. package/apps/web/dist/manifest.webmanifest +20 -0
  269. package/apps/web/dist/sw.js +15 -0
  270. package/bin/wizards.mjs +13 -0
  271. package/node_modules/@engenty-wizards/shared/dist/connectors.d.ts +77 -0
  272. package/node_modules/@engenty-wizards/shared/dist/connectors.js +2 -0
  273. package/node_modules/@engenty-wizards/shared/dist/definition.d.ts +1415 -0
  274. package/node_modules/@engenty-wizards/shared/dist/definition.js +762 -0
  275. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-format.d.ts +5 -0
  276. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-format.js +132 -0
  277. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-value.d.ts +11 -0
  278. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-value.js +209 -0
  279. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns.d.ts +532 -0
  280. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns.js +216 -0
  281. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/index.d.ts +3 -0
  282. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/index.js +3 -0
  283. package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-lexical.d.ts +14 -0
  284. package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-lexical.js +166 -0
  285. package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-rank.d.ts +33 -0
  286. package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-rank.js +99 -0
  287. package/node_modules/@engenty-wizards/shared/dist/marketplace-entry.d.ts +35 -0
  288. package/node_modules/@engenty-wizards/shared/dist/marketplace-entry.js +86 -0
  289. package/node_modules/@engenty-wizards/shared/dist/marketplace-search.d.ts +20 -0
  290. package/node_modules/@engenty-wizards/shared/dist/marketplace-search.js +133 -0
  291. package/node_modules/@engenty-wizards/shared/dist/marketplace.d.ts +298 -0
  292. package/node_modules/@engenty-wizards/shared/dist/marketplace.js +211 -0
  293. package/node_modules/@engenty-wizards/shared/dist/merge.d.ts +7 -0
  294. package/node_modules/@engenty-wizards/shared/dist/merge.js +40 -0
  295. package/node_modules/@engenty-wizards/shared/dist/projects.d.ts +74 -0
  296. package/node_modules/@engenty-wizards/shared/dist/projects.js +96 -0
  297. package/node_modules/@engenty-wizards/shared/dist/run.d.ts +219 -0
  298. package/node_modules/@engenty-wizards/shared/dist/run.js +1 -0
  299. package/node_modules/@engenty-wizards/shared/dist/store.d.ts +100 -0
  300. package/node_modules/@engenty-wizards/shared/dist/store.js +44 -0
  301. package/node_modules/@engenty-wizards/shared/dist/workspace.d.ts +16 -0
  302. package/node_modules/@engenty-wizards/shared/dist/workspace.js +51 -0
  303. package/node_modules/@engenty-wizards/shared/package.json +10 -0
  304. package/package.json +62 -0
  305. package/plugin/.claude-plugin/plugin.json +9 -0
  306. package/plugin/.mcp.json +8 -0
  307. package/plugin/commands/wizard.md +8 -0
  308. package/plugin/skills/build-wizard/SKILL.md +27 -0
@@ -0,0 +1,84 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Synthesize the write actions for a connector's storage capability, the
4
+ * counterpart of `filesCapabilityActions`. `files_write`/`files_move` are
5
+ * `group: "write"` and `files_delete` is `group: "destructive"`, so the
6
+ * projected gateway operations inherit the ask-by-default policy, approval
7
+ * flow, and audit with no extra code. Refs share the read capability's
8
+ * semantics (provider-relative to the connection root).
9
+ */
10
+ export function storageCapabilityActions(storage, providerScopes) {
11
+ const scopes = providerScopes ?? [];
12
+ const writeAction = {
13
+ description: "Write (create or overwrite) a file. Pass folder_ref: null for the connection root. Provide the content as content_text for text or content_base64 for binary — exactly one of the two.",
14
+ group: "write",
15
+ id: "files_write",
16
+ inputSchema: z.object({
17
+ content_base64: z
18
+ .string()
19
+ .optional()
20
+ .describe("Base64-encoded file bytes (binary content)."),
21
+ content_text: z
22
+ .string()
23
+ .optional()
24
+ .describe("UTF-8 text content (text files)."),
25
+ folder_ref: z
26
+ .string()
27
+ .nullable()
28
+ .describe("Folder to write into; null targets the connection root."),
29
+ mime_type: z
30
+ .string()
31
+ .nullish()
32
+ .describe("Content type; inferred from the name when omitted."),
33
+ name: z.string().min(1).max(512).describe("File name."),
34
+ }),
35
+ providerScopes: scopes,
36
+ summary: "Write a file",
37
+ handler: (input, ctx) => {
38
+ const write = input;
39
+ const hasText = typeof write.content_text === "string";
40
+ const hasBase64 = typeof write.content_base64 === "string";
41
+ if (hasText === hasBase64) {
42
+ throw new Error("files_write: provide exactly one of content_text or content_base64");
43
+ }
44
+ return storage.write(ctx, write);
45
+ },
46
+ };
47
+ const deleteAction = {
48
+ description: "Permanently delete a file from the connected storage. This cannot be undone.",
49
+ group: "destructive",
50
+ id: "files_delete",
51
+ inputSchema: z.object({
52
+ ref: z.string().min(1).describe("File ref to delete (an entry's ref)."),
53
+ }),
54
+ providerScopes: scopes,
55
+ summary: "Delete a file",
56
+ handler: (input, ctx) => storage.delete(ctx, input),
57
+ };
58
+ const actions = [writeAction, deleteAction];
59
+ if (storage.move) {
60
+ const move = storage.move.bind(storage);
61
+ actions.push({
62
+ description: "Move (or rename) a file to another folder. Pass to_folder_ref: null for the connection root.",
63
+ group: "write",
64
+ id: "files_move",
65
+ inputSchema: z.object({
66
+ new_name: z
67
+ .string()
68
+ .min(1)
69
+ .max(512)
70
+ .optional()
71
+ .describe("Rename while moving; keeps the current name if omitted."),
72
+ ref: z.string().min(1).describe("File ref to move."),
73
+ to_folder_ref: z
74
+ .string()
75
+ .nullable()
76
+ .describe("Destination folder; null targets the connection root."),
77
+ }),
78
+ providerScopes: scopes,
79
+ summary: "Move or rename a file",
80
+ handler: (input, ctx) => move(ctx, input),
81
+ });
82
+ }
83
+ return actions;
84
+ }
@@ -0,0 +1,30 @@
1
+ export const ACTION_GROUP_CONTRACTS = {
2
+ read: { idempotent: true, requiresApproval: false, riskLevel: "low" },
3
+ write: { idempotent: false, requiresApproval: true, riskLevel: "medium" },
4
+ destructive: { idempotent: false, requiresApproval: true, riskLevel: "high" },
5
+ };
6
+ /** Default connection policy per group when the user has not overridden it. */
7
+ export const ACTION_GROUP_DEFAULT_POLICY = {
8
+ read: "allow",
9
+ write: "ask",
10
+ destructive: "ask",
11
+ };
12
+ export function connectorOperationId(connector, actionId) {
13
+ return `${connector.toolPrefix}_${actionId}`;
14
+ }
15
+ /** Union of provider scopes for the given groups (plus base scopes). */
16
+ export function scopesForGroups(connector, groups) {
17
+ if (connector.auth.kind !== "oauth2") {
18
+ return [];
19
+ }
20
+ const scopes = new Set(connector.auth.oauth2.baseScopes);
21
+ for (const action of connector.actions) {
22
+ if (!groups.has(action.group)) {
23
+ continue;
24
+ }
25
+ for (const scope of action.providerScopes ?? []) {
26
+ scopes.add(scope);
27
+ }
28
+ }
29
+ return [...scopes];
30
+ }
@@ -0,0 +1,360 @@
1
+ import { defineConnector, } from "../shims/connections-sdk.js";
2
+ import { z } from "zod";
3
+ /**
4
+ * Every user scope the Slack connector's actions need. Slack's OAuth v2
5
+ * user-token flow expects USER scopes in the `user_scope` query parameter —
6
+ * the `scope` parameter is reserved for BOT scopes. The generic SDK
7
+ * (`buildAuthorizationUrl`) only writes into `scope`, so:
8
+ *
9
+ * - `baseScopes` and every action's `providerScopes` are deliberately `[]`
10
+ * (an empty `scope` param means "no bot scopes", which is what we want), and
11
+ * - the full user scope set is passed statically via
12
+ * `extraAuthParams.user_scope` instead of incrementally per action group.
13
+ */
14
+ export const SLACK_USER_SCOPES = [
15
+ "channels:read",
16
+ "groups:read",
17
+ "channels:history",
18
+ "groups:history",
19
+ "search:read",
20
+ "chat:write",
21
+ "reactions:write",
22
+ "users:read",
23
+ ];
24
+ const SLACK_API_BASE = "https://slack.com/api";
25
+ /**
26
+ * Call a Slack Web API method. Slack returns HTTP 200 with
27
+ * `{ ok: false, error }` on application-level failures, so both the HTTP
28
+ * status and the body's `ok` flag must be checked.
29
+ */
30
+ async function slackApi(ctx, method, params = {}) {
31
+ const body = new URLSearchParams();
32
+ for (const [key, value] of Object.entries(params)) {
33
+ if (value !== undefined) {
34
+ body.set(key, String(value));
35
+ }
36
+ }
37
+ const response = await ctx.fetchImpl(`${SLACK_API_BASE}/${method}`, {
38
+ body,
39
+ headers: {
40
+ authorization: `Bearer ${ctx.accessToken}`,
41
+ "content-type": "application/x-www-form-urlencoded",
42
+ },
43
+ method: "POST",
44
+ });
45
+ if (!response.ok) {
46
+ throw new Error(`slack_http_error: ${method} returned ${response.status}`);
47
+ }
48
+ const data = (await response.json());
49
+ if (!data.ok) {
50
+ throw new Error(`slack_api_error: ${data.error ?? "unknown_error"}`);
51
+ }
52
+ return data;
53
+ }
54
+ function mapMessage(message) {
55
+ return {
56
+ reply_count: message.reply_count ?? 0,
57
+ subtype: message.subtype ?? null,
58
+ text: message.text ?? "",
59
+ thread_ts: message.thread_ts ?? null,
60
+ ts: message.ts,
61
+ user: message.user ?? null,
62
+ };
63
+ }
64
+ const listChannelsInput = z.object({
65
+ limit: z
66
+ .number()
67
+ .int()
68
+ .min(1)
69
+ .max(50)
70
+ .optional()
71
+ .describe("Maximum number of channels to return (1-50, default 50)."),
72
+ });
73
+ const getChannelHistoryInput = z.object({
74
+ channel: z
75
+ .string()
76
+ .describe("Channel ID to fetch history for (e.g. C0123456789)."),
77
+ latest: z
78
+ .string()
79
+ .optional()
80
+ .describe("Only messages before this Slack timestamp (exclusive)."),
81
+ limit: z
82
+ .number()
83
+ .int()
84
+ .min(1)
85
+ .max(25)
86
+ .optional()
87
+ .describe("Maximum number of messages to return (1-25, default 25)."),
88
+ oldest: z
89
+ .string()
90
+ .optional()
91
+ .describe("Only messages after this Slack timestamp (exclusive)."),
92
+ });
93
+ const getThreadRepliesInput = z.object({
94
+ channel: z.string().describe("Channel ID the thread lives in."),
95
+ limit: z
96
+ .number()
97
+ .int()
98
+ .min(1)
99
+ .max(25)
100
+ .optional()
101
+ .describe("Maximum number of replies to return (1-25, default 25)."),
102
+ thread_ts: z
103
+ .string()
104
+ .describe("Timestamp of the thread's parent message (e.g. 1712345678.000100)."),
105
+ });
106
+ const searchMessagesInput = z.object({
107
+ count: z
108
+ .number()
109
+ .int()
110
+ .min(1)
111
+ .max(25)
112
+ .optional()
113
+ .describe("Maximum number of matches to return (1-25, default 25)."),
114
+ query: z
115
+ .string()
116
+ .describe("Search query; supports Slack search modifiers like in:#channel or from:@user."),
117
+ });
118
+ const listUsersInput = z.object({
119
+ limit: z
120
+ .number()
121
+ .int()
122
+ .min(1)
123
+ .max(50)
124
+ .optional()
125
+ .describe("Maximum number of users to return (1-50, default 50)."),
126
+ });
127
+ const postMessageInput = z.object({
128
+ channel: z
129
+ .string()
130
+ .describe("Channel ID (or user ID for a DM) to post the message to."),
131
+ text: z.string().describe("Message text (Slack mrkdwn)."),
132
+ thread_ts: z
133
+ .string()
134
+ .optional()
135
+ .describe("Parent message timestamp to reply in a thread instead of the channel."),
136
+ });
137
+ const updateMessageInput = z.object({
138
+ channel: z.string().describe("Channel ID containing the message to update."),
139
+ text: z.string().describe("New message text (replaces the previous text)."),
140
+ ts: z.string().describe("Timestamp of the message to update."),
141
+ });
142
+ const addReactionInput = z.object({
143
+ channel: z
144
+ .string()
145
+ .describe("Channel ID containing the message to react to."),
146
+ emoji: z
147
+ .string()
148
+ .describe("Emoji name without colons (e.g. thumbsup, white_check_mark)."),
149
+ ts: z.string().describe("Timestamp of the message to react to."),
150
+ });
151
+ // Per-action providerScopes are [] on purpose — see SLACK_USER_SCOPES above:
152
+ // Slack user scopes are requested via extraAuthParams.user_scope, not through
153
+ // the SDK's incremental `scope` machinery (which would send them as bot scopes).
154
+ const actions = [
155
+ {
156
+ description: "List public and private Slack channels the connected user can see, excluding archived ones.",
157
+ group: "read",
158
+ handler: async (input, ctx) => {
159
+ const args = listChannelsInput.parse(input);
160
+ const data = await slackApi(ctx, "conversations.list", {
161
+ exclude_archived: true,
162
+ limit: args.limit ?? 50,
163
+ types: "public_channel,private_channel",
164
+ });
165
+ return {
166
+ channels: (data.channels ?? []).map((channel) => ({
167
+ id: channel.id,
168
+ is_private: channel.is_private ?? false,
169
+ name: channel.name ?? "",
170
+ num_members: channel.num_members ?? 0,
171
+ topic: channel.topic?.value ?? "",
172
+ })),
173
+ };
174
+ },
175
+ id: "list_channels",
176
+ inputSchema: listChannelsInput,
177
+ providerScopes: [],
178
+ summary: "List Slack channels",
179
+ },
180
+ {
181
+ description: "Fetch recent messages from a Slack channel, newest first.",
182
+ group: "read",
183
+ handler: async (input, ctx) => {
184
+ const args = getChannelHistoryInput.parse(input);
185
+ const data = await slackApi(ctx, "conversations.history", {
186
+ channel: args.channel,
187
+ latest: args.latest,
188
+ limit: args.limit ?? 25,
189
+ oldest: args.oldest,
190
+ });
191
+ return { messages: (data.messages ?? []).map(mapMessage) };
192
+ },
193
+ id: "get_channel_history",
194
+ inputSchema: getChannelHistoryInput,
195
+ providerScopes: [],
196
+ summary: "Get Slack channel history",
197
+ },
198
+ {
199
+ description: "Fetch the replies of a Slack thread (including the parent message).",
200
+ group: "read",
201
+ handler: async (input, ctx) => {
202
+ const args = getThreadRepliesInput.parse(input);
203
+ const data = await slackApi(ctx, "conversations.replies", {
204
+ channel: args.channel,
205
+ limit: args.limit ?? 25,
206
+ ts: args.thread_ts,
207
+ });
208
+ return { messages: (data.messages ?? []).map(mapMessage) };
209
+ },
210
+ id: "get_thread_replies",
211
+ inputSchema: getThreadRepliesInput,
212
+ providerScopes: [],
213
+ summary: "Get Slack thread replies",
214
+ },
215
+ {
216
+ description: "Search messages across the Slack workspace as the connected user.",
217
+ group: "read",
218
+ handler: async (input, ctx) => {
219
+ const args = searchMessagesInput.parse(input);
220
+ const data = await slackApi(ctx, "search.messages", {
221
+ count: args.count ?? 25,
222
+ query: args.query,
223
+ });
224
+ return {
225
+ matches: (data.messages?.matches ?? []).map((match) => ({
226
+ channel: match.channel?.name ?? "",
227
+ permalink: match.permalink ?? "",
228
+ text: match.text ?? "",
229
+ ts: match.ts ?? "",
230
+ user: match.user ?? match.username ?? null,
231
+ })),
232
+ };
233
+ },
234
+ id: "search_messages",
235
+ inputSchema: searchMessagesInput,
236
+ providerScopes: [],
237
+ summary: "Search Slack messages",
238
+ },
239
+ {
240
+ description: "List workspace members (deactivated accounts are skipped).",
241
+ group: "read",
242
+ handler: async (input, ctx) => {
243
+ const args = listUsersInput.parse(input);
244
+ const data = await slackApi(ctx, "users.list", { limit: args.limit ?? 50 });
245
+ return {
246
+ users: (data.members ?? [])
247
+ .filter((member) => !member.deleted)
248
+ .map((member) => ({
249
+ id: member.id,
250
+ is_bot: member.is_bot ?? false,
251
+ name: member.name ?? "",
252
+ real_name: member.real_name ?? "",
253
+ })),
254
+ };
255
+ },
256
+ id: "list_users",
257
+ inputSchema: listUsersInput,
258
+ providerScopes: [],
259
+ summary: "List Slack users",
260
+ },
261
+ {
262
+ description: "Post a message to a Slack channel (or as a thread reply) as the connected user.",
263
+ group: "write",
264
+ handler: async (input, ctx) => {
265
+ const args = postMessageInput.parse(input);
266
+ const data = await slackApi(ctx, "chat.postMessage", {
267
+ channel: args.channel,
268
+ text: args.text,
269
+ thread_ts: args.thread_ts,
270
+ });
271
+ return { channel: data.channel ?? args.channel, ts: data.ts ?? "" };
272
+ },
273
+ id: "post_message",
274
+ inputSchema: postMessageInput,
275
+ providerScopes: [],
276
+ summary: "Post a Slack message",
277
+ },
278
+ {
279
+ description: "Update the text of a previously posted Slack message.",
280
+ group: "write",
281
+ handler: async (input, ctx) => {
282
+ const args = updateMessageInput.parse(input);
283
+ const data = await slackApi(ctx, "chat.update", {
284
+ channel: args.channel,
285
+ text: args.text,
286
+ ts: args.ts,
287
+ });
288
+ return { channel: data.channel ?? args.channel, ts: data.ts ?? args.ts };
289
+ },
290
+ id: "update_message",
291
+ inputSchema: updateMessageInput,
292
+ providerScopes: [],
293
+ summary: "Update a Slack message",
294
+ },
295
+ {
296
+ description: "Add an emoji reaction to a Slack message.",
297
+ group: "write",
298
+ handler: async (input, ctx) => {
299
+ const args = addReactionInput.parse(input);
300
+ await slackApi(ctx, "reactions.add", {
301
+ channel: args.channel,
302
+ name: args.emoji,
303
+ timestamp: args.ts,
304
+ });
305
+ return {
306
+ added: true,
307
+ channel: args.channel,
308
+ emoji: args.emoji,
309
+ ts: args.ts,
310
+ };
311
+ },
312
+ id: "add_reaction",
313
+ inputSchema: addReactionInput,
314
+ providerScopes: [],
315
+ summary: "Add a Slack reaction",
316
+ },
317
+ // No destructive actions in v1: chat.delete is deliberately omitted until
318
+ // there is a concrete need for agent-driven message deletion.
319
+ ];
320
+ export const slackConnector = defineConnector({
321
+ actions,
322
+ auth: {
323
+ kind: "oauth2",
324
+ oauth2: {
325
+ authUrl: "https://slack.com/oauth/v2/authorize",
326
+ // Empty on purpose — Slack bot scopes are unused; see SLACK_USER_SCOPES.
327
+ baseScopes: [],
328
+ clientIdEnv: "SLACK_OAUTH_CLIENT_ID",
329
+ clientSecretEnv: "SLACK_OAUTH_CLIENT_SECRET",
330
+ extraAuthParams: {
331
+ user_scope: SLACK_USER_SCOPES.join(","),
332
+ },
333
+ resolveAccount: async (accessToken, fetchImpl) => {
334
+ const response = await fetchImpl(`${SLACK_API_BASE}/auth.test`, {
335
+ headers: { authorization: `Bearer ${accessToken}` },
336
+ method: "POST",
337
+ });
338
+ if (!response.ok) {
339
+ throw new Error(`slack_http_error: auth.test returned ${response.status}`);
340
+ }
341
+ const data = (await response.json());
342
+ if (!data.ok) {
343
+ throw new Error(`slack_api_error: ${data.error ?? "unknown_error"}`);
344
+ }
345
+ return {
346
+ label: `${data.team ?? "Slack"} / ${data.user ?? "unknown"}`,
347
+ ...(data.user_id ? { externalId: data.user_id } : {}),
348
+ };
349
+ },
350
+ scopeSeparator: ",",
351
+ tokenUrl: "https://slack.com/api/oauth.v2.access",
352
+ },
353
+ },
354
+ description: "Slack workspace access as the connected user: channels, history, search, messages, reactions.",
355
+ icon: "logo:slack",
356
+ id: "slack",
357
+ moduleId: "connections-slack",
358
+ name: "Slack",
359
+ toolPrefix: "slack",
360
+ });
@@ -0,0 +1,178 @@
1
+ /**
2
+ * A CSV as an editable matrix — the model behind `CsvTable`.
3
+ *
4
+ * Built on this package's own parser rather than a new dependency, and that is
5
+ * the point: `parseCSVRows`/`detectDelimiter`/`serializeDelimitedMatrix` are
6
+ * what the import wizard reads with, so a grid built on them shows exactly the
7
+ * cells an import would produce. A second CSV dialect in the codebase — a
8
+ * library that quotes, trims or splits by different rules — would let the
9
+ * viewer and the importer disagree about the same file, which is worse than
10
+ * anything a library would buy us here.
11
+ *
12
+ * Pure: every operation returns a new matrix and nothing here touches React.
13
+ */
14
+ import { detectDelimiter, parseCSVRows } from "./parse-csv.js";
15
+ import { escapeDelimitedField, serializeDelimitedMatrix, } from "./serialize-csv.js";
16
+ export const EMPTY_CSV_MATRIX = {
17
+ columns: [],
18
+ delimiter: ",",
19
+ raggedRows: 0,
20
+ rows: [],
21
+ };
22
+ /** Text → matrix. Never throws: unparseable input is an empty matrix. */
23
+ export function parseCsvMatrix(text) {
24
+ if (!text.trim()) {
25
+ return EMPTY_CSV_MATRIX;
26
+ }
27
+ const delimiter = detectDelimiter(text);
28
+ const raw = parseCSVRows(text, delimiter);
29
+ const [header = [], ...body] = raw;
30
+ // Widen to the widest row, so a row with more cells than the header keeps
31
+ // all of them — `serializeDelimitedMatrix` maps over the header, and a
32
+ // narrow header would drop the tail on the next save.
33
+ const width = body.reduce((max, row) => Math.max(max, row.length), header.length);
34
+ const columns = Array.from({ length: width }, (_, i) => header[i] ?? "");
35
+ const raggedRows = body.filter((row) => row.length > header.length).length;
36
+ return {
37
+ columns,
38
+ delimiter,
39
+ raggedRows,
40
+ rows: body.map((row) => Array.from({ length: width }, (_, i) => row[i] ?? "")),
41
+ };
42
+ }
43
+ /** Matrix → text, in the delimiter it was read with. */
44
+ export function serializeCsvMatrix(matrix) {
45
+ if (matrix.columns.length === 0) {
46
+ return "";
47
+ }
48
+ return serializeDelimitedMatrix(matrix.columns, matrix.rows, matrix.delimiter);
49
+ }
50
+ export function setCsvCell(matrix, rowIndex, columnIndex, value) {
51
+ const rows = matrix.rows.map((row, index) => index === rowIndex
52
+ ? row.map((cell, column) => (column === columnIndex ? value : cell))
53
+ : row);
54
+ return { ...matrix, rows };
55
+ }
56
+ export function setCsvColumnName(matrix, columnIndex, name) {
57
+ return {
58
+ ...matrix,
59
+ columns: matrix.columns.map((column, index) => index === columnIndex ? name : column),
60
+ };
61
+ }
62
+ /** Append an empty row. A matrix with no columns gets one first. */
63
+ export function appendCsvRow(matrix) {
64
+ const columns = matrix.columns.length > 0 ? matrix.columns : [""];
65
+ return {
66
+ ...matrix,
67
+ columns,
68
+ rows: [...matrix.rows, columns.map(() => "")],
69
+ };
70
+ }
71
+ export function removeCsvRow(matrix, rowIndex) {
72
+ return {
73
+ ...matrix,
74
+ rows: matrix.rows.filter((_, index) => index !== rowIndex),
75
+ };
76
+ }
77
+ export function appendCsvColumn(matrix, name = "") {
78
+ return {
79
+ ...matrix,
80
+ columns: [...matrix.columns, name],
81
+ rows: matrix.rows.map((row) => [...row, ""]),
82
+ };
83
+ }
84
+ export function removeCsvColumn(matrix, columnIndex) {
85
+ return {
86
+ ...matrix,
87
+ columns: matrix.columns.filter((_, index) => index !== columnIndex),
88
+ rows: matrix.rows.map((row) => row.filter((_, index) => index !== columnIndex)),
89
+ };
90
+ }
91
+ /** Two corners → a rectangle, in either drag direction. */
92
+ export function csvRange(anchor, focus) {
93
+ return {
94
+ bottom: Math.max(anchor.row, focus.row),
95
+ left: Math.min(anchor.column, focus.column),
96
+ right: Math.max(anchor.column, focus.column),
97
+ top: Math.min(anchor.row, focus.row),
98
+ };
99
+ }
100
+ export function csvRangeContains(range, cell) {
101
+ return (cell.row >= range.top &&
102
+ cell.row <= range.bottom &&
103
+ cell.column >= range.left &&
104
+ cell.column <= range.right);
105
+ }
106
+ /**
107
+ * A selection as TAB-separated text.
108
+ *
109
+ * Tabs rather than the matrix's own delimiter because this is what goes on the
110
+ * clipboard, and tab-separated is what Excel, Numbers and Sheets paste back as
111
+ * cells. Cells containing a tab or newline are quoted so the round trip holds.
112
+ */
113
+ export function csvRangeToText(matrix, range) {
114
+ const lines = [];
115
+ for (let row = range.top; row <= range.bottom; row++) {
116
+ const cells = [];
117
+ for (let column = range.left; column <= range.right; column++) {
118
+ const value = row < 0
119
+ ? (matrix.columns[column] ?? "")
120
+ : (matrix.rows[row]?.[column] ?? "");
121
+ cells.push(escapeDelimitedField(value, "\t"));
122
+ }
123
+ lines.push(cells.join("\t"));
124
+ }
125
+ return lines.join("\n");
126
+ }
127
+ /** Clipboard text → a block of cells. Tab-separated wins; CSV is the fallback. */
128
+ export function parseCsvBlock(text) {
129
+ if (!text) {
130
+ return [];
131
+ }
132
+ const delimiter = text.includes("\t") ? "\t" : detectDelimiter(text);
133
+ return parseCSVRows(text, delimiter);
134
+ }
135
+ /**
136
+ * Write a block of cells at a corner, GROWING the matrix to fit.
137
+ *
138
+ * A paste that silently stopped at the last row would be the same data loss the
139
+ * ragged-row rule exists to prevent: pasting 12 rows into a 3-row table means
140
+ * you wanted 12 rows.
141
+ */
142
+ export function writeCsvBlock(matrix, at, block) {
143
+ if (block.length === 0) {
144
+ return matrix;
145
+ }
146
+ const blockWidth = block.reduce((max, row) => Math.max(max, row.length), 0);
147
+ const width = Math.max(matrix.columns.length, at.column + blockWidth);
148
+ const headerOffset = at.row < 0 ? 1 : 0;
149
+ const height = Math.max(matrix.rows.length, Math.max(at.row, 0) + block.length - headerOffset);
150
+ const columns = Array.from({ length: width }, (_, index) => matrix.columns[index] ?? "");
151
+ const rows = Array.from({ length: height }, (_, rowIndex) => Array.from({ length: width }, (_, columnIndex) => matrix.rows[rowIndex]?.[columnIndex] ?? ""));
152
+ block.forEach((blockRow, blockRowIndex) => {
153
+ const targetRow = at.row + blockRowIndex;
154
+ blockRow.forEach((value, blockColumnIndex) => {
155
+ const targetColumn = at.column + blockColumnIndex;
156
+ if (targetColumn >= width) {
157
+ return;
158
+ }
159
+ if (targetRow < 0) {
160
+ columns[targetColumn] = value;
161
+ return;
162
+ }
163
+ const row = rows[targetRow];
164
+ if (row) {
165
+ row[targetColumn] = value;
166
+ }
167
+ });
168
+ });
169
+ return { ...matrix, columns, rows };
170
+ }
171
+ /** Empty every cell in the rectangle, keeping the rows and columns themselves. */
172
+ export function clearCsvRange(matrix, range) {
173
+ const columns = matrix.columns.map((column, index) => range.top <= -1 && index >= range.left && index <= range.right ? "" : column);
174
+ const rows = matrix.rows.map((row, rowIndex) => rowIndex >= range.top && rowIndex <= range.bottom
175
+ ? row.map((cell, columnIndex) => columnIndex >= range.left && columnIndex <= range.right ? "" : cell)
176
+ : row);
177
+ return { ...matrix, columns, rows };
178
+ }