@graph8/sdk 0.303.0 → 0.357.0

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 (664) hide show
  1. package/README.md +81 -0
  2. package/blocks/README.md +32 -0
  3. package/blocks/_foundation/README.md +78 -0
  4. package/blocks/_foundation/block.json +79 -0
  5. package/blocks/_foundation/components/api-error.tsx +62 -0
  6. package/blocks/_foundation/components/confirm-spend-dialog.tsx +142 -0
  7. package/blocks/_foundation/components/empty-state.tsx +24 -0
  8. package/blocks/_foundation/hooks/use-g8-mutation.ts +88 -0
  9. package/blocks/_foundation/hooks/use-g8-query.ts +87 -0
  10. package/blocks/_foundation/hooks/use-job-poll.ts +130 -0
  11. package/blocks/_foundation/hooks/use-paginated.ts +105 -0
  12. package/blocks/_foundation/lib/g8-client.ts +128 -0
  13. package/blocks/_foundation/recipes/verify-proxy-key.ts +18 -0
  14. package/blocks/_foundation/server/proxy.ts +233 -0
  15. package/blocks/_foundation/server/route.ts +30 -0
  16. package/blocks/agents/README.md +133 -0
  17. package/blocks/agents/block.json +223 -0
  18. package/blocks/agents/components/agent-builder.tsx +199 -0
  19. package/blocks/agents/components/agent-knowledge-panel.tsx +144 -0
  20. package/blocks/agents/components/agent-runs.tsx +175 -0
  21. package/blocks/agents/components/agent-skills-panel.tsx +126 -0
  22. package/blocks/agents/components/agent-test-console.tsx +175 -0
  23. package/blocks/agents/lib/agent-types.ts +114 -0
  24. package/blocks/agents/recipes/create-agent-and-attach-skill.ts +50 -0
  25. package/blocks/agents/recipes/run-and-follow-agent-run.ts +46 -0
  26. package/blocks/agents/recipes/test-agent-before-activating.ts +36 -0
  27. package/blocks/analytics/README.md +73 -0
  28. package/blocks/analytics/block.json +178 -0
  29. package/blocks/analytics/components/funnel-chart.tsx +79 -0
  30. package/blocks/analytics/components/kpi-tiles.tsx +74 -0
  31. package/blocks/analytics/components/leaderboard.tsx +86 -0
  32. package/blocks/analytics/components/report-builder.tsx +200 -0
  33. package/blocks/analytics/hooks/use-report.ts +26 -0
  34. package/blocks/analytics/lib/analytics-types.ts +108 -0
  35. package/blocks/analytics/lib/use-debounced.ts +16 -0
  36. package/blocks/analytics/recipes/01-weekly-outbound-report.ts +32 -0
  37. package/blocks/analytics/recipes/02-revenue-snapshot.ts +22 -0
  38. package/blocks/analytics/recipes/03-saved-report.ts +30 -0
  39. package/blocks/appointments/README.md +80 -0
  40. package/blocks/appointments/block.json +218 -0
  41. package/blocks/appointments/components/availability-editor.tsx +192 -0
  42. package/blocks/appointments/components/booking-page.tsx +154 -0
  43. package/blocks/appointments/components/event-type-editor.tsx +214 -0
  44. package/blocks/appointments/components/meeting-detail.tsx +147 -0
  45. package/blocks/appointments/components/meeting-list.tsx +148 -0
  46. package/blocks/appointments/hooks/use-available-slots.ts +29 -0
  47. package/blocks/appointments/hooks/use-schedules.ts +18 -0
  48. package/blocks/appointments/lib/appointments-types.ts +159 -0
  49. package/blocks/appointments/lib/idempotency.ts +30 -0
  50. package/blocks/appointments/recipes/01-create-event-type-and-hours.ts +27 -0
  51. package/blocks/appointments/recipes/02-find-slots-and-book.ts +34 -0
  52. package/blocks/appointments/recipes/03-bookings-digest.ts +22 -0
  53. package/blocks/appointments/recipes/04-calendar-invite.ts +21 -0
  54. package/blocks/block.schema.json +130 -0
  55. package/blocks/block.types.ts +109 -0
  56. package/blocks/campaigns/README.md +118 -0
  57. package/blocks/campaigns/block.json +361 -0
  58. package/blocks/campaigns/components/campaign-assets.tsx +180 -0
  59. package/blocks/campaigns/components/campaign-audience-step.tsx +91 -0
  60. package/blocks/campaigns/components/campaign-launch-step.tsx +147 -0
  61. package/blocks/campaigns/components/campaign-list.tsx +120 -0
  62. package/blocks/campaigns/components/campaign-metrics.tsx +93 -0
  63. package/blocks/campaigns/components/campaign-select.tsx +52 -0
  64. package/blocks/campaigns/components/campaign-sequence-step.tsx +158 -0
  65. package/blocks/campaigns/components/campaign-wizard.tsx +238 -0
  66. package/blocks/campaigns/components/form-field.tsx +29 -0
  67. package/blocks/campaigns/components/icp-editor.tsx +230 -0
  68. package/blocks/campaigns/components/persona-editor.tsx +210 -0
  69. package/blocks/campaigns/hooks/use-company-profile.ts +34 -0
  70. package/blocks/campaigns/lib/campaigns-utils.ts +155 -0
  71. package/blocks/campaigns/recipes/attach-audience-and-launch.ts +33 -0
  72. package/blocks/campaigns/recipes/campaign-report.ts +22 -0
  73. package/blocks/campaigns/recipes/generate-ideas-and-create-campaign.ts +51 -0
  74. package/blocks/campaigns/recipes/set-up-icp-and-personas.ts +37 -0
  75. package/blocks/coach/README.md +93 -0
  76. package/blocks/coach/block.json +125 -0
  77. package/blocks/coach/components/call-scorecard.tsx +126 -0
  78. package/blocks/coach/components/coaching-feed.tsx +140 -0
  79. package/blocks/coach/lib/coach-types.ts +116 -0
  80. package/blocks/coach/recipes/read-a-scorecard.ts +27 -0
  81. package/blocks/coach/recipes/weekly-coaching-digest.ts +53 -0
  82. package/blocks/copilot/README.md +99 -0
  83. package/blocks/copilot/block.json +151 -0
  84. package/blocks/copilot/components/copilot-memory.tsx +87 -0
  85. package/blocks/copilot/components/copilot-panel.tsx +189 -0
  86. package/blocks/copilot/components/copilot-team-chat.tsx +111 -0
  87. package/blocks/copilot/lib/copilot-types.ts +111 -0
  88. package/blocks/copilot/recipes/ask-the-copilot.ts +35 -0
  89. package/blocks/copilot/recipes/post-in-a-work-channel.ts +33 -0
  90. package/blocks/copilot/recipes/remember-and-recall.ts +19 -0
  91. package/blocks/crm/README.md +122 -0
  92. package/blocks/crm/block.json +378 -0
  93. package/blocks/crm/components/activity-timeline.tsx +99 -0
  94. package/blocks/crm/components/company-detail.tsx +118 -0
  95. package/blocks/crm/components/company-table.tsx +173 -0
  96. package/blocks/crm/components/contact-detail.tsx +135 -0
  97. package/blocks/crm/components/contact-table.tsx +217 -0
  98. package/blocks/crm/components/dedupe-review.tsx +164 -0
  99. package/blocks/crm/components/import-wizard.tsx +193 -0
  100. package/blocks/crm/components/notes-panel.tsx +112 -0
  101. package/blocks/crm/components/record-form.tsx +191 -0
  102. package/blocks/crm/components/task-list.tsx +107 -0
  103. package/blocks/crm/hooks/use-custom-fields.ts +18 -0
  104. package/blocks/crm/hooks/use-debounced.ts +13 -0
  105. package/blocks/crm/hooks/use-page-query.ts +52 -0
  106. package/blocks/crm/lib/crm-types.ts +136 -0
  107. package/blocks/crm/recipes/custom-fields-and-values.ts +28 -0
  108. package/blocks/crm/recipes/dedupe-and-merge.ts +30 -0
  109. package/blocks/crm/recipes/notes-tasks-and-activity.ts +23 -0
  110. package/blocks/crm/recipes/upsert-contact-with-company.ts +33 -0
  111. package/blocks/deals/README.md +78 -0
  112. package/blocks/deals/block.json +198 -0
  113. package/blocks/deals/components/deal-detail.tsx +134 -0
  114. package/blocks/deals/components/forecast-view.tsx +112 -0
  115. package/blocks/deals/components/pipeline-board.tsx +142 -0
  116. package/blocks/deals/components/stage-editor.tsx +96 -0
  117. package/blocks/deals/hooks/use-pipeline-deals.ts +67 -0
  118. package/blocks/deals/hooks/use-pipelines.ts +19 -0
  119. package/blocks/deals/lib/deals-types.ts +128 -0
  120. package/blocks/deals/recipes/01-create-deal-and-advance.ts +32 -0
  121. package/blocks/deals/recipes/02-pipeline-report.ts +35 -0
  122. package/blocks/deals/recipes/03-stale-deals-sweep.ts +20 -0
  123. package/blocks/dialer/README.md +114 -0
  124. package/blocks/dialer/block.json +260 -0
  125. package/blocks/dialer/components/call-log.tsx +78 -0
  126. package/blocks/dialer/components/call-queue.tsx +188 -0
  127. package/blocks/dialer/components/dialer-workspace.tsx +47 -0
  128. package/blocks/dialer/components/disposition-panel.tsx +75 -0
  129. package/blocks/dialer/components/live-calls-table.tsx +107 -0
  130. package/blocks/dialer/components/session-start-form.tsx +111 -0
  131. package/blocks/dialer/components/softphone.tsx +122 -0
  132. package/blocks/dialer/components/transcript-viewer.tsx +82 -0
  133. package/blocks/dialer/hooks/use-dialer-numbers.ts +19 -0
  134. package/blocks/dialer/hooks/use-dialer-session.ts +68 -0
  135. package/blocks/dialer/hooks/use-dispositions.ts +29 -0
  136. package/blocks/dialer/lib/dialer-types.ts +158 -0
  137. package/blocks/dialer/lib/softphone-transport.ts +67 -0
  138. package/blocks/dialer/lib/twilio-transport.ts +74 -0
  139. package/blocks/dialer/recipes/01-create-session-and-dial.ts +39 -0
  140. package/blocks/dialer/recipes/02-disposition-calls.ts +26 -0
  141. package/blocks/dialer/recipes/03-review-a-call.ts +23 -0
  142. package/blocks/enrichment/README.md +105 -0
  143. package/blocks/enrichment/block.json +204 -0
  144. package/blocks/enrichment/components/bulk-enrich-panel.tsx +205 -0
  145. package/blocks/enrichment/components/email-verify-badge.tsx +82 -0
  146. package/blocks/enrichment/components/enrich-button.tsx +136 -0
  147. package/blocks/enrichment/components/waterfall-config.tsx +127 -0
  148. package/blocks/enrichment/hooks/use-providers.ts +18 -0
  149. package/blocks/enrichment/lib/enrichment-types.ts +104 -0
  150. package/blocks/enrichment/recipes/bulk-enrich-and-poll.ts +41 -0
  151. package/blocks/enrichment/recipes/lookup-and-verify.ts +25 -0
  152. package/blocks/enrichment/recipes/waterfall-pipeline.ts +26 -0
  153. package/blocks/forms/README.md +112 -0
  154. package/blocks/forms/block.json +200 -0
  155. package/blocks/forms/components/form-builder.tsx +150 -0
  156. package/blocks/forms/components/form-embed.tsx +103 -0
  157. package/blocks/forms/components/form-mapping-editor.tsx +125 -0
  158. package/blocks/forms/components/site-audit-panel.tsx +73 -0
  159. package/blocks/forms/components/submissions-table.tsx +158 -0
  160. package/blocks/forms/lib/form-types.ts +127 -0
  161. package/blocks/forms/recipes/install-snippet-and-form.ts +21 -0
  162. package/blocks/forms/recipes/retry-failed-deliveries.ts +26 -0
  163. package/blocks/forms/recipes/route-form-to-list.ts +44 -0
  164. package/blocks/inbox/README.md +72 -0
  165. package/blocks/inbox/block.json +194 -0
  166. package/blocks/inbox/components/inbox-list.tsx +156 -0
  167. package/blocks/inbox/components/inbox-screen.tsx +49 -0
  168. package/blocks/inbox/components/reply-composer.tsx +183 -0
  169. package/blocks/inbox/components/tag-and-assign.tsx +189 -0
  170. package/blocks/inbox/components/thread-badge.tsx +17 -0
  171. package/blocks/inbox/components/thread-view.tsx +108 -0
  172. package/blocks/inbox/hooks/use-inbox-thread.ts +24 -0
  173. package/blocks/inbox/hooks/use-inbox-threads.ts +40 -0
  174. package/blocks/inbox/lib/idempotency.ts +30 -0
  175. package/blocks/inbox/lib/inbox-types.ts +100 -0
  176. package/blocks/inbox/recipes/01-triage-and-reply.ts +43 -0
  177. package/blocks/inbox/recipes/02-tag-and-assign.ts +31 -0
  178. package/blocks/inbox/recipes/03-hot-contacts-digest.ts +16 -0
  179. package/blocks/integrations/README.md +75 -0
  180. package/blocks/integrations/block.json +172 -0
  181. package/blocks/integrations/components/integration-list.tsx +165 -0
  182. package/blocks/integrations/components/sync-status.tsx +223 -0
  183. package/blocks/integrations/components/webhook-manager.tsx +300 -0
  184. package/blocks/integrations/lib/integrations-utils.ts +62 -0
  185. package/blocks/integrations/recipes/crm-sync-health.ts +19 -0
  186. package/blocks/integrations/recipes/push-contacts-to-crm.ts +20 -0
  187. package/blocks/integrations/recipes/register-webhook.ts +18 -0
  188. package/blocks/integrations/recipes/verify-webhook-route.ts +33 -0
  189. package/blocks/intent/README.md +57 -0
  190. package/blocks/intent/block.json +138 -0
  191. package/blocks/intent/components/account-intent-score.tsx +91 -0
  192. package/blocks/intent/components/intent-feed.tsx +215 -0
  193. package/blocks/intent/components/intent-keyword-manager.tsx +331 -0
  194. package/blocks/intent/hooks/use-account-intent.ts +46 -0
  195. package/blocks/intent/lib/intent-utils.ts +95 -0
  196. package/blocks/intent/recipes/rank-intent-accounts.ts +36 -0
  197. package/blocks/intent/recipes/track-competitor-domain.ts +23 -0
  198. package/blocks/intent/recipes/who-visited-url.ts +18 -0
  199. package/blocks/lifecycle/README.md +79 -0
  200. package/blocks/lifecycle/block.json +198 -0
  201. package/blocks/lifecycle/components/churn-list.tsx +92 -0
  202. package/blocks/lifecycle/components/customer-table.tsx +119 -0
  203. package/blocks/lifecycle/components/lead-queue.tsx +127 -0
  204. package/blocks/lifecycle/components/lifecycle-board.tsx +102 -0
  205. package/blocks/lifecycle/components/stage-move-dialog.tsx +75 -0
  206. package/blocks/lifecycle/hooks/use-debounced.ts +13 -0
  207. package/blocks/lifecycle/hooks/use-lifecycle-stages.ts +27 -0
  208. package/blocks/lifecycle/lib/lifecycle.ts +99 -0
  209. package/blocks/lifecycle/recipes/customer-brief.ts +23 -0
  210. package/blocks/lifecycle/recipes/move-company-stage.ts +17 -0
  211. package/blocks/lifecycle/recipes/read-lifecycle-board.ts +21 -0
  212. package/blocks/lifecycle/recipes/unlock-stage-contacts.ts +25 -0
  213. package/blocks/lists/README.md +96 -0
  214. package/blocks/lists/block.json +166 -0
  215. package/blocks/lists/components/add-to-list-button.tsx +111 -0
  216. package/blocks/lists/components/list-picker.tsx +94 -0
  217. package/blocks/lists/components/list-table.tsx +160 -0
  218. package/blocks/lists/components/suppression-manager.tsx +153 -0
  219. package/blocks/lists/recipes/create-list-and-add-contacts.ts +35 -0
  220. package/blocks/lists/recipes/refresh-and-check-lists.ts +21 -0
  221. package/blocks/lists/recipes/suppress-and-reinstate.ts +30 -0
  222. package/blocks/mailboxes/README.md +71 -0
  223. package/blocks/mailboxes/block.json +187 -0
  224. package/blocks/mailboxes/components/connect-mailbox.tsx +284 -0
  225. package/blocks/mailboxes/components/domain-purchase.tsx +271 -0
  226. package/blocks/mailboxes/components/mailbox-list.tsx +185 -0
  227. package/blocks/mailboxes/components/warmup-health.tsx +232 -0
  228. package/blocks/mailboxes/lib/mailboxes-utils.ts +69 -0
  229. package/blocks/mailboxes/recipes/buy-domain-and-mailboxes.ts +64 -0
  230. package/blocks/mailboxes/recipes/connect-imap-mailbox.ts +38 -0
  231. package/blocks/mailboxes/recipes/is-it-safe-to-send.ts +17 -0
  232. package/blocks/mailboxes/recipes/warmup-report.ts +31 -0
  233. package/blocks/newsletter/README.md +122 -0
  234. package/blocks/newsletter/block.json +224 -0
  235. package/blocks/newsletter/components/issue-form.tsx +124 -0
  236. package/blocks/newsletter/components/issue-scheduler.tsx +152 -0
  237. package/blocks/newsletter/components/newsletter-editor.tsx +131 -0
  238. package/blocks/newsletter/components/newsletter-stats.tsx +93 -0
  239. package/blocks/newsletter/components/subscriber-table.tsx +173 -0
  240. package/blocks/newsletter/lib/newsletter-types.ts +97 -0
  241. package/blocks/newsletter/recipes/create-newsletter-and-draft-issue.ts +27 -0
  242. package/blocks/newsletter/recipes/import-audience-and-send.ts +43 -0
  243. package/blocks/newsletter/recipes/issue-performance-report.ts +26 -0
  244. package/blocks/nurture/README.md +110 -0
  245. package/blocks/nurture/block.json +191 -0
  246. package/blocks/nurture/components/nurture-builder.tsx +114 -0
  247. package/blocks/nurture/components/nurture-enroll.tsx +107 -0
  248. package/blocks/nurture/components/nurture-form.tsx +120 -0
  249. package/blocks/nurture/components/nurture-stats.tsx +81 -0
  250. package/blocks/nurture/lib/nurture-types.ts +109 -0
  251. package/blocks/nurture/recipes/create-a-nurture.ts +34 -0
  252. package/blocks/nurture/recipes/enroll-contacts-and-launch.ts +24 -0
  253. package/blocks/nurture/recipes/nurture-health-check.ts +28 -0
  254. package/blocks/objects/README.md +120 -0
  255. package/blocks/objects/block.json +243 -0
  256. package/blocks/objects/components/attribute-field-input.tsx +82 -0
  257. package/blocks/objects/components/attribute-form-dialog.tsx +159 -0
  258. package/blocks/objects/components/object-not-found.tsx +34 -0
  259. package/blocks/objects/components/object-record-detail.tsx +143 -0
  260. package/blocks/objects/components/object-schema-editor.tsx +197 -0
  261. package/blocks/objects/components/object-table.tsx +130 -0
  262. package/blocks/objects/components/record-form-dialog.tsx +112 -0
  263. package/blocks/objects/components/record-roles-panel.tsx +106 -0
  264. package/blocks/objects/hooks/use-object-schema.ts +36 -0
  265. package/blocks/objects/lib/object-types.ts +214 -0
  266. package/blocks/objects/recipes/define-object-and-add-records.ts +24 -0
  267. package/blocks/objects/recipes/update-audit-and-archive-records.ts +39 -0
  268. package/blocks/objects/recipes/upsert-records-from-your-system.ts +30 -0
  269. package/blocks/pages/README.md +104 -0
  270. package/blocks/pages/block.json +223 -0
  271. package/blocks/pages/components/brand-kit-editor.tsx +152 -0
  272. package/blocks/pages/components/content-library.tsx +163 -0
  273. package/blocks/pages/components/page-editor.tsx +253 -0
  274. package/blocks/pages/components/page-list.tsx +159 -0
  275. package/blocks/pages/components/seo-report.tsx +159 -0
  276. package/blocks/pages/lib/studio.ts +77 -0
  277. package/blocks/pages/recipes/audit-site-seo.ts +28 -0
  278. package/blocks/pages/recipes/create-and-publish-page.ts +26 -0
  279. package/blocks/pages/recipes/edit-page-section.ts +25 -0
  280. package/blocks/pages/recipes/refresh-brand-kit.ts +22 -0
  281. package/blocks/quotes/README.md +79 -0
  282. package/blocks/quotes/block.json +183 -0
  283. package/blocks/quotes/components/product-picker.tsx +80 -0
  284. package/blocks/quotes/components/quote-builder.tsx +195 -0
  285. package/blocks/quotes/components/quote-list.tsx +79 -0
  286. package/blocks/quotes/components/quote-viewer.tsx +135 -0
  287. package/blocks/quotes/components/signature-status.tsx +77 -0
  288. package/blocks/quotes/hooks/use-debounced.ts +13 -0
  289. package/blocks/quotes/lib/quotes.ts +142 -0
  290. package/blocks/quotes/recipes/create-and-send-quote.ts +44 -0
  291. package/blocks/quotes/recipes/revise-sent-quote.ts +18 -0
  292. package/blocks/quotes/recipes/track-quote-signature.ts +22 -0
  293. package/blocks/radar/README.md +106 -0
  294. package/blocks/radar/block.json +189 -0
  295. package/blocks/radar/components/competitor-battle-card.tsx +53 -0
  296. package/blocks/radar/components/competitor-tracker.tsx +158 -0
  297. package/blocks/radar/components/radar-overview.tsx +67 -0
  298. package/blocks/radar/components/radar-report.tsx +86 -0
  299. package/blocks/radar/hooks/use-competitors.ts +23 -0
  300. package/blocks/radar/lib/radar-types.ts +124 -0
  301. package/blocks/radar/recipes/battle-card.ts +28 -0
  302. package/blocks/radar/recipes/deep-dive-report.ts +32 -0
  303. package/blocks/radar/recipes/marketing-snapshot.ts +18 -0
  304. package/blocks/radar/recipes/track-and-scan.ts +39 -0
  305. package/blocks/search/README.md +129 -0
  306. package/blocks/search/block.json +185 -0
  307. package/blocks/search/components/filter-builder.tsx +149 -0
  308. package/blocks/search/components/prospect-results.tsx +175 -0
  309. package/blocks/search/components/save-to-list-dialog.tsx +135 -0
  310. package/blocks/search/components/saved-searches.tsx +95 -0
  311. package/blocks/search/hooks/use-filter-options.ts +41 -0
  312. package/blocks/search/lib/search-types.ts +149 -0
  313. package/blocks/search/recipes/explore-filter-values.ts +27 -0
  314. package/blocks/search/recipes/saved-searches.ts +34 -0
  315. package/blocks/search/recipes/search-and-save-to-list.ts +37 -0
  316. package/blocks/sequencer/README.md +87 -0
  317. package/blocks/sequencer/block.json +214 -0
  318. package/blocks/sequencer/components/enroll-dialog.tsx +93 -0
  319. package/blocks/sequencer/components/sequence-builder.tsx +306 -0
  320. package/blocks/sequencer/components/sequence-list.tsx +200 -0
  321. package/blocks/sequencer/components/sequence-stats.tsx +256 -0
  322. package/blocks/sequencer/components/step-editor.tsx +213 -0
  323. package/blocks/sequencer/lib/sequencer-utils.ts +168 -0
  324. package/blocks/sequencer/recipes/create-multichannel-sequence.ts +51 -0
  325. package/blocks/sequencer/recipes/launch-and-enroll.ts +44 -0
  326. package/blocks/sequencer/recipes/pause-when-unsafe.ts +30 -0
  327. package/blocks/sequencer/recipes/sequence-report.ts +22 -0
  328. package/blocks/social-ads/README.md +128 -0
  329. package/blocks/social-ads/block.json +256 -0
  330. package/blocks/social-ads/components/ad-campaign-list.tsx +136 -0
  331. package/blocks/social-ads/components/audience-sync.tsx +150 -0
  332. package/blocks/social-ads/components/creative-gallery.tsx +121 -0
  333. package/blocks/social-ads/components/post-composer.tsx +122 -0
  334. package/blocks/social-ads/components/social-post-list.tsx +116 -0
  335. package/blocks/social-ads/hooks/use-social-accounts.ts +16 -0
  336. package/blocks/social-ads/lib/social-ads-types.ts +167 -0
  337. package/blocks/social-ads/recipes/creative-to-gallery.ts +41 -0
  338. package/blocks/social-ads/recipes/schedule-post.ts +51 -0
  339. package/blocks/social-ads/recipes/sync-audience-and-launch.ts +41 -0
  340. package/blocks/visitors/README.md +56 -0
  341. package/blocks/visitors/block.json +139 -0
  342. package/blocks/visitors/components/snippet-install.tsx +169 -0
  343. package/blocks/visitors/components/traffic-chart.tsx +156 -0
  344. package/blocks/visitors/components/visitor-company-card.tsx +92 -0
  345. package/blocks/visitors/components/visitor-feed.tsx +157 -0
  346. package/blocks/visitors/lib/visitors-utils.ts +78 -0
  347. package/blocks/visitors/recipes/check-snippet-installed.ts +24 -0
  348. package/blocks/visitors/recipes/recent-company-visits.ts +27 -0
  349. package/blocks/visitors/recipes/traffic-summary.ts +24 -0
  350. package/blocks/webchat/README.md +87 -0
  351. package/blocks/webchat/block.json +134 -0
  352. package/blocks/webchat/components/chat-widget-config.tsx +160 -0
  353. package/blocks/webchat/components/conversation-list.tsx +74 -0
  354. package/blocks/webchat/lib/webchat-types.ts +83 -0
  355. package/blocks/webchat/recipes/create-bot-and-style-widget.ts +24 -0
  356. package/blocks/webchat/recipes/list-chat-visitors.ts +24 -0
  357. package/blocks/workbench/README.md +135 -0
  358. package/blocks/workbench/block.json +259 -0
  359. package/blocks/workbench/components/column-recipe-editor.tsx +183 -0
  360. package/blocks/workbench/components/run-progress.tsx +132 -0
  361. package/blocks/workbench/components/workbench-add-rows.tsx +100 -0
  362. package/blocks/workbench/components/workbench-grid.tsx +159 -0
  363. package/blocks/workbench/components/workbench-row-actions.tsx +102 -0
  364. package/blocks/workbench/components/workbench-table-list.tsx +105 -0
  365. package/blocks/workbench/hooks/use-workbench-table.ts +30 -0
  366. package/blocks/workbench/lib/waterfall-presets.ts +119 -0
  367. package/blocks/workbench/lib/workbench-types.ts +191 -0
  368. package/blocks/workbench/recipes/build-enriched-table.ts +65 -0
  369. package/blocks/workbench/recipes/import-from-search.ts +42 -0
  370. package/blocks/workbench/recipes/scrape-into-table.ts +36 -0
  371. package/blocks/workbench/recipes/waterfall-column.ts +55 -0
  372. package/blocks/workflows/README.md +100 -0
  373. package/blocks/workflows/block.json +188 -0
  374. package/blocks/workflows/components/run-history.tsx +136 -0
  375. package/blocks/workflows/components/trigger-config.tsx +134 -0
  376. package/blocks/workflows/components/workflow-editor.tsx +142 -0
  377. package/blocks/workflows/components/workflow-list.tsx +153 -0
  378. package/blocks/workflows/hooks/use-workflow.ts +51 -0
  379. package/blocks/workflows/lib/graph.ts +187 -0
  380. package/blocks/workflows/recipes/create-workflow.ts +35 -0
  381. package/blocks/workflows/recipes/find-failed-runs.ts +15 -0
  382. package/blocks/workflows/recipes/run-workflow-and-wait.ts +32 -0
  383. package/blocks/workflows/recipes/set-trigger.ts +22 -0
  384. package/dist/cli.js +5330 -29
  385. package/dist/index.browser.js +7740 -0
  386. package/dist/index.browser.js.map +1 -0
  387. package/dist/index.browser.mjs +7717 -0
  388. package/dist/index.browser.mjs.map +1 -0
  389. package/dist/index.d.mts +2220 -23
  390. package/dist/index.d.ts +2220 -23
  391. package/dist/index.js +64 -3
  392. package/dist/index.js.map +1 -1
  393. package/dist/index.mjs +64 -3
  394. package/dist/index.mjs.map +1 -1
  395. package/dist/react.browser.js +7598 -0
  396. package/dist/react.browser.js.map +1 -0
  397. package/dist/react.browser.mjs +7568 -0
  398. package/dist/react.browser.mjs.map +1 -0
  399. package/dist/react.d.mts +2082 -20
  400. package/dist/react.d.ts +2082 -20
  401. package/dist/react.js +63 -2
  402. package/dist/react.js.map +1 -1
  403. package/dist/react.mjs +63 -2
  404. package/dist/react.mjs.map +1 -1
  405. package/dist/types/agentSetup.d.ts +6 -6
  406. package/dist/types/blocks/add.d.ts +53 -0
  407. package/dist/types/blocks/create.d.ts +46 -0
  408. package/dist/types/blocks/doctor.d.ts +26 -0
  409. package/dist/types/blocks/registry.d.ts +134 -0
  410. package/dist/types/cli.d.ts +16 -0
  411. package/dist/types/core.d.ts +1 -1
  412. package/dist/types/crypto-browser.d.ts +2 -0
  413. package/dist/types/crypto-node.d.ts +6 -0
  414. package/dist/types/react.d.ts +1 -1
  415. package/dist/types/version.d.ts +2 -2
  416. package/dist/types/webhooks.d.ts +2 -2
  417. package/dist/ui/index.d.mts +54 -0
  418. package/dist/ui/index.d.ts +54 -0
  419. package/dist/ui/index.js +105 -0
  420. package/dist/ui/index.js.map +1 -0
  421. package/dist/ui/index.mjs +44 -0
  422. package/dist/ui/index.mjs.map +1 -0
  423. package/dist/ui/lib/compose-refs.d.mts +19 -0
  424. package/dist/ui/lib/compose-refs.d.ts +19 -0
  425. package/dist/ui/lib/compose-refs.js +76 -0
  426. package/dist/ui/lib/compose-refs.js.map +1 -0
  427. package/dist/ui/lib/compose-refs.mjs +41 -0
  428. package/dist/ui/lib/compose-refs.mjs.map +1 -0
  429. package/dist/ui/lib/use-mobile.d.mts +4 -0
  430. package/dist/ui/lib/use-mobile.d.ts +4 -0
  431. package/dist/ui/lib/use-mobile.js +57 -0
  432. package/dist/ui/lib/use-mobile.js.map +1 -0
  433. package/dist/ui/lib/use-mobile.mjs +22 -0
  434. package/dist/ui/lib/use-mobile.mjs.map +1 -0
  435. package/dist/ui/lib/utils.d.mts +10 -0
  436. package/dist/ui/lib/utils.d.ts +10 -0
  437. package/dist/ui/lib/utils.js +33 -0
  438. package/dist/ui/lib/utils.js.map +1 -0
  439. package/dist/ui/lib/utils.mjs +9 -0
  440. package/dist/ui/lib/utils.mjs.map +1 -0
  441. package/dist/ui/primitives/alert-dialog.d.mts +22 -0
  442. package/dist/ui/primitives/alert-dialog.d.ts +22 -0
  443. package/dist/ui/primitives/alert-dialog.js +163 -0
  444. package/dist/ui/primitives/alert-dialog.js.map +1 -0
  445. package/dist/ui/primitives/alert-dialog.mjs +119 -0
  446. package/dist/ui/primitives/alert-dialog.mjs.map +1 -0
  447. package/dist/ui/primitives/avatar.d.mts +8 -0
  448. package/dist/ui/primitives/avatar.d.ts +8 -0
  449. package/dist/ui/primitives/avatar.js +91 -0
  450. package/dist/ui/primitives/avatar.js.map +1 -0
  451. package/dist/ui/primitives/avatar.mjs +55 -0
  452. package/dist/ui/primitives/avatar.mjs.map +1 -0
  453. package/dist/ui/primitives/breadcrumb.d.mts +13 -0
  454. package/dist/ui/primitives/breadcrumb.d.ts +13 -0
  455. package/dist/ui/primitives/breadcrumb.js +127 -0
  456. package/dist/ui/primitives/breadcrumb.js.map +1 -0
  457. package/dist/ui/primitives/breadcrumb.mjs +97 -0
  458. package/dist/ui/primitives/breadcrumb.mjs.map +1 -0
  459. package/dist/ui/primitives/button.d.mts +15 -0
  460. package/dist/ui/primitives/button.d.ts +15 -0
  461. package/dist/ui/primitives/button.js +91 -0
  462. package/dist/ui/primitives/button.js.map +1 -0
  463. package/dist/ui/primitives/button.mjs +56 -0
  464. package/dist/ui/primitives/button.mjs.map +1 -0
  465. package/dist/ui/primitives/card.d.mts +10 -0
  466. package/dist/ui/primitives/card.d.ts +10 -0
  467. package/dist/ui/primitives/card.js +101 -0
  468. package/dist/ui/primitives/card.js.map +1 -0
  469. package/dist/ui/primitives/card.mjs +62 -0
  470. package/dist/ui/primitives/card.mjs.map +1 -0
  471. package/dist/ui/primitives/carousel.d.mts +45 -0
  472. package/dist/ui/primitives/carousel.d.ts +45 -0
  473. package/dist/ui/primitives/carousel.js +300 -0
  474. package/dist/ui/primitives/carousel.js.map +1 -0
  475. package/dist/ui/primitives/carousel.mjs +261 -0
  476. package/dist/ui/primitives/carousel.mjs.map +1 -0
  477. package/dist/ui/primitives/chart.d.mts +68 -0
  478. package/dist/ui/primitives/chart.d.ts +68 -0
  479. package/dist/ui/primitives/chart.js +280 -0
  480. package/dist/ui/primitives/chart.js.map +1 -0
  481. package/dist/ui/primitives/chart.mjs +241 -0
  482. package/dist/ui/primitives/chart.mjs.map +1 -0
  483. package/dist/ui/primitives/collapsible.d.mts +8 -0
  484. package/dist/ui/primitives/collapsible.d.ts +8 -0
  485. package/dist/ui/primitives/collapsible.js +72 -0
  486. package/dist/ui/primitives/collapsible.js.map +1 -0
  487. package/dist/ui/primitives/collapsible.mjs +36 -0
  488. package/dist/ui/primitives/collapsible.mjs.map +1 -0
  489. package/dist/ui/primitives/context-menu.d.mts +29 -0
  490. package/dist/ui/primitives/context-menu.d.ts +29 -0
  491. package/dist/ui/primitives/context-menu.js +203 -0
  492. package/dist/ui/primitives/context-menu.js.map +1 -0
  493. package/dist/ui/primitives/context-menu.mjs +155 -0
  494. package/dist/ui/primitives/context-menu.mjs.map +1 -0
  495. package/dist/ui/primitives/dialog.d.mts +26 -0
  496. package/dist/ui/primitives/dialog.d.ts +26 -0
  497. package/dist/ui/primitives/dialog.js +192 -0
  498. package/dist/ui/primitives/dialog.js.map +1 -0
  499. package/dist/ui/primitives/dialog.mjs +148 -0
  500. package/dist/ui/primitives/dialog.mjs.map +1 -0
  501. package/dist/ui/primitives/drawer.d.mts +26 -0
  502. package/dist/ui/primitives/drawer.d.ts +26 -0
  503. package/dist/ui/primitives/drawer.js +146 -0
  504. package/dist/ui/primitives/drawer.js.map +1 -0
  505. package/dist/ui/primitives/drawer.mjs +103 -0
  506. package/dist/ui/primitives/drawer.mjs.map +1 -0
  507. package/dist/ui/primitives/input.d.mts +5 -0
  508. package/dist/ui/primitives/input.d.ts +5 -0
  509. package/dist/ui/primitives/input.js +46 -0
  510. package/dist/ui/primitives/input.js.map +1 -0
  511. package/dist/ui/primitives/input.mjs +22 -0
  512. package/dist/ui/primitives/input.mjs.map +1 -0
  513. package/dist/ui/primitives/item.d.mts +27 -0
  514. package/dist/ui/primitives/item.d.ts +27 -0
  515. package/dist/ui/primitives/item.js +221 -0
  516. package/dist/ui/primitives/item.js.map +1 -0
  517. package/dist/ui/primitives/item.mjs +188 -0
  518. package/dist/ui/primitives/item.mjs.map +1 -0
  519. package/dist/ui/primitives/kbd.d.mts +15 -0
  520. package/dist/ui/primitives/kbd.d.ts +15 -0
  521. package/dist/ui/primitives/kbd.js +45 -0
  522. package/dist/ui/primitives/kbd.js.map +1 -0
  523. package/dist/ui/primitives/kbd.mjs +21 -0
  524. package/dist/ui/primitives/kbd.mjs.map +1 -0
  525. package/dist/ui/primitives/label.d.mts +6 -0
  526. package/dist/ui/primitives/label.d.ts +6 -0
  527. package/dist/ui/primitives/label.js +55 -0
  528. package/dist/ui/primitives/label.js.map +1 -0
  529. package/dist/ui/primitives/label.mjs +21 -0
  530. package/dist/ui/primitives/label.mjs.map +1 -0
  531. package/dist/ui/primitives/resizable.d.mts +10 -0
  532. package/dist/ui/primitives/resizable.d.ts +10 -0
  533. package/dist/ui/primitives/resizable.js +68 -0
  534. package/dist/ui/primitives/resizable.js.map +1 -0
  535. package/dist/ui/primitives/resizable.mjs +42 -0
  536. package/dist/ui/primitives/resizable.mjs.map +1 -0
  537. package/dist/ui/primitives/save-status.d.mts +31 -0
  538. package/dist/ui/primitives/save-status.d.ts +31 -0
  539. package/dist/ui/primitives/save-status.js +150 -0
  540. package/dist/ui/primitives/save-status.js.map +1 -0
  541. package/dist/ui/primitives/save-status.mjs +126 -0
  542. package/dist/ui/primitives/save-status.mjs.map +1 -0
  543. package/dist/ui/primitives/separator.d.mts +6 -0
  544. package/dist/ui/primitives/separator.d.ts +6 -0
  545. package/dist/ui/primitives/separator.js +62 -0
  546. package/dist/ui/primitives/separator.js.map +1 -0
  547. package/dist/ui/primitives/separator.mjs +28 -0
  548. package/dist/ui/primitives/separator.mjs.map +1 -0
  549. package/dist/ui/primitives/sheet.d.mts +16 -0
  550. package/dist/ui/primitives/sheet.d.ts +16 -0
  551. package/dist/ui/primitives/sheet.js +170 -0
  552. package/dist/ui/primitives/sheet.js.map +1 -0
  553. package/dist/ui/primitives/sheet.mjs +129 -0
  554. package/dist/ui/primitives/sheet.mjs.map +1 -0
  555. package/dist/ui/primitives/sidebar.d.mts +74 -0
  556. package/dist/ui/primitives/sidebar.d.ts +74 -0
  557. package/dist/ui/primitives/sidebar.js +697 -0
  558. package/dist/ui/primitives/sidebar.js.map +1 -0
  559. package/dist/ui/primitives/sidebar.mjs +651 -0
  560. package/dist/ui/primitives/sidebar.mjs.map +1 -0
  561. package/dist/ui/primitives/skeleton.d.mts +5 -0
  562. package/dist/ui/primitives/skeleton.d.ts +5 -0
  563. package/dist/ui/primitives/skeleton.js +40 -0
  564. package/dist/ui/primitives/skeleton.js.map +1 -0
  565. package/dist/ui/primitives/skeleton.mjs +16 -0
  566. package/dist/ui/primitives/skeleton.mjs.map +1 -0
  567. package/dist/ui/primitives/slider.d.mts +20 -0
  568. package/dist/ui/primitives/slider.d.ts +20 -0
  569. package/dist/ui/primitives/slider.js +112 -0
  570. package/dist/ui/primitives/slider.js.map +1 -0
  571. package/dist/ui/primitives/slider.mjs +78 -0
  572. package/dist/ui/primitives/slider.mjs.map +1 -0
  573. package/dist/ui/primitives/sortable.d.mts +50 -0
  574. package/dist/ui/primitives/sortable.d.ts +50 -0
  575. package/dist/ui/primitives/sortable.js +456 -0
  576. package/dist/ui/primitives/sortable.js.map +1 -0
  577. package/dist/ui/primitives/sortable.mjs +435 -0
  578. package/dist/ui/primitives/sortable.mjs.map +1 -0
  579. package/dist/ui/primitives/switch.d.mts +6 -0
  580. package/dist/ui/primitives/switch.d.ts +6 -0
  581. package/dist/ui/primitives/switch.js +67 -0
  582. package/dist/ui/primitives/switch.js.map +1 -0
  583. package/dist/ui/primitives/switch.mjs +33 -0
  584. package/dist/ui/primitives/switch.mjs.map +1 -0
  585. package/dist/ui/primitives/table.d.mts +12 -0
  586. package/dist/ui/primitives/table.d.ts +12 -0
  587. package/dist/ui/primitives/table.js +130 -0
  588. package/dist/ui/primitives/table.js.map +1 -0
  589. package/dist/ui/primitives/table.mjs +89 -0
  590. package/dist/ui/primitives/table.mjs.map +1 -0
  591. package/dist/ui/primitives/tabs.d.mts +9 -0
  592. package/dist/ui/primitives/tabs.d.ts +9 -0
  593. package/dist/ui/primitives/tabs.js +86 -0
  594. package/dist/ui/primitives/tabs.js.map +1 -0
  595. package/dist/ui/primitives/tabs.mjs +49 -0
  596. package/dist/ui/primitives/tabs.mjs.map +1 -0
  597. package/dist/ui/primitives/textarea.d.mts +5 -0
  598. package/dist/ui/primitives/textarea.d.ts +5 -0
  599. package/dist/ui/primitives/textarea.js +43 -0
  600. package/dist/ui/primitives/textarea.js.map +1 -0
  601. package/dist/ui/primitives/textarea.mjs +19 -0
  602. package/dist/ui/primitives/textarea.mjs.map +1 -0
  603. package/dist/ui/primitives/tooltip.d.mts +9 -0
  604. package/dist/ui/primitives/tooltip.d.ts +9 -0
  605. package/dist/ui/primitives/tooltip.js +94 -0
  606. package/dist/ui/primitives/tooltip.js.map +1 -0
  607. package/dist/ui/primitives/tooltip.mjs +57 -0
  608. package/dist/ui/primitives/tooltip.mjs.map +1 -0
  609. package/docs/agents.md +72 -29
  610. package/docs/api/README.md +7 -3
  611. package/docs/api/appObjects.d.ts +3 -1
  612. package/docs/api/appObjects.md +2 -1
  613. package/docs/api/by-path.md +51 -0
  614. package/docs/api/dataPipelines.d.ts +149 -0
  615. package/docs/api/dataPipelines.md +232 -0
  616. package/docs/api/forms.d.ts +16 -0
  617. package/docs/api/forms.md +8 -0
  618. package/docs/api/objects.d.ts +705 -5
  619. package/docs/api/objects.md +1099 -14
  620. package/docs/api/ops.d.ts +1 -1
  621. package/docs/api/ops.md +1 -1
  622. package/docs/api/processLists.d.ts +247 -0
  623. package/docs/api/processLists.md +409 -0
  624. package/docs/api/quotes.d.ts +3 -1
  625. package/docs/api/quotes.md +7 -2
  626. package/docs/api/radar.d.ts +4 -0
  627. package/docs/api/radar.md +1 -0
  628. package/docs/api/reports.d.ts +476 -0
  629. package/docs/api/reports.md +539 -0
  630. package/docs/api/scheduleBuy.d.ts +12 -8
  631. package/docs/api/scheduleBuy.md +13 -10
  632. package/docs/api/seo.d.ts +406 -3
  633. package/docs/api/seo.md +505 -1
  634. package/docs/api/workspaceBackup.d.ts +324 -0
  635. package/docs/api/workspaceBackup.md +427 -0
  636. package/package.json +174 -5
  637. package/stacks/6sense.json +46 -0
  638. package/stacks/README.md +20 -0
  639. package/stacks/apollo.json +128 -0
  640. package/stacks/clay.json +59 -0
  641. package/stacks/gong.json +72 -0
  642. package/stacks/hubspot.json +162 -0
  643. package/stacks/intercom.json +53 -0
  644. package/stacks/mailchimp.json +71 -0
  645. package/stacks/outreach.json +86 -0
  646. package/stacks/salesforce.json +94 -0
  647. package/stacks/stack.schema.json +32 -0
  648. package/stacks/starter.json +22 -0
  649. package/styles/styles.css +2 -0
  650. package/styles/theme.css +181 -0
  651. package/styles/tokens.css +284 -0
  652. package/templates/next-app/README.md.tmpl +35 -0
  653. package/templates/next-app/_pages/redirect-page.tsx.tmpl +5 -0
  654. package/templates/next-app/_pages/route-page.tsx.tmpl +9 -0
  655. package/templates/next-app/app/globals.css.tmpl +8 -0
  656. package/templates/next-app/app/layout.tsx.tmpl +19 -0
  657. package/templates/next-app/components/app-shell.tsx.tmpl +34 -0
  658. package/templates/next-app/dot-env.local.example.tmpl +6 -0
  659. package/templates/next-app/dot-gitignore.tmpl +7 -0
  660. package/templates/next-app/lib/nav.ts.tmpl +2 -0
  661. package/templates/next-app/next.config.mjs.tmpl +4 -0
  662. package/templates/next-app/package.json.tmpl +25 -0
  663. package/templates/next-app/postcss.config.mjs.tmpl +4 -0
  664. package/templates/next-app/tsconfig.json.tmpl +21 -0
@@ -0,0 +1,153 @@
1
+ "use client";
2
+
3
+ /**
4
+ * WorkflowList: the workspace's workflows with run, enable/disable, create and delete.
5
+ *
6
+ * SDK operations, in the order the screen uses them:
7
+ * 1. listWorkflows GET /workflows (read; `{ actions, total }`)
8
+ * 2. createWorkflow POST /workflows (external: a saved workflow can start acting on triggers)
9
+ * 3. planWorkflow POST /workflows/{id}/plan (read; what a run would do, touch and cost)
10
+ * 4. executeWorkflow POST /workflows/{id}/execute (external: a run can message people and change records)
11
+ * 5. updateWorkflow PUT /workflows/{id} (external; here only `{ enabled }`)
12
+ * 6. deleteWorkflow DELETE /workflows/{id} (destructive)
13
+ *
14
+ * Run shows the plan first (steps, audience, advisory credits) inside the confirm dialog, with the run input as
15
+ * JSON. The `action_id` of each row is the id every other call needs; the run response carries `execution_id`,
16
+ * which is handed to `onRunStarted` so you can open RunHistory on it.
17
+ */
18
+
19
+ import { useEffect, useState } from "react";
20
+ import { Button, Skeleton, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Textarea, Input } from "@graph8/sdk/ui";
21
+ import { g8Delete, g8Get, g8Post, g8Put } from "@/g8/lib/g8-client";
22
+ import { useG8Query } from "@/g8/hooks/use-g8-query";
23
+ import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
24
+ import { ApiError } from "@/g8/components/api-error";
25
+ import { EmptyState } from "@/g8/components/empty-state";
26
+ import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
27
+ import { blankConfig, payload, str } from "../lib/graph";
28
+ import type { Rec, WorkflowRow } from "../lib/graph";
29
+
30
+ export interface WorkflowListProps {
31
+ /** Open a workflow in your WorkflowEditor route. */
32
+ onOpen?: (actionId: string) => void;
33
+ /** Called with `(actionId, executionId)` after a run starts, so you can show RunHistory. */
34
+ onRunStarted?: (actionId: string, executionId: string) => void;
35
+ className?: string;
36
+ }
37
+
38
+ type Pending =
39
+ | { kind: "create"; name: string }
40
+ | { kind: "run"; workflow: WorkflowRow }
41
+ | { kind: "toggle"; workflow: WorkflowRow }
42
+ | { kind: "delete"; workflow: WorkflowRow };
43
+
44
+ /** Plan numbers shown in the run dialog. Every field is optional: the plan is advisory. */
45
+ function planFacts(plan: Rec): { credits?: number; audience?: number; steps: number; approval: boolean; external: boolean } {
46
+ const cost = (plan.estimated_cost ?? {}) as Rec;
47
+ const credits = (cost.estimated_for_audience ?? cost.estimated_total_per_target) as number | undefined;
48
+ const policy = (plan.policy ?? {}) as Rec;
49
+ return {
50
+ credits: typeof credits === "number" ? credits : undefined,
51
+ audience: typeof plan.audience_size === "number" ? plan.audience_size : undefined,
52
+ steps: Array.isArray(plan.steps) ? plan.steps.length : 0,
53
+ approval: policy.requires_approval === true,
54
+ external: policy.has_external_effects === true,
55
+ };
56
+ }
57
+
58
+ function RunPlan({ actionId, input, setInput }: { actionId: string; input: string; setInput: (v: string) => void }) {
59
+ const plan = useG8Query((signal) => g8Post(`/workflows/${encodeURIComponent(actionId)}/plan`, {}, { signal }), [actionId]);
60
+ const facts = planFacts(payload(plan.data));
61
+ let valid = true;
62
+ try { JSON.parse(input || "{}"); } catch { valid = false; }
63
+ return (
64
+ <div className="space-y-2 text-left">
65
+ {plan.loading ? <Skeleton className="h-4 w-48" /> : plan.error ? <p className="text-xs">The preview is unavailable; the run still works.</p> : (
66
+ <p className="text-xs" data-testid="run-plan">
67
+ {facts.steps} steps{facts.audience != null ? `, ${facts.audience.toLocaleString()} contacts in scope` : ""}
68
+ {facts.credits != null ? `, about ${Math.round(facts.credits).toLocaleString()} credits (advisory)` : ""}
69
+ {facts.approval ? ", waits for a human approval" : ""}{facts.external ? ", contacts people or systems outside graph8" : ""}.
70
+ </p>
71
+ )}
72
+ <label className="block text-xs font-medium" htmlFor="run-input">Run input (JSON)</label>
73
+ <Textarea id="run-input" rows={3} value={input} onChange={(e) => setInput(e.target.value)} aria-invalid={!valid} />
74
+ {!valid ? <p className="text-destructive text-xs">That is not valid JSON.</p> : null}
75
+ </div>
76
+ );
77
+ }
78
+
79
+ function confirmCopy(p: Pending): { tier: "external" | "destructive"; title: string; label: string; description?: string } {
80
+ if (p.kind === "create") return { tier: "external", title: `Create workflow "${p.name || "Untitled workflow"}"`, label: "Create", description: "A manual-trigger workflow with no steps yet. Add steps in the editor." };
81
+ if (p.kind === "run") return { tier: "external", title: `Run "${p.workflow.name}"`, label: "Run now" };
82
+ if (p.kind === "toggle") {
83
+ const on = p.workflow.enabled !== false;
84
+ return { tier: "external", title: `${on ? "Disable" : "Enable"} "${p.workflow.name}"`, label: on ? "Disable" : "Enable", description: on ? "Its trigger stops firing." : "Its trigger starts firing and the workflow can act on real contacts." };
85
+ }
86
+ return { tier: "destructive", title: `Delete "${p.workflow.name}"`, label: "Delete", description: "The workflow and its trigger are removed. Past runs stay in history." };
87
+ }
88
+
89
+ export function WorkflowList({ onOpen, onRunStarted, className }: WorkflowListProps) {
90
+ const list = useG8Query((signal) => g8Get("/workflows", undefined, { signal }), []);
91
+ const [name, setName] = useState("");
92
+ const [pending, setPending] = useState<Pending | null>(null);
93
+ const [input, setInput] = useState("{}");
94
+ useEffect(() => setInput("{}"), [pending]);
95
+
96
+ const run = useG8Mutation(async (p: Pending) => {
97
+ if (p.kind === "create") return g8Post("/workflows", { name: p.name || "Untitled workflow", description: "", config: blankConfig(p.name || "Untitled workflow") });
98
+ const base = `/workflows/${encodeURIComponent(p.workflow.action_id)}`;
99
+ if (p.kind === "run") return g8Post(`${base}/execute`, { input_data: JSON.parse(input || "{}") });
100
+ if (p.kind === "toggle") return g8Put(base, { enabled: p.workflow.enabled === false });
101
+ return g8Delete(base);
102
+ }, {
103
+ onSuccess: (res, p) => {
104
+ setPending(null);
105
+ if (p.kind === "create") { setName(""); const id = str(payload(res).action_id); if (id) onOpen?.(id); }
106
+ if (p.kind === "run") onRunStarted?.(p.workflow.action_id, str(payload(res).execution_id));
107
+ void list.refetch();
108
+ },
109
+ });
110
+
111
+ const rows = (Array.isArray(payload(list.data).actions) ? payload(list.data).actions : []) as WorkflowRow[];
112
+ const copy = pending ? confirmCopy(pending) : null;
113
+ let inputOk = true;
114
+ try { JSON.parse(input || "{}"); } catch { inputOk = false; }
115
+
116
+ return (
117
+ <div className={className}>
118
+ <div className="mb-4 flex items-center justify-end gap-2">
119
+ <Input aria-label="New workflow name" placeholder="Workflow name" value={name} onChange={(e) => setName(e.target.value)} className="w-64" />
120
+ <Button onClick={() => setPending({ kind: "create", name: name.trim() })}>New workflow</Button>
121
+ </div>
122
+ {list.error ? <ApiError error={list.error} onRetry={() => void list.refetch()} /> : null}
123
+ {run.error ? <ApiError error={run.error} title="That action failed" /> : null}
124
+ {list.loading && !list.data ? <div className="space-y-2" aria-busy="true">{[0, 1, 2].map((i) => <Skeleton key={i} className="h-10 w-full" />)}</div> : !list.error && rows.length === 0 ? (
125
+ <EmptyState title="No workflows yet" description="A workflow runs steps automatically when its trigger fires or when you run it. Create one, then add steps." />
126
+ ) : (
127
+ <Table>
128
+ <TableHeader><TableRow><TableHead>Name</TableHead><TableHead>Status</TableHead><TableHead>Category</TableHead><TableHead className="text-right">Actions</TableHead></TableRow></TableHeader>
129
+ <TableBody>
130
+ {rows.map((w) => (
131
+ <TableRow key={w.action_id}>
132
+ <TableCell><button type="button" className="font-medium hover:underline" onClick={() => onOpen?.(w.action_id)}>{w.name}</button>{w.description ? <div className="text-muted-foreground text-xs">{w.description}</div> : null}</TableCell>
133
+ <TableCell>{w.enabled === false ? "Disabled" : "Enabled"}</TableCell>
134
+ <TableCell>{w.category ?? ""}</TableCell>
135
+ <TableCell className="space-x-1 text-right">
136
+ <Button size="sm" variant="outline" onClick={() => setPending({ kind: "run", workflow: w })}>Run</Button>
137
+ <Button size="sm" variant="ghost" onClick={() => setPending({ kind: "toggle", workflow: w })}>{w.enabled === false ? "Enable" : "Disable"}</Button>
138
+ <Button size="sm" variant="ghost" aria-label={`Delete ${w.name}`} onClick={() => setPending({ kind: "delete", workflow: w })}>Delete</Button>
139
+ </TableCell>
140
+ </TableRow>
141
+ ))}
142
+ </TableBody>
143
+ </Table>
144
+ )}
145
+ {pending && copy ? (
146
+ <ConfirmSpendDialog open tier={copy.tier} title={copy.title} confirmLabel={copy.label} confirming={run.loading}
147
+ affected={pending.kind === "delete" ? 1 : undefined}
148
+ description={pending.kind === "run" ? <RunPlan actionId={pending.workflow.action_id} input={input} setInput={setInput} /> : copy.description}
149
+ onConfirm={() => { if (pending.kind !== "run" || inputOk) void run.mutate(pending); }} onCancel={() => setPending(null)} />
150
+ ) : null}
151
+ </div>
152
+ );
153
+ }
@@ -0,0 +1,51 @@
1
+ "use client";
2
+
3
+ /**
4
+ * useWorkflow: load one workflow and save changes to its graph.
5
+ *
6
+ * SDK operations, in order: getWorkflow (GET /workflows/{id}, read), then updateWorkflow
7
+ * (PUT /workflows/{id}, external tier: saving can start a trigger or change what a live workflow sends).
8
+ * Use `saveAndRefresh` so drafts are cleared only after the refetch landed.
9
+ * The component that calls `save` must ask for confirmation first (ConfirmSpendDialog, tier "external").
10
+ *
11
+ * const { workflow, config, save } = useWorkflow(actionId);
12
+ * await save.mutate({ config: nextConfig });
13
+ */
14
+
15
+ import { useMemo } from "react";
16
+ import { g8Get, g8Put } from "@/g8/lib/g8-client";
17
+ import { useG8Query } from "@/g8/hooks/use-g8-query";
18
+ import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
19
+ import { configOf, payload } from "../lib/graph";
20
+ import type { Rec } from "../lib/graph";
21
+
22
+ export interface WorkflowUpdate {
23
+ config?: Rec;
24
+ name?: string;
25
+ description?: string;
26
+ enabled?: boolean;
27
+ }
28
+
29
+ export function useWorkflow(actionId: string) {
30
+ const base = `/workflows/${encodeURIComponent(actionId)}`;
31
+ const query = useG8Query((signal) => g8Get(base, undefined, { signal }), [actionId]);
32
+ const save = useG8Mutation((update: WorkflowUpdate) => g8Put(base, update));
33
+ /** Save, then WAIT for the fresh copy: callers clear their drafts after this resolves, so nothing shows pre-save data. */
34
+ const saveAndRefresh = async (update: WorkflowUpdate): Promise<unknown> => {
35
+ const result = await save.mutate(update);
36
+ if (result) await query.refetch();
37
+ return result;
38
+ };
39
+ // Memoised: callers key effects on `workflow`, and a fresh `{}` per render would loop them.
40
+ const workflow = useMemo(() => payload(query.data), [query.data]);
41
+ const config = useMemo(() => configOf(workflow), [workflow]);
42
+ return {
43
+ workflow,
44
+ config,
45
+ loading: query.loading && !query.data,
46
+ error: query.error,
47
+ refetch: query.refetch,
48
+ save,
49
+ saveAndRefresh,
50
+ };
51
+ }
@@ -0,0 +1,187 @@
1
+ "use client";
2
+
3
+ /**
4
+ * Pure helpers for a workflow graph (`config` of a workflow): read it as an ordered list of steps, and edit it
5
+ * as a list. No network, no React, so the rules are easy to test and to change.
6
+ *
7
+ * A saved graph is stored TWICE (see docs/workflow-authoring-guide.md): per-node `connections` /
8
+ * `conditionalConnections` that the runtime walks, and a top-level `edges` array the canvas draws. The two must
9
+ * agree, so every structural edit here rewrites both. Key casing follows the graph you loaded (`node_id` or
10
+ * `nodeId`): the API accepts either and normalises on save, but we never mix styles inside one graph.
11
+ *
12
+ * Only LINEAR graphs (every node has at most one next node, no branches) can be restructured here. A branching
13
+ * graph can still have its steps' names and config edited; add, remove and move are disabled for it, because a
14
+ * list cannot show where a branch goes. Use the graph8 app's canvas for branching logic.
15
+ */
16
+
17
+ export type Rec = Record<string, unknown>;
18
+ export type WfNode = Rec;
19
+
20
+ export function asRec(value: unknown): Rec {
21
+ return value && typeof value === "object" && !Array.isArray(value) ? (value as Rec) : {};
22
+ }
23
+
24
+ /**
25
+ * The body of a response. Workflow routes pass the service's JSON through as-is (no `{ data }` wrapper), while most
26
+ * other graph8 routes wrap it; accept both so a later API change does not blank the screens.
27
+ */
28
+ export function payload(res: unknown): Rec {
29
+ const r = asRec(res);
30
+ return r.data && typeof r.data === "object" && !Array.isArray(r.data) ? asRec(r.data) : r;
31
+ }
32
+
33
+ export function str(value: unknown, fallback = ""): string {
34
+ return typeof value === "string" ? value : value == null ? fallback : String(value);
35
+ }
36
+
37
+ /** The graph inside a workflow record (`config`, or voice's older name `skill_config`). */
38
+ export function configOf(workflow: unknown): Rec {
39
+ const w = asRec(workflow);
40
+ return asRec(w.config ?? w.skill_config);
41
+ }
42
+
43
+ export function nodesOf(config: Rec): WfNode[] {
44
+ return Array.isArray(config.nodes) ? (config.nodes as WfNode[]) : [];
45
+ }
46
+
47
+ export const nodeIdOf = (n: WfNode): string => str(n.node_id ?? n.nodeId ?? n.id);
48
+ export const nodeTypeOf = (n: WfNode): string => str(n.node_type ?? n.nodeType ?? n.type);
49
+ export const nodeNameOf = (n: WfNode): string => str(n.name) || nodeTypeOf(n).replace(/_/g, " ") || nodeIdOf(n);
50
+ export const startIdOf = (config: Rec): string => str(config.start_node_id ?? config.startNodeId);
51
+
52
+ const nextIds = (n: WfNode): string[] => (Array.isArray(n.connections) ? (n.connections as unknown[]).map((c) => str(c)) : []);
53
+ const branches = (n: WfNode): Rec => asRec(n.conditionalConnections ?? n.conditional_connections);
54
+
55
+ /** No branches and no fan-out anywhere: the graph is one chain. */
56
+ export function isLinear(config: Rec): boolean {
57
+ return nodesOf(config).every((n) => nextIds(n).length <= 1 && Object.keys(branches(n)).length === 0);
58
+ }
59
+
60
+ /** Steps in run order: follow `connections` from the start node, then any node the walk did not reach. */
61
+ export function orderedSteps(config: Rec): WfNode[] {
62
+ const nodes = nodesOf(config);
63
+ const byId = new Map(nodes.map((n) => [nodeIdOf(n), n]));
64
+ const out: WfNode[] = [];
65
+ const seen = new Set<string>();
66
+ let cursor: WfNode | undefined = byId.get(startIdOf(config)) ?? nodes[0];
67
+ while (cursor && !seen.has(nodeIdOf(cursor))) {
68
+ seen.add(nodeIdOf(cursor));
69
+ out.push(cursor);
70
+ const next: string | undefined = [...nextIds(cursor), ...Object.values(branches(cursor)).map((v) => str(v))][0];
71
+ cursor = next ? byId.get(next) : undefined;
72
+ }
73
+ return [...out, ...nodes.filter((n) => !seen.has(nodeIdOf(n)))];
74
+ }
75
+
76
+ const snake = (config: Rec): boolean => nodesOf(config).some((n) => "node_id" in n) || "start_node_id" in config;
77
+
78
+ /** Rewrite `connections` and `edges` so the given order is the chain start -> ... -> last. */
79
+ export function relink(config: Rec, order: WfNode[]): Rec {
80
+ const linked = order.map((n, i) => {
81
+ const next = order[i + 1];
82
+ return { ...n, connections: next ? [nodeIdOf(next)] : [] };
83
+ });
84
+ const edges = order.slice(0, -1).map((n, i) => ({
85
+ id: `e-${nodeIdOf(n)}-${nodeIdOf(order[i + 1])}`,
86
+ source: nodeIdOf(n),
87
+ target: nodeIdOf(order[i + 1]),
88
+ type: "default",
89
+ }));
90
+ const startKey = snake(config) ? "start_node_id" : "startNodeId";
91
+ return { ...config, nodes: linked, edges, [startKey]: order.length ? nodeIdOf(order[0]) : "" };
92
+ }
93
+
94
+ export function setStep(config: Rec, id: string, patch: Rec): Rec {
95
+ return { ...config, nodes: nodesOf(config).map((n) => (nodeIdOf(n) === id ? { ...n, ...patch } : n)) };
96
+ }
97
+
98
+ export function uniqueId(config: Rec, type: string): string {
99
+ const taken = new Set(nodesOf(config).map(nodeIdOf));
100
+ const base = type.replace(/_/g, "-");
101
+ let i = 1;
102
+ while (taken.has(`${base}-${i}`)) i++;
103
+ return `${base}-${i}`;
104
+ }
105
+
106
+ export function newStep(config: Rec, type: string, name: string): WfNode {
107
+ const id = uniqueId(config, type);
108
+ const ids = snake(config) ? { node_id: id, node_type: type } : { nodeId: id, nodeType: type };
109
+ return { ...ids, name, position: { x: 100, y: 200 }, config: {}, connections: [] };
110
+ }
111
+
112
+ /** Insert a step after `afterId`: undefined appends to the chain, null inserts at the very start. Linear graphs only. */
113
+ export function addStep(config: Rec, step: WfNode, afterId?: string | null): Rec {
114
+ const order = orderedSteps(config);
115
+ const at = afterId === null ? 0 : afterId ? order.findIndex((n) => nodeIdOf(n) === afterId) + 1 : order.length;
116
+ return relink(config, [...order.slice(0, at), step, ...order.slice(at)]);
117
+ }
118
+
119
+ export function removeStep(config: Rec, id: string): Rec {
120
+ return relink(config, orderedSteps(config).filter((n) => nodeIdOf(n) !== id));
121
+ }
122
+
123
+ const FIXED_TYPES = new Set(["trigger", "end"]);
124
+
125
+ /** A step can move one place only if neither it nor the step it would swap with is the trigger or the end. */
126
+ export function canMove(config: Rec, id: string, delta: -1 | 1): boolean {
127
+ const order = orderedSteps(config);
128
+ const from = order.findIndex((n) => nodeIdOf(n) === id);
129
+ const to = from + delta;
130
+ if (from < 0 || to < 0 || to >= order.length) return false;
131
+ return !FIXED_TYPES.has(nodeTypeOf(order[from])) && !FIXED_TYPES.has(nodeTypeOf(order[to]));
132
+ }
133
+
134
+ export function moveStep(config: Rec, id: string, delta: -1 | 1): Rec {
135
+ if (!canMove(config, id, delta)) return config;
136
+ const order = orderedSteps(config);
137
+ const from = order.findIndex((n) => nodeIdOf(n) === id);
138
+ const to = from + delta;
139
+ const next = [...order];
140
+ [next[from], next[to]] = [next[to], next[from]];
141
+ return relink(config, next);
142
+ }
143
+
144
+ /** A new workflow: a manual (`tool_call`) trigger and an end step, ready for steps to be inserted between. */
145
+ export function blankConfig(name: string): Rec {
146
+ const trigger = { nodeId: "trigger-1", nodeType: "trigger", name: "Trigger", position: { x: 100, y: 200 }, config: { trigger_type: "tool_call" }, connections: ["end-1"] };
147
+ const end = { nodeId: "end-1", nodeType: "end", name: "End", position: { x: 400, y: 200 }, config: {}, connections: [] };
148
+ return {
149
+ metadata: { name, version: 1 },
150
+ startNodeId: "trigger-1",
151
+ nodes: [trigger, end],
152
+ edges: [{ id: "e-trigger-1-end-1", source: "trigger-1", target: "end-1", type: "default" }],
153
+ settings: { stop_on_failure: true },
154
+ };
155
+ }
156
+
157
+ export const triggerOf = (config: Rec): WfNode | undefined => nodesOf(config).find((n) => nodeTypeOf(n) === "trigger");
158
+
159
+ export interface WorkflowRow {
160
+ action_id: string;
161
+ name: string;
162
+ description?: string | null;
163
+ enabled?: boolean;
164
+ category?: string | null;
165
+ is_template?: boolean;
166
+ updated_at?: string | null;
167
+ workflow_summary?: Rec | null;
168
+ }
169
+
170
+ export interface ExecutionRow {
171
+ execution_id: string;
172
+ action_id: string;
173
+ status: string;
174
+ triggered_by?: string | null;
175
+ trigger_type?: string | null;
176
+ error_message?: string | null;
177
+ output_data?: Rec | null;
178
+ input_data?: Rec | null;
179
+ started_at?: string | null;
180
+ completed_at?: string | null;
181
+ duration_ms?: number | null;
182
+ created_at?: string | null;
183
+ }
184
+
185
+ /** Statuses after which a run will not change on its own (paused and pending_approval wait for a person). */
186
+ export const TERMINAL_STATUSES = ["completed", "failed", "stopped"];
187
+ export const WAITING_STATUSES = ["paused", "pending_approval"];
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @title Create a workflow from a list of steps, validate it, and save it
3
+ * @when The customer describes an automation ("when a form is submitted, add the contact to a list and notify
4
+ * Slack"). Read the node catalog first (getNodeTypesSchema) so every `config` uses real field names. Saving is
5
+ * external tier: a saved, enabled workflow can start acting on live events, so confirm with the person, and create
6
+ * it disabled when in doubt.
7
+ */
8
+ import { g8 } from "@graph8/sdk";
9
+
10
+ g8.init({ apiKey: process.env.G8_API_KEY! });
11
+
12
+ export interface StepSpec {
13
+ type: string;
14
+ name: string;
15
+ config: Record<string, unknown>;
16
+ }
17
+
18
+ export async function createWorkflow(name: string, triggerType: string, steps: StepSpec[]): Promise<string> {
19
+ const nodes = [
20
+ { nodeId: "trigger-1", nodeType: "trigger", name: "Trigger", position: { x: 100, y: 200 }, config: { trigger_type: triggerType } },
21
+ ...steps.map((s, i) => ({ nodeId: `step-${i + 1}`, nodeType: s.type, name: s.name, position: { x: 400 + i * 300, y: 200 }, config: s.config })),
22
+ { nodeId: "end-1", nodeType: "end", name: "End", position: { x: 400 + steps.length * 300, y: 200 }, config: {} },
23
+ ].map((n, i, all) => ({ ...n, connections: all[i + 1] ? [all[i + 1].nodeId] : [] }));
24
+ const edges = nodes.slice(0, -1).map((n, i) => ({ id: `e-${n.nodeId}-${nodes[i + 1].nodeId}`, source: n.nodeId, target: nodes[i + 1].nodeId, type: "default" }));
25
+ const config = { metadata: { name, version: 1 }, startNodeId: "trigger-1", nodes, edges };
26
+
27
+ // 1. Validate before saving: errors block the save, missing_references are ids that do not exist (fix them first).
28
+ const check = (await g8.api.workflows.validateWorkflow({ body: { config } })) as { valid?: boolean; errors?: string[] };
29
+ if (check.valid === false) throw new Error(`Workflow is not valid: ${(check.errors ?? []).join("; ")}`);
30
+
31
+ // 2. Save. Returns the new workflow's action_id, the id every other call needs.
32
+ const created = (await g8.api.workflows.createWorkflow({ body: { name, description: "", config } })) as { action_id?: string };
33
+ if (!created.action_id) throw new Error("The API did not return an action_id");
34
+ return created.action_id;
35
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @title List a workflow's failed runs with their errors
3
+ * @when Something did not happen and the customer wants to know why. Reading runs is free. Restarting a failed run
4
+ * is external tier (steps that message people run again): ask first.
5
+ */
6
+ import { g8 } from "@graph8/sdk";
7
+
8
+ g8.init({ apiKey: process.env.G8_API_KEY! });
9
+
10
+ export async function failedRuns(actionId: string, limit = 25): Promise<Array<{ executionId: string; startedAt: string; error: string }>> {
11
+ const res = (await g8.api.workflows.listWorkflowExecutions({ query: { action_id: actionId, status_filter: "failed", limit } })) as {
12
+ executions?: Array<{ execution_id: string; started_at?: string | null; created_at?: string | null; error_message?: string | null }>;
13
+ };
14
+ return (res.executions ?? []).map((e) => ({ executionId: e.execution_id, startedAt: e.started_at ?? e.created_at ?? "", error: e.error_message ?? "no error recorded" }));
15
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @title Preview a workflow run, start it, and wait for the result
3
+ * @when The customer wants to run a workflow now. The plan is free and shows steps, audience size and advisory
4
+ * credits; the run itself is external tier (it can message people), so show the plan to the person first. The run
5
+ * is asynchronous: poll the execution until it is completed, failed or stopped. `paused` and `pending_approval`
6
+ * wait for a person and never finish on their own.
7
+ */
8
+ import { g8 } from "@graph8/sdk";
9
+
10
+ g8.init({ apiKey: process.env.G8_API_KEY! });
11
+
12
+ const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
13
+ type Body = Record<string, unknown>;
14
+
15
+ export async function runWorkflow(actionId: string, input: Body = {}): Promise<Body> {
16
+ // 1. Preview. Read-only.
17
+ const plan = (await g8.api.workflows.planWorkflow({ path: { action_id: actionId }, body: {} })) as Body;
18
+ console.log("steps:", Array.isArray(plan.steps) ? plan.steps.length : 0, "audience:", plan.audience_size ?? "unknown");
19
+
20
+ // 2. Start. The response carries execution_id (outcome "unknown" means check the executions list before retrying).
21
+ const started = (await g8.api.workflows.executeWorkflow({ path: { action_id: actionId }, body: { input_data: input } })) as Body;
22
+ const executionId = String(started.execution_id ?? "");
23
+ if (!executionId) throw new Error(`Run not confirmed (outcome: ${String(started.outcome)}); list executions before retrying`);
24
+
25
+ // 3. Poll with backoff until the run settles.
26
+ for (let attempt = 0; attempt < 60; attempt++) {
27
+ const run = (await g8.api.workflows.getExecution({ path: { execution_id: executionId } })) as Body;
28
+ if (["completed", "failed", "stopped", "paused", "pending_approval"].includes(String(run.status))) return run;
29
+ await sleep(Math.min(15_000, 1_500 * 1.4 ** attempt));
30
+ }
31
+ throw new Error(`Execution ${executionId} is still running; check it later`);
32
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @title Change a workflow's trigger and skip the backlog
3
+ * @when The customer wants a different start event or interval. Updating is external tier (new events start real
4
+ * runs). Resetting the cursor skips events that arrived while the trigger was off, so say so before doing it.
5
+ */
6
+ import { g8 } from "@graph8/sdk";
7
+
8
+ g8.init({ apiKey: process.env.G8_API_KEY! });
9
+
10
+ type Node = { nodeId?: string; node_id?: string; nodeType?: string; node_type?: string; config?: Record<string, unknown> };
11
+
12
+ export async function setTrigger(actionId: string, triggerType: string, pollingMinutes?: number, resetCursor = false): Promise<void> {
13
+ const workflow = (await g8.api.workflows.getWorkflow({ path: { action_id: actionId } })) as { config?: { nodes?: Node[] } & Record<string, unknown> };
14
+ const config = workflow.config;
15
+ const nodes = config?.nodes ?? [];
16
+ const trigger = nodes.find((n) => (n.nodeType ?? n.node_type) === "trigger");
17
+ if (!config || !trigger) throw new Error("This workflow has no trigger node");
18
+
19
+ trigger.config = { ...trigger.config, trigger_type: triggerType, ...(pollingMinutes ? { polling_interval_minutes: pollingMinutes } : {}) };
20
+ await g8.api.workflows.updateWorkflow({ path: { action_id: actionId }, body: { config } });
21
+ if (resetCursor) await g8.api.workflows.resetTriggerCursor({ path: { action_id: actionId } });
22
+ }