@graph8/sdk 0.308.1 → 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 (654) 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 +1440 -21
  390. package/dist/index.d.ts +1440 -21
  391. package/dist/index.js +42 -3
  392. package/dist/index.js.map +1 -1
  393. package/dist/index.mjs +42 -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 +1340 -18
  400. package/dist/react.d.ts +1340 -18
  401. package/dist/react.js +41 -2
  402. package/dist/react.js.map +1 -1
  403. package/dist/react.mjs +41 -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 +6 -3
  611. package/docs/api/appObjects.d.ts +1 -1
  612. package/docs/api/appObjects.md +1 -1
  613. package/docs/api/by-path.md +29 -0
  614. package/docs/api/objects.d.ts +72 -5
  615. package/docs/api/objects.md +84 -7
  616. package/docs/api/processLists.d.ts +247 -0
  617. package/docs/api/processLists.md +409 -0
  618. package/docs/api/reports.d.ts +476 -0
  619. package/docs/api/reports.md +539 -0
  620. package/docs/api/scheduleBuy.d.ts +12 -8
  621. package/docs/api/scheduleBuy.md +13 -10
  622. package/docs/api/seo.d.ts +406 -3
  623. package/docs/api/seo.md +505 -1
  624. package/docs/api/workspaceBackup.d.ts +324 -0
  625. package/docs/api/workspaceBackup.md +427 -0
  626. package/package.json +174 -5
  627. package/stacks/6sense.json +46 -0
  628. package/stacks/README.md +20 -0
  629. package/stacks/apollo.json +128 -0
  630. package/stacks/clay.json +59 -0
  631. package/stacks/gong.json +72 -0
  632. package/stacks/hubspot.json +162 -0
  633. package/stacks/intercom.json +53 -0
  634. package/stacks/mailchimp.json +71 -0
  635. package/stacks/outreach.json +86 -0
  636. package/stacks/salesforce.json +94 -0
  637. package/stacks/stack.schema.json +32 -0
  638. package/stacks/starter.json +22 -0
  639. package/styles/styles.css +2 -0
  640. package/styles/theme.css +181 -0
  641. package/styles/tokens.css +284 -0
  642. package/templates/next-app/README.md.tmpl +35 -0
  643. package/templates/next-app/_pages/redirect-page.tsx.tmpl +5 -0
  644. package/templates/next-app/_pages/route-page.tsx.tmpl +9 -0
  645. package/templates/next-app/app/globals.css.tmpl +8 -0
  646. package/templates/next-app/app/layout.tsx.tmpl +19 -0
  647. package/templates/next-app/components/app-shell.tsx.tmpl +34 -0
  648. package/templates/next-app/dot-env.local.example.tmpl +6 -0
  649. package/templates/next-app/dot-gitignore.tmpl +7 -0
  650. package/templates/next-app/lib/nav.ts.tmpl +2 -0
  651. package/templates/next-app/next.config.mjs.tmpl +4 -0
  652. package/templates/next-app/package.json.tmpl +25 -0
  653. package/templates/next-app/postcss.config.mjs.tmpl +4 -0
  654. package/templates/next-app/tsconfig.json.tmpl +21 -0
@@ -0,0 +1,160 @@
1
+ "use client";
2
+
3
+ /**
4
+ * ChatWidgetConfig: pick a chat bot, style its widget, set starter questions, and get the embed code.
5
+ *
6
+ * SDK operations, in order:
7
+ * 1. listChatBots (GET /webchat/chat-bots) read (START HERE: the bot id is the chat_bot_id of every other call)
8
+ * 2. createChatBot (POST /webchat/chat-bots) write
9
+ * 3. getWebchatSettings (GET /webchat/settings?chat_bot_id=&surface=) read (404 when the bot has no theme yet)
10
+ * 4. createWebchatSettings (POST /webchat/settings) / updateWebchatSettings (PATCH) write (LIVE: the next visitor sees it, there is no publish step)
11
+ * 5. listWebchatQuestions / createWebchatQuestion read / write
12
+ * 6. deleteWebchatQuestion (DELETE /webchat/questions/{id}?chat_bot_id=) destructive -> ConfirmSpendDialog
13
+ * 7. getOrgSnippet (GET /snippet) read (the widget loads through the graph8 tracking snippet on your pages)
14
+ * Colours must be hex and sizes CSS lengths; the API also refuses an illegible text/background pair (422).
15
+ */
16
+
17
+ import { useEffect, useState } from "react";
18
+ import { Button, Card, CardContent, CardHeader, CardTitle, Input, Label, Skeleton } from "@graph8/sdk/ui";
19
+ import { ApiError } from "@/g8/components/api-error";
20
+ import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
21
+ import { EmptyState } from "@/g8/components/empty-state";
22
+ import { G8Error } from "@graph8/sdk";
23
+ import { g8Delete, g8Get, g8Patch, g8Post } from "@/g8/lib/g8-client";
24
+ import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
25
+ import { useG8Query } from "@/g8/hooks/use-g8-query";
26
+ import { isCssLength, isHexColor, pickItems, readSettings } from "../lib/webchat-types";
27
+ import type { ChatBot, StarterQuestion, WidgetSettings } from "../lib/webchat-types";
28
+
29
+ export interface ChatWidgetConfigProps {
30
+ initialBotId?: string;
31
+ /** "external" is the public website widget (default); "internal" is the in-app one. */
32
+ surface?: "external" | "internal";
33
+ }
34
+
35
+ const EMPTY: WidgetSettings = { assistantName: "", darkMode: false, chatWidth: "", borderRadius: "", assistantMessageBgColor: "", assistantMessageTextColor: "" };
36
+
37
+ function validate(s: WidgetSettings): string | null {
38
+ for (const key of ["assistantMessageBgColor", "assistantMessageTextColor"] as const) {
39
+ if (s[key] && !isHexColor(s[key] as string)) return "Colours must be hex, like #1a1a1a.";
40
+ }
41
+ for (const key of ["chatWidth", "borderRadius"] as const) {
42
+ if (s[key] && !isCssLength(s[key] as string)) return "Sizes must be CSS lengths, like 380px or 1rem.";
43
+ }
44
+ return null;
45
+ }
46
+
47
+ export function ChatWidgetConfig({ initialBotId, surface = "external" }: ChatWidgetConfigProps) {
48
+ const bots = useG8Query((signal) => g8Get<unknown>("/webchat/chat-bots", {}, { signal }).then((r) => pickItems<ChatBot>(r.data)), []);
49
+ const [botId, setBotId] = useState(initialBotId ?? "");
50
+ const [newBot, setNewBot] = useState("");
51
+ const [draft, setDraft] = useState<WidgetSettings>(EMPTY);
52
+ const [exists, setExists] = useState(false);
53
+ const [question, setQuestion] = useState("");
54
+ const [toDelete, setToDelete] = useState<StarterQuestion | null>(null);
55
+ const id = botId || bots.data?.find((b) => b.is_default)?.id || bots.data?.[0]?.id || "";
56
+ const q = { chat_bot_id: id, surface };
57
+
58
+ const settings = useG8Query((signal) => g8Get<unknown>("/webchat/settings", q, { signal }).then((r) => readSettings(r.data)).catch((e: unknown) => {
59
+ if (e instanceof G8Error && e.status === 404) return null;
60
+ throw e;
61
+ }), [id, surface], { enabled: Boolean(id) });
62
+ const questions = useG8Query((signal) => g8Get<unknown>("/webchat/questions", { chat_bot_id: id }, { signal }).then((r) => pickItems<StarterQuestion>(r.data)), [id], { enabled: Boolean(id) });
63
+ const snippet = useG8Query((signal) => g8Get<{ script_tag?: string }>("/snippet", {}, { signal }).then((r) => r.data), []);
64
+
65
+ // A half-typed question or an open delete belongs to the previous bot.
66
+ useEffect(() => {
67
+ setQuestion("");
68
+ setToDelete(null);
69
+ }, [id]);
70
+
71
+ useEffect(() => {
72
+ if (settings.loading) return;
73
+ setExists(settings.data != null);
74
+ setDraft({ ...EMPTY, ...(settings.data ?? {}) });
75
+ }, [settings.data, settings.loading, id]);
76
+
77
+ const createBot = useG8Mutation(async () => (await g8Post<ChatBot>("/webchat/chat-bots", { name: newBot.trim() })).data, { onSuccess: (b) => { setNewBot(""); setBotId(b.id); void bots.refetch(); } });
78
+ const save = useG8Mutation(async () => {
79
+ const body = { chat_bot_id: id, surface, ...Object.fromEntries(Object.entries(draft).filter(([, v]) => v !== "" && v !== undefined)) };
80
+ return exists ? g8Patch("/webchat/settings", body) : g8Post("/webchat/settings", body);
81
+ }, { onSuccess: () => void settings.refetch() });
82
+ const addQuestion = useG8Mutation(() => g8Post("/webchat/questions", { chat_bot_id: id, question: question.trim(), surface }), { onSuccess: () => { setQuestion(""); void questions.refetch(); } });
83
+ const removeQuestion = useG8Mutation((qn: StarterQuestion) => g8Delete(`/webchat/questions/${encodeURIComponent(qn.id)}`, { query: { chat_bot_id: id, expected_revision: qn.revision } }), { onSuccess: () => { setToDelete(null); void questions.refetch(); } });
84
+
85
+ if (bots.error) return <ApiError error={bots.error} title="Could not load chat bots" onRetry={() => void bots.refetch()} />;
86
+ if (bots.loading && !bots.data) return <Skeleton className="h-40 w-full" aria-busy="true" />;
87
+ const problem = validate(draft);
88
+ const set = (patch: WidgetSettings): void => setDraft((d) => ({ ...d, ...patch }));
89
+ return (
90
+ <div className="grid gap-4">
91
+ <Card>
92
+ <CardHeader><CardTitle>Chat bot</CardTitle></CardHeader>
93
+ <CardContent className="grid gap-3">
94
+ {(bots.data ?? []).length > 0 ? (
95
+ <select aria-label="Chat bot" className="border-input bg-background h-9 max-w-sm rounded-md border px-2 text-sm" value={id} onChange={(e) => setBotId(e.target.value)}>
96
+ {(bots.data ?? []).map((b) => <option key={b.id} value={b.id}>{b.name}{b.is_default ? " (default)" : ""}</option>)}
97
+ </select>
98
+ ) : <EmptyState title="No chat bot yet" description="Create one to configure its widget." />}
99
+ <form className="flex max-w-sm gap-2" onSubmit={(e) => { e.preventDefault(); if (newBot.trim()) void createBot.mutate(undefined); }}>
100
+ <Input aria-label="New bot name" placeholder="New bot name" value={newBot} onChange={(e) => setNewBot(e.target.value)} />
101
+ <Button type="submit" size="sm" variant="outline" loading={createBot.loading} disabled={!newBot.trim()}>Create bot</Button>
102
+ </form>
103
+ {createBot.error ? <ApiError error={createBot.error} title="Could not create the bot" /> : null}
104
+ </CardContent>
105
+ </Card>
106
+ {id ? (
107
+ <>
108
+ <Card>
109
+ <CardHeader><CardTitle>Widget appearance</CardTitle></CardHeader>
110
+ <CardContent className="grid gap-3">
111
+ {settings.error ? <ApiError error={settings.error} title="Could not load the widget settings" onRetry={() => void settings.refetch()} /> : null}
112
+ <p className="text-muted-foreground text-sm">Changes are live for the next visitor. {settings.loading ? "Loading..." : exists ? "" : "This bot uses default styling until you save."}</p>
113
+ <div className="grid gap-3 sm:grid-cols-2">
114
+ <div><Label htmlFor="g8-wc-name" className="mb-1">Assistant name</Label><Input id="g8-wc-name" value={draft.assistantName ?? ""} onChange={(e) => set({ assistantName: e.target.value })} /></div>
115
+ <div><Label htmlFor="g8-wc-width" className="mb-1">Chat width</Label><Input id="g8-wc-width" placeholder="380px" value={draft.chatWidth ?? ""} onChange={(e) => set({ chatWidth: e.target.value })} /></div>
116
+ <div><Label htmlFor="g8-wc-radius" className="mb-1">Corner radius</Label><Input id="g8-wc-radius" placeholder="12px" value={draft.borderRadius ?? ""} onChange={(e) => set({ borderRadius: e.target.value })} /></div>
117
+ <div><Label htmlFor="g8-wc-bg" className="mb-1">Reply background</Label><Input id="g8-wc-bg" placeholder="#f4f4f5" value={draft.assistantMessageBgColor ?? ""} onChange={(e) => set({ assistantMessageBgColor: e.target.value })} /></div>
118
+ <div><Label htmlFor="g8-wc-text" className="mb-1">Reply text</Label><Input id="g8-wc-text" placeholder="#111111" value={draft.assistantMessageTextColor ?? ""} onChange={(e) => set({ assistantMessageTextColor: e.target.value })} /></div>
119
+ <label className="flex items-center gap-2 self-end text-sm"><input type="checkbox" checked={Boolean(draft.darkMode)} onChange={(e) => set({ darkMode: e.target.checked })} />Dark mode</label>
120
+ </div>
121
+ {problem ? <p role="alert" className="text-sm text-red-700">{problem}</p> : null}
122
+ {save.error ? <ApiError error={save.error} title="Could not save the widget settings" /> : null}
123
+ <div><Button loading={save.loading} disabled={Boolean(problem) || settings.loading} onClick={() => void save.mutate(undefined)}>{exists ? "Save changes" : "Create widget settings"}</Button></div>
124
+ </CardContent>
125
+ </Card>
126
+ <Card>
127
+ <CardHeader><CardTitle>Starter questions</CardTitle></CardHeader>
128
+ <CardContent className="grid gap-3">
129
+ {questions.error ? <ApiError error={questions.error} title="Could not load questions" onRetry={() => void questions.refetch()} /> : (questions.data ?? []).length === 0 ? <p className="text-muted-foreground text-sm">No starter questions yet.</p> : (
130
+ <ul className="grid gap-1">{(questions.data ?? []).map((qn) => <li key={qn.id} className="flex items-center justify-between text-sm"><span>{qn.question}</span><Button size="sm" variant="ghost" onClick={() => setToDelete(qn)}>Delete</Button></li>)}</ul>
131
+ )}
132
+ <form className="flex gap-2" onSubmit={(e) => { e.preventDefault(); if (question.trim()) void addQuestion.mutate(undefined); }}>
133
+ <Input aria-label="New question" placeholder="What does it cost?" value={question} onChange={(e) => setQuestion(e.target.value)} />
134
+ <Button type="submit" size="sm" loading={addQuestion.loading} disabled={!question.trim()}>Add question</Button>
135
+ </form>
136
+ {addQuestion.error ? <ApiError error={addQuestion.error} title="Could not add the question" /> : null}
137
+ </CardContent>
138
+ </Card>
139
+ </>
140
+ ) : null}
141
+ <Card>
142
+ <CardHeader><CardTitle>Put the widget on your site</CardTitle></CardHeader>
143
+ <CardContent className="grid gap-2">
144
+ <p className="text-muted-foreground text-sm">The widget loads through the graph8 tracking snippet. Add it to every page that should show the chat.</p>
145
+ {snippet.error ? <ApiError error={snippet.error} title="Could not load the snippet" onRetry={() => void snippet.refetch()} /> : snippet.data?.script_tag ? <pre aria-label="Embed code" className="bg-muted overflow-auto rounded-md p-3 text-xs">{snippet.data.script_tag}</pre> : <Skeleton className="h-12 w-full" />}
146
+ </CardContent>
147
+ </Card>
148
+ <ConfirmSpendDialog
149
+ open={toDelete !== null}
150
+ tier="destructive"
151
+ title="Delete this starter question?"
152
+ description={<><p>{toDelete?.question}</p>{removeQuestion.error ? <ApiError error={removeQuestion.error} title="Could not delete" /> : null}</>}
153
+ confirmLabel="Delete"
154
+ confirming={removeQuestion.loading}
155
+ onCancel={() => setToDelete(null)}
156
+ onConfirm={() => { if (toDelete) void removeQuestion.mutate(toDelete); }}
157
+ />
158
+ </div>
159
+ );
160
+ }
@@ -0,0 +1,74 @@
1
+ "use client";
2
+
3
+ /**
4
+ * ConversationList: the people who used the chat widget, identified where possible, with the engagement funnel.
5
+ *
6
+ * SDK operations, in order:
7
+ * 1. listWebchatVisitors (GET /webchat/visitors?page&page_size&identified_only&search) read
8
+ * 2. getWebchatVisitorFunnel (GET /webchat/visitors/funnel?bot_id=) read (per-event counts and top pages)
9
+ * Message threads themselves live in the inbox (the `inbox` block); this list is the widget's visitors. Note the funnel's
10
+ * `abandonment_window_minutes` is accepted but not applied by the API yet, so the component does not offer it.
11
+ */
12
+
13
+ import { useState } from "react";
14
+ import { Button, Input, Skeleton, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@graph8/sdk/ui";
15
+ import { ApiError } from "@/g8/components/api-error";
16
+ import { EmptyState } from "@/g8/components/empty-state";
17
+ import { g8Get } from "@/g8/lib/g8-client";
18
+ import { useG8Query } from "@/g8/hooks/use-g8-query";
19
+ import { pickItems, visitorCompany, visitorEmail, visitorLastSeen, visitorName } from "../lib/webchat-types";
20
+ import type { Visitor } from "../lib/webchat-types";
21
+
22
+ export interface ConversationListProps {
23
+ /** Limit the funnel to one bot. */
24
+ botId?: string;
25
+ pageSize?: number;
26
+ onSelectVisitor?: (visitor: Visitor) => void;
27
+ }
28
+
29
+ export function ConversationList({ botId, pageSize = 25, onSelectVisitor }: ConversationListProps) {
30
+ const [page, setPage] = useState(1);
31
+ const [identifiedOnly, setIdentifiedOnly] = useState(false);
32
+ const [text, setText] = useState("");
33
+ const [search, setSearch] = useState("");
34
+ const visitors = useG8Query(
35
+ (signal) => g8Get<unknown>("/webchat/visitors", { page, page_size: pageSize, identified_only: identifiedOnly || undefined, search: search || undefined }, { signal }).then((r) => ({ items: pickItems<Visitor>(r.data), total: (r.data as { total?: number } | undefined)?.total })),
36
+ [page, pageSize, identifiedOnly, search],
37
+ );
38
+ const funnel = useG8Query((signal) => g8Get<Record<string, unknown>>("/webchat/visitors/funnel", { bot_id: botId }, { signal }).then((r) => r.data), [botId]);
39
+ const items = visitors.data?.items ?? [];
40
+ const counts = Object.entries((funnel.data?.counts ?? funnel.data?.events ?? {}) as Record<string, unknown>).filter(([, v]) => typeof v === "number") as Array<[string, number]>;
41
+
42
+ return (
43
+ <div className="grid gap-3">
44
+ {counts.length > 0 ? (
45
+ <dl className="grid grid-cols-2 gap-2 sm:grid-cols-4">
46
+ {counts.map(([k, v]) => <div key={k} className="rounded-md border p-3"><dt className="text-muted-foreground text-xs">{k.replace(/_/g, " ")}</dt><dd className="text-lg font-semibold">{v.toLocaleString()}</dd></div>)}
47
+ </dl>
48
+ ) : null}
49
+ <form className="flex flex-wrap items-center gap-2" onSubmit={(e) => { e.preventDefault(); setPage(1); setSearch(text.trim()); }}>
50
+ <Input aria-label="Search visitors" placeholder="Search by name or email" className="max-w-xs" value={text} onChange={(e) => setText(e.target.value)} />
51
+ <label className="flex items-center gap-2 text-sm"><input type="checkbox" checked={identifiedOnly} onChange={(e) => { setPage(1); setIdentifiedOnly(e.target.checked); }} />Identified only</label>
52
+ <Button type="submit" size="sm" variant="outline">Search</Button>
53
+ </form>
54
+ {visitors.error ? <ApiError error={visitors.error} title="Could not load visitors" onRetry={() => void visitors.refetch()} /> : visitors.loading && !visitors.data ? (
55
+ <div aria-busy="true" className="grid gap-2"><Skeleton className="h-9 w-full" /><Skeleton className="h-9 w-full" /></div>
56
+ ) : items.length === 0 ? <EmptyState title="No chat visitors yet" description="People appear here after they use the widget on your site." /> : (
57
+ <Table>
58
+ <TableHeader><TableRow><TableHead>Visitor</TableHead><TableHead>Email</TableHead><TableHead>Company</TableHead><TableHead>Last seen</TableHead></TableRow></TableHeader>
59
+ <TableBody>
60
+ {items.map((v, i) => (
61
+ <TableRow key={String(v.id ?? i)} className={onSelectVisitor ? "cursor-pointer" : undefined} onClick={() => onSelectVisitor?.(v)}>
62
+ <TableCell>{visitorName(v)}</TableCell><TableCell>{visitorEmail(v)}</TableCell><TableCell>{visitorCompany(v)}</TableCell><TableCell>{visitorLastSeen(v)}</TableCell>
63
+ </TableRow>
64
+ ))}
65
+ </TableBody>
66
+ </Table>
67
+ )}
68
+ <div className="flex justify-end">
69
+ <Button size="sm" variant="ghost" disabled={page <= 1} onClick={() => setPage(page - 1)}>Previous</Button>
70
+ <Button size="sm" variant="ghost" disabled={items.length < pageSize} onClick={() => setPage(page + 1)}>Next</Button>
71
+ </div>
72
+ </div>
73
+ );
74
+ }
@@ -0,0 +1,83 @@
1
+ "use client";
2
+
3
+ /**
4
+ * Types and helpers for the webchat block. Webchat responses are passed through from the app and only partly typed,
5
+ * so readers accept the shapes the app is known to send. The chat bot id (`chat_bot_id`) is the key almost every
6
+ * call needs: start from listChatBots.
7
+ */
8
+
9
+ export interface ChatBot {
10
+ id: string;
11
+ name: string;
12
+ is_default?: boolean;
13
+ revision?: number;
14
+ }
15
+
16
+ export interface WidgetSettings {
17
+ assistantName?: string;
18
+ darkMode?: boolean;
19
+ chatWidth?: string;
20
+ borderRadius?: string;
21
+ assistantMessageBgColor?: string;
22
+ assistantMessageTextColor?: string;
23
+ }
24
+
25
+ export interface StarterQuestion {
26
+ id: string;
27
+ question: string;
28
+ active?: boolean;
29
+ revision?: number;
30
+ }
31
+
32
+ export type Visitor = Record<string, unknown> & { id?: string | number };
33
+
34
+ export function pickItems<T>(data: unknown, key = "items"): T[] {
35
+ if (Array.isArray(data)) return data as T[];
36
+ const v = data && typeof data === "object" ? (data as Record<string, unknown>)[key] : undefined;
37
+ return Array.isArray(v) ? (v as T[]) : [];
38
+ }
39
+
40
+ /** Settings may arrive bare or wrapped in `settings`. */
41
+ export const SETTING_KEYS = ["assistantName", "darkMode", "chatWidth", "borderRadius", "assistantMessageBgColor", "assistantMessageTextColor"] as const;
42
+
43
+ export function readSettings(data: unknown): WidgetSettings {
44
+ const d = (data ?? {}) as Record<string, unknown>;
45
+ const row = ((d.settings as Record<string, unknown> | undefined) ?? d) || {};
46
+ // Only the fields this screen edits: a PATCH must stay a real partial update, never echo back the whole row.
47
+ const out: Record<string, unknown> = {};
48
+ for (const key of SETTING_KEYS) if (row[key] !== undefined && row[key] !== null) out[key] = row[key];
49
+ return out as WidgetSettings;
50
+ }
51
+
52
+ export function isHexColor(value: string): boolean {
53
+ return /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(value.trim());
54
+ }
55
+
56
+ export function isCssLength(value: string): boolean {
57
+ return /^\d+(\.\d+)?(px|rem|em|%|vw|vh)$/.test(value.trim());
58
+ }
59
+
60
+ function firstText(v: Visitor, keys: string[]): string {
61
+ for (const k of keys) {
62
+ const x = v[k];
63
+ if (typeof x === "string" && x) return x;
64
+ }
65
+ return "";
66
+ }
67
+
68
+ export function visitorName(v: Visitor): string {
69
+ return firstText(v, ["name", "full_name", "display_name", "email", "visitor_id"]) || `Visitor ${v.id ?? ""}`.trim();
70
+ }
71
+
72
+ export function visitorEmail(v: Visitor): string {
73
+ return firstText(v, ["email", "work_email"]);
74
+ }
75
+
76
+ export function visitorCompany(v: Visitor): string {
77
+ return firstText(v, ["company", "company_name", "company_domain"]);
78
+ }
79
+
80
+ export function visitorLastSeen(v: Visitor): string {
81
+ const t = firstText(v, ["last_seen_at", "last_seen", "last_message_at", "updated_at"]);
82
+ return t && !Number.isNaN(new Date(t).getTime()) ? new Date(t).toLocaleString() : t;
83
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @title Create a chat bot, style its widget and add starter questions
3
+ * @when First-time chat setup. Settings are LIVE the moment they are saved (no publish step), so style a new bot
4
+ * before the snippet is on production pages. A bot with no answering agent answers nothing until one is mapped.
5
+ */
6
+ import { g8 } from "@graph8/sdk";
7
+
8
+ g8.init({ apiKey: process.env.G8_API_KEY! });
9
+
10
+ // 1. The bot id is the chat_bot_id every other call needs.
11
+ const bot = await g8.api.webChat.createChatBot({ body: { name: "Website chat" } });
12
+ const chatBotId = (bot.data as { id?: string }).id;
13
+ if (!chatBotId) throw new Error("No bot id returned");
14
+
15
+ // 2. ONE settings row per bot and surface; POST creates it (409 if it exists: use PATCH then).
16
+ await g8.api.webChat.createWebchatSettings({
17
+ body: { chat_bot_id: chatBotId, surface: "external", assistantName: "Ada", chatWidth: "380px", borderRadius: "12px", assistantMessageBgColor: "#f4f4f5", assistantMessageTextColor: "#111111" },
18
+ });
19
+
20
+ // 3. Starter questions shown in the launcher.
21
+ for (const question of ["What does it cost?", "Can I book a demo?"]) {
22
+ await g8.api.webChat.createWebchatQuestion({ body: { chat_bot_id: chatBotId, question, surface: "external" } });
23
+ }
24
+ console.log("bot", chatBotId);
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @title List identified chat visitors and the engagement funnel
3
+ * @when A daily pull of people who chatted, to route hot ones to sales. Visitors come back as open dicts, so read
4
+ * fields defensively. The funnel's `abandonment_window_minutes` is accepted but not applied by the API yet.
5
+ */
6
+ import { g8 } from "@graph8/sdk";
7
+
8
+ g8.init({ apiKey: process.env.G8_API_KEY! });
9
+
10
+ export async function identifiedVisitors(max = 200): Promise<Array<Record<string, unknown>>> {
11
+ const out: Array<Record<string, unknown>> = [];
12
+ for (let page = 1; out.length < max; page += 1) {
13
+ const res = await g8.api.webChat.listWebchatVisitors({ query: { page, page_size: 50, identified_only: true } });
14
+ const items = (res.data as { items?: Array<Record<string, unknown>> }).items ?? [];
15
+ out.push(...items);
16
+ if (items.length < 50) break;
17
+ }
18
+ return out.slice(0, max);
19
+ }
20
+
21
+ export async function funnel(botId?: string): Promise<unknown> {
22
+ const res = await g8.api.webChat.getWebchatVisitorFunnel({ query: { bot_id: botId } });
23
+ return res.data;
24
+ }
@@ -0,0 +1,135 @@
1
+ # workbench
2
+
3
+ A Clay-style table: rows are records (people or companies), columns are plain data or the output of a recipe (an AI
4
+ prompt, a provider waterfall, a formula). You add rows, add recipe columns, **price a run for free**, confirm the
5
+ price, run it, and watch it finish. Finished rows go to a list. Builds on `enrichment` (provider catalog) and `lists`
6
+ (the destination picker).
7
+
8
+ Install: `npx @graph8/sdk add workbench` (installs `foundation`, `crm`, `enrichment` and `lists` first).
9
+
10
+ ## What you get
11
+
12
+ | Component | Use it for | Props |
13
+ |---|---|---|
14
+ | `WorkbenchTableList` | Pick, create (people or companies) or delete a workbench | `onOpen(table)` (optional: omit it and the table opens in a `WorkbenchGrid` inside the component) |
15
+ | `WorkbenchGrid` | The table: select rows, edit cells, add rows or columns, run recipe columns, act on the selection | `tableId`, `pageSize` |
16
+ | `WorkbenchAddRows` | Type a row or paste a spreadsheet (header line = columns) | `tableId`, `columns`, `onAdded` |
17
+ | `ColumnRecipeEditor` | Add an AI, enrichment, formula or plain column; map your columns to graph8 targets | `tableId`, `columns`, `entityType`, `onSaved` |
18
+ | `RunProgress` | Quote, confirm, run and follow one recipe column | `tableId`, `target`, `rowIds`, `label`, `onFinished` |
19
+ | `WorkbenchRowActions` | Unlock locked rows, send rows to a list, delete rows | `tableId`, `rowIds`, `entityType`, `onChanged` |
20
+
21
+ Hook `useWorkbenchTable(tableId)` (table + columns). `WorkbenchGrid` already composes the other four panels around one
22
+ table; use them alone only when you build your own layout.
23
+
24
+ ## Cost, in plain numbers (every billable step is confirmed)
25
+
26
+ | Action | Cost | Confirmed with |
27
+ |---|---|---|
28
+ | Add/paste/edit rows, add columns, save an AI prompt or a waterfall | free (saving never runs anything) | nothing |
29
+ | Run an AI or waterfall column | the quote's `required_credits` (free to ask); graph8-key providers spend, your own keys do not | `ConfirmSpendDialog` (billable) with that number |
30
+ | Unlock locked rows | 1 credit per locked row (quote first) | `ConfirmSpendDialog` (billable) |
31
+ | Import people from a search | billable; rows land LOCKED (see `recipes/import-from-search.ts`) | confirm in your UI |
32
+ | Scrape the web into the table | per result, plus enrichment if `auto_enrich` (see `recipes/scrape-into-table.ts`) | confirm in your UI |
33
+ | Delete rows or a table | free, irreversible | `ConfirmSpendDialog` (destructive) |
34
+
35
+ The quote endpoints also return `available_credits` and a shortfall, so the dialog can say you cannot afford a run.
36
+
37
+ ## The flow, with the ids that pass between steps
38
+
39
+ 1. `GET /workbench/tables` -> pick or `POST /workbench/tables { name, entity_type }` -> `table_id` (a UUID string).
40
+ 2. Rows: `POST .../import/paste { headers, rows }` (creates a column per header) or `POST .../rows { data }` where `data` is keyed
41
+ by the column's **machine name** (`^[a-z][a-z0-9_]*$`), not its display name. Read `GET .../columns` first.
42
+ 3. Columns: `POST .../columns { name, display_name }` -> `{ id }` (the **numeric** column id); AI prompt:
43
+ `PUT .../columns/{id}/ai-config { prompt, usecase }`, where `{{machine_name}}` in the prompt reads another cell of the row.
44
+ Waterfall: map your columns to graph8 targets (`PUT .../columns/{id}/mapping { mapping_target: "CONTACT_FIRST_NAME" }`), then
45
+ `POST .../waterfall/configs { config }`. Afterwards `GET .../columns` shows the new column with `enrichment_type` `ai` or `waterfall`.
46
+ 4. Quote (free): `POST .../ai/validate-credits { column_id, row_ids? }` or `POST .../waterfall/configs/{column_id}/validate-credits`
47
+ -> `required_credits`, `available_credits`, `estimated_records`, `quote_id`, `quote_expires_at` (about 5 minutes).
48
+ 5. A person confirms. Run: `POST .../columns/{id}/ai/run` or `.../waterfall/configs/{column_id}/run` with
49
+ `{ approved_max_credits: required_credits, quote_expires_at, quote_id, row_ids? }` -> 202 `{ job_id }`. **No `row_ids` = every row.**
50
+ An expired quote, or a price that grew since, is refused until you re-quote.
51
+ 6. Poll `GET .../history?limit=10` (`useJobPoll`, backoff; there is no stream): the run is the receipt whose `external_ref` is the
52
+ `job_id`; it is final at `completed`, `partial`, `failed` or `cancelled`. `GET .../analytics` gives rows per status meanwhile.
53
+ 7. Read results with `GET .../rows?start_row&end_row` (a recipe writes `extracted_data`, imports write `raw_data`), then
54
+ `POST .../export { row_ids, list_id }` to send them to a list.
55
+
56
+ Locked rows (`credit_charged: false`) hold masked cells (`"***"`): unlock them (quote, confirm, 1 credit per row) before using the values.
57
+
58
+ ```tsx
59
+ "use client";
60
+ import { useState } from "react";
61
+ import { WorkbenchTableList } from "@/components/g8/workbench/workbench-table-list";
62
+ import { WorkbenchGrid } from "@/components/g8/workbench/workbench-grid";
63
+
64
+ export function Workbench() {
65
+ const [tableId, setTableId] = useState<string | null>(null);
66
+ return tableId ? <WorkbenchGrid tableId={tableId} /> : <WorkbenchTableList onOpen={(t) => setTableId(t.id)} />;
67
+ }
68
+ ```
69
+
70
+ (`add` flattens `components/` into `components/g8/workbench/`; adjust the alias to your app.)
71
+
72
+ ## Prerequisites (a human must do these)
73
+
74
+ - `enrichment-and-lists-installed` (app): the CLI installs them.
75
+ - `proxy-allowlist` (app): `allowedPrefixes` includes `workbench`, `enrichment`, `lists`.
76
+ - `provider-keys` (api): providers graph8 does not fund need the workspace's own key in graph8 settings (no API for it).
77
+ - `credits` (api): the workspace needs credits for AI columns, graph8-funded providers, scraping and unlocking.
78
+
79
+ ## Defaults the agent can use without asking
80
+
81
+ | Decision | Default |
82
+ |---|---|
83
+ | Rows a run covers | the selected rows; with no selection, every row (quote and dialog say so) |
84
+ | Waterfall auto-update on new rows | off: saving a waterfall never starts spending by itself |
85
+ | Waterfall providers and order | the preset's order, own key if configured else graph8's |
86
+ | AI column web research | off |
87
+ | Where finished rows go | a list from the picker in the row actions panel |
88
+
89
+ Ask the person: who approves a run's price (the proxy's `authorize` decides who may call at all).
90
+
91
+ ## Recipes
92
+
93
+ | File | Does |
94
+ |---|---|
95
+ | `recipes/build-enriched-table.ts` | Create a table, paste rows, add an AI column, quote, run, poll, read the rows |
96
+ | `recipes/waterfall-column.ts` | Map columns, save a work-email waterfall, quote, run |
97
+ | `recipes/import-from-search.ts` | Free search preview, billable import (locked rows), unlock quote and unlock, export to a list |
98
+ | `recipes/scrape-into-table.ts` | Free credit estimate, run the built-in scraper with it as the cap, poll the run |
99
+
100
+ ## Operations
101
+
102
+ | Operation | Method and path | Tier | Used by |
103
+ |---|---|---|---|
104
+ | addWorkbenchColumn | POST /workbench/tables/{table_id}/columns | write | ColumnRecipeEditor |
105
+ | addWorkbenchRow | POST /workbench/tables/{table_id}/rows | write | WorkbenchAddRows |
106
+ | bulkDeleteWorkbenchRows | POST /workbench/tables/{table_id}/rows/bulk-delete | destructive | WorkbenchRowActions |
107
+ | createWorkbenchFormulaColumns | POST /workbench/tables/{table_id}/formula-columns | write | ColumnRecipeEditor |
108
+ | createWorkbenchTable | POST /workbench/tables | write | WorkbenchTableList |
109
+ | deleteWorkbenchTable | DELETE /workbench/tables/{table_id} | destructive | WorkbenchTableList |
110
+ | estimateWorkbenchScraperCredits | GET /workbench/scraper/credit-estimate | read | recipes |
111
+ | exportWorkbenchTable | POST /workbench/tables/{table_id}/export | write | WorkbenchRowActions |
112
+ | getWorkbenchAnalytics | GET /workbench/tables/{table_id}/analytics | read | RunProgress |
113
+ | getWorkbenchHistory | GET /workbench/tables/{table_id}/history | read | RunProgress |
114
+ | getWorkbenchScraperRun | GET /workbench/tables/{table_id}/scraper/runs/{run_id} | read | recipes |
115
+ | getWorkbenchTable | GET /workbench/tables/{table_id} | read | WorkbenchGrid, useWorkbenchTable |
116
+ | importWorkbenchFromSearch | POST /workbench/tables/{table_id}/import/search | billable | recipes |
117
+ | listEnrichmentProviders | GET /enrichment/providers | read | ColumnRecipeEditor |
118
+ | listWorkbenchColumns | GET /workbench/tables/{table_id}/columns | read | WorkbenchGrid, useWorkbenchTable |
119
+ | listWorkbenchRows | GET /workbench/tables/{table_id}/rows | read | WorkbenchGrid |
120
+ | listWorkbenchTables | GET /workbench/tables | read | WorkbenchTableList |
121
+ | listWorkbenchTargetFields | GET /workbench/target-fields | read | ColumnRecipeEditor |
122
+ | mapWorkbenchColumn | PUT /workbench/tables/{table_id}/columns/{column_id}/mapping | write | ColumnRecipeEditor |
123
+ | pasteImportWorkbenchRows | POST /workbench/tables/{table_id}/import/paste | write | WorkbenchAddRows |
124
+ | previewWorkbenchSearchImport | POST /workbench/tables/{table_id}/import/search/preview | read | recipes |
125
+ | quoteWorkbenchAiRun | POST /workbench/tables/{table_id}/ai/validate-credits | read | RunProgress |
126
+ | quoteWorkbenchUnlock | POST /workbench/tables/{table_id}/unlock-quote | read | WorkbenchRowActions |
127
+ | quoteWorkbenchWaterfallRun | POST /workbench/tables/{table_id}/waterfall/configs/{column_id}/validate-credits | read | RunProgress |
128
+ | runWorkbenchColumnAi | POST /workbench/tables/{table_id}/columns/{col_id}/ai/run | billable | RunProgress |
129
+ | runWorkbenchDefaultAi | POST /workbench/tables/{table_id}/ai/run | billable | RunProgress |
130
+ | runWorkbenchScraper | POST /workbench/tables/{table_id}/scraper/runs | billable | recipes |
131
+ | runWorkbenchWaterfall | POST /workbench/tables/{table_id}/waterfall/configs/{column_id}/run | billable | RunProgress |
132
+ | saveWorkbenchColumnAiConfig | PUT /workbench/tables/{table_id}/columns/{col_id}/ai-config | write | ColumnRecipeEditor |
133
+ | saveWorkbenchWaterfall | POST /workbench/tables/{table_id}/waterfall/configs | write | ColumnRecipeEditor |
134
+ | unlockWorkbenchRows | POST /workbench/tables/{table_id}/unlock-rows | billable | WorkbenchRowActions |
135
+ | updateWorkbenchRow | PATCH /workbench/tables/{table_id}/rows/{row_id} | write | WorkbenchGrid |