@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.
- package/README.md +81 -0
- package/blocks/README.md +32 -0
- package/blocks/_foundation/README.md +78 -0
- package/blocks/_foundation/block.json +79 -0
- package/blocks/_foundation/components/api-error.tsx +62 -0
- package/blocks/_foundation/components/confirm-spend-dialog.tsx +142 -0
- package/blocks/_foundation/components/empty-state.tsx +24 -0
- package/blocks/_foundation/hooks/use-g8-mutation.ts +88 -0
- package/blocks/_foundation/hooks/use-g8-query.ts +87 -0
- package/blocks/_foundation/hooks/use-job-poll.ts +130 -0
- package/blocks/_foundation/hooks/use-paginated.ts +105 -0
- package/blocks/_foundation/lib/g8-client.ts +128 -0
- package/blocks/_foundation/recipes/verify-proxy-key.ts +18 -0
- package/blocks/_foundation/server/proxy.ts +233 -0
- package/blocks/_foundation/server/route.ts +30 -0
- package/blocks/agents/README.md +133 -0
- package/blocks/agents/block.json +223 -0
- package/blocks/agents/components/agent-builder.tsx +199 -0
- package/blocks/agents/components/agent-knowledge-panel.tsx +144 -0
- package/blocks/agents/components/agent-runs.tsx +175 -0
- package/blocks/agents/components/agent-skills-panel.tsx +126 -0
- package/blocks/agents/components/agent-test-console.tsx +175 -0
- package/blocks/agents/lib/agent-types.ts +114 -0
- package/blocks/agents/recipes/create-agent-and-attach-skill.ts +50 -0
- package/blocks/agents/recipes/run-and-follow-agent-run.ts +46 -0
- package/blocks/agents/recipes/test-agent-before-activating.ts +36 -0
- package/blocks/analytics/README.md +73 -0
- package/blocks/analytics/block.json +178 -0
- package/blocks/analytics/components/funnel-chart.tsx +79 -0
- package/blocks/analytics/components/kpi-tiles.tsx +74 -0
- package/blocks/analytics/components/leaderboard.tsx +86 -0
- package/blocks/analytics/components/report-builder.tsx +200 -0
- package/blocks/analytics/hooks/use-report.ts +26 -0
- package/blocks/analytics/lib/analytics-types.ts +108 -0
- package/blocks/analytics/lib/use-debounced.ts +16 -0
- package/blocks/analytics/recipes/01-weekly-outbound-report.ts +32 -0
- package/blocks/analytics/recipes/02-revenue-snapshot.ts +22 -0
- package/blocks/analytics/recipes/03-saved-report.ts +30 -0
- package/blocks/appointments/README.md +80 -0
- package/blocks/appointments/block.json +218 -0
- package/blocks/appointments/components/availability-editor.tsx +192 -0
- package/blocks/appointments/components/booking-page.tsx +154 -0
- package/blocks/appointments/components/event-type-editor.tsx +214 -0
- package/blocks/appointments/components/meeting-detail.tsx +147 -0
- package/blocks/appointments/components/meeting-list.tsx +148 -0
- package/blocks/appointments/hooks/use-available-slots.ts +29 -0
- package/blocks/appointments/hooks/use-schedules.ts +18 -0
- package/blocks/appointments/lib/appointments-types.ts +159 -0
- package/blocks/appointments/lib/idempotency.ts +30 -0
- package/blocks/appointments/recipes/01-create-event-type-and-hours.ts +27 -0
- package/blocks/appointments/recipes/02-find-slots-and-book.ts +34 -0
- package/blocks/appointments/recipes/03-bookings-digest.ts +22 -0
- package/blocks/appointments/recipes/04-calendar-invite.ts +21 -0
- package/blocks/block.schema.json +130 -0
- package/blocks/block.types.ts +109 -0
- package/blocks/campaigns/README.md +118 -0
- package/blocks/campaigns/block.json +361 -0
- package/blocks/campaigns/components/campaign-assets.tsx +180 -0
- package/blocks/campaigns/components/campaign-audience-step.tsx +91 -0
- package/blocks/campaigns/components/campaign-launch-step.tsx +147 -0
- package/blocks/campaigns/components/campaign-list.tsx +120 -0
- package/blocks/campaigns/components/campaign-metrics.tsx +93 -0
- package/blocks/campaigns/components/campaign-select.tsx +52 -0
- package/blocks/campaigns/components/campaign-sequence-step.tsx +158 -0
- package/blocks/campaigns/components/campaign-wizard.tsx +238 -0
- package/blocks/campaigns/components/form-field.tsx +29 -0
- package/blocks/campaigns/components/icp-editor.tsx +230 -0
- package/blocks/campaigns/components/persona-editor.tsx +210 -0
- package/blocks/campaigns/hooks/use-company-profile.ts +34 -0
- package/blocks/campaigns/lib/campaigns-utils.ts +155 -0
- package/blocks/campaigns/recipes/attach-audience-and-launch.ts +33 -0
- package/blocks/campaigns/recipes/campaign-report.ts +22 -0
- package/blocks/campaigns/recipes/generate-ideas-and-create-campaign.ts +51 -0
- package/blocks/campaigns/recipes/set-up-icp-and-personas.ts +37 -0
- package/blocks/coach/README.md +93 -0
- package/blocks/coach/block.json +125 -0
- package/blocks/coach/components/call-scorecard.tsx +126 -0
- package/blocks/coach/components/coaching-feed.tsx +140 -0
- package/blocks/coach/lib/coach-types.ts +116 -0
- package/blocks/coach/recipes/read-a-scorecard.ts +27 -0
- package/blocks/coach/recipes/weekly-coaching-digest.ts +53 -0
- package/blocks/copilot/README.md +99 -0
- package/blocks/copilot/block.json +151 -0
- package/blocks/copilot/components/copilot-memory.tsx +87 -0
- package/blocks/copilot/components/copilot-panel.tsx +189 -0
- package/blocks/copilot/components/copilot-team-chat.tsx +111 -0
- package/blocks/copilot/lib/copilot-types.ts +111 -0
- package/blocks/copilot/recipes/ask-the-copilot.ts +35 -0
- package/blocks/copilot/recipes/post-in-a-work-channel.ts +33 -0
- package/blocks/copilot/recipes/remember-and-recall.ts +19 -0
- package/blocks/crm/README.md +122 -0
- package/blocks/crm/block.json +378 -0
- package/blocks/crm/components/activity-timeline.tsx +99 -0
- package/blocks/crm/components/company-detail.tsx +118 -0
- package/blocks/crm/components/company-table.tsx +173 -0
- package/blocks/crm/components/contact-detail.tsx +135 -0
- package/blocks/crm/components/contact-table.tsx +217 -0
- package/blocks/crm/components/dedupe-review.tsx +164 -0
- package/blocks/crm/components/import-wizard.tsx +193 -0
- package/blocks/crm/components/notes-panel.tsx +112 -0
- package/blocks/crm/components/record-form.tsx +191 -0
- package/blocks/crm/components/task-list.tsx +107 -0
- package/blocks/crm/hooks/use-custom-fields.ts +18 -0
- package/blocks/crm/hooks/use-debounced.ts +13 -0
- package/blocks/crm/hooks/use-page-query.ts +52 -0
- package/blocks/crm/lib/crm-types.ts +136 -0
- package/blocks/crm/recipes/custom-fields-and-values.ts +28 -0
- package/blocks/crm/recipes/dedupe-and-merge.ts +30 -0
- package/blocks/crm/recipes/notes-tasks-and-activity.ts +23 -0
- package/blocks/crm/recipes/upsert-contact-with-company.ts +33 -0
- package/blocks/deals/README.md +78 -0
- package/blocks/deals/block.json +198 -0
- package/blocks/deals/components/deal-detail.tsx +134 -0
- package/blocks/deals/components/forecast-view.tsx +112 -0
- package/blocks/deals/components/pipeline-board.tsx +142 -0
- package/blocks/deals/components/stage-editor.tsx +96 -0
- package/blocks/deals/hooks/use-pipeline-deals.ts +67 -0
- package/blocks/deals/hooks/use-pipelines.ts +19 -0
- package/blocks/deals/lib/deals-types.ts +128 -0
- package/blocks/deals/recipes/01-create-deal-and-advance.ts +32 -0
- package/blocks/deals/recipes/02-pipeline-report.ts +35 -0
- package/blocks/deals/recipes/03-stale-deals-sweep.ts +20 -0
- package/blocks/dialer/README.md +114 -0
- package/blocks/dialer/block.json +260 -0
- package/blocks/dialer/components/call-log.tsx +78 -0
- package/blocks/dialer/components/call-queue.tsx +188 -0
- package/blocks/dialer/components/dialer-workspace.tsx +47 -0
- package/blocks/dialer/components/disposition-panel.tsx +75 -0
- package/blocks/dialer/components/live-calls-table.tsx +107 -0
- package/blocks/dialer/components/session-start-form.tsx +111 -0
- package/blocks/dialer/components/softphone.tsx +122 -0
- package/blocks/dialer/components/transcript-viewer.tsx +82 -0
- package/blocks/dialer/hooks/use-dialer-numbers.ts +19 -0
- package/blocks/dialer/hooks/use-dialer-session.ts +68 -0
- package/blocks/dialer/hooks/use-dispositions.ts +29 -0
- package/blocks/dialer/lib/dialer-types.ts +158 -0
- package/blocks/dialer/lib/softphone-transport.ts +67 -0
- package/blocks/dialer/lib/twilio-transport.ts +74 -0
- package/blocks/dialer/recipes/01-create-session-and-dial.ts +39 -0
- package/blocks/dialer/recipes/02-disposition-calls.ts +26 -0
- package/blocks/dialer/recipes/03-review-a-call.ts +23 -0
- package/blocks/enrichment/README.md +105 -0
- package/blocks/enrichment/block.json +204 -0
- package/blocks/enrichment/components/bulk-enrich-panel.tsx +205 -0
- package/blocks/enrichment/components/email-verify-badge.tsx +82 -0
- package/blocks/enrichment/components/enrich-button.tsx +136 -0
- package/blocks/enrichment/components/waterfall-config.tsx +127 -0
- package/blocks/enrichment/hooks/use-providers.ts +18 -0
- package/blocks/enrichment/lib/enrichment-types.ts +104 -0
- package/blocks/enrichment/recipes/bulk-enrich-and-poll.ts +41 -0
- package/blocks/enrichment/recipes/lookup-and-verify.ts +25 -0
- package/blocks/enrichment/recipes/waterfall-pipeline.ts +26 -0
- package/blocks/forms/README.md +112 -0
- package/blocks/forms/block.json +200 -0
- package/blocks/forms/components/form-builder.tsx +150 -0
- package/blocks/forms/components/form-embed.tsx +103 -0
- package/blocks/forms/components/form-mapping-editor.tsx +125 -0
- package/blocks/forms/components/site-audit-panel.tsx +73 -0
- package/blocks/forms/components/submissions-table.tsx +158 -0
- package/blocks/forms/lib/form-types.ts +127 -0
- package/blocks/forms/recipes/install-snippet-and-form.ts +21 -0
- package/blocks/forms/recipes/retry-failed-deliveries.ts +26 -0
- package/blocks/forms/recipes/route-form-to-list.ts +44 -0
- package/blocks/inbox/README.md +72 -0
- package/blocks/inbox/block.json +194 -0
- package/blocks/inbox/components/inbox-list.tsx +156 -0
- package/blocks/inbox/components/inbox-screen.tsx +49 -0
- package/blocks/inbox/components/reply-composer.tsx +183 -0
- package/blocks/inbox/components/tag-and-assign.tsx +189 -0
- package/blocks/inbox/components/thread-badge.tsx +17 -0
- package/blocks/inbox/components/thread-view.tsx +108 -0
- package/blocks/inbox/hooks/use-inbox-thread.ts +24 -0
- package/blocks/inbox/hooks/use-inbox-threads.ts +40 -0
- package/blocks/inbox/lib/idempotency.ts +30 -0
- package/blocks/inbox/lib/inbox-types.ts +100 -0
- package/blocks/inbox/recipes/01-triage-and-reply.ts +43 -0
- package/blocks/inbox/recipes/02-tag-and-assign.ts +31 -0
- package/blocks/inbox/recipes/03-hot-contacts-digest.ts +16 -0
- package/blocks/integrations/README.md +75 -0
- package/blocks/integrations/block.json +172 -0
- package/blocks/integrations/components/integration-list.tsx +165 -0
- package/blocks/integrations/components/sync-status.tsx +223 -0
- package/blocks/integrations/components/webhook-manager.tsx +300 -0
- package/blocks/integrations/lib/integrations-utils.ts +62 -0
- package/blocks/integrations/recipes/crm-sync-health.ts +19 -0
- package/blocks/integrations/recipes/push-contacts-to-crm.ts +20 -0
- package/blocks/integrations/recipes/register-webhook.ts +18 -0
- package/blocks/integrations/recipes/verify-webhook-route.ts +33 -0
- package/blocks/intent/README.md +57 -0
- package/blocks/intent/block.json +138 -0
- package/blocks/intent/components/account-intent-score.tsx +91 -0
- package/blocks/intent/components/intent-feed.tsx +215 -0
- package/blocks/intent/components/intent-keyword-manager.tsx +331 -0
- package/blocks/intent/hooks/use-account-intent.ts +46 -0
- package/blocks/intent/lib/intent-utils.ts +95 -0
- package/blocks/intent/recipes/rank-intent-accounts.ts +36 -0
- package/blocks/intent/recipes/track-competitor-domain.ts +23 -0
- package/blocks/intent/recipes/who-visited-url.ts +18 -0
- package/blocks/lifecycle/README.md +79 -0
- package/blocks/lifecycle/block.json +198 -0
- package/blocks/lifecycle/components/churn-list.tsx +92 -0
- package/blocks/lifecycle/components/customer-table.tsx +119 -0
- package/blocks/lifecycle/components/lead-queue.tsx +127 -0
- package/blocks/lifecycle/components/lifecycle-board.tsx +102 -0
- package/blocks/lifecycle/components/stage-move-dialog.tsx +75 -0
- package/blocks/lifecycle/hooks/use-debounced.ts +13 -0
- package/blocks/lifecycle/hooks/use-lifecycle-stages.ts +27 -0
- package/blocks/lifecycle/lib/lifecycle.ts +99 -0
- package/blocks/lifecycle/recipes/customer-brief.ts +23 -0
- package/blocks/lifecycle/recipes/move-company-stage.ts +17 -0
- package/blocks/lifecycle/recipes/read-lifecycle-board.ts +21 -0
- package/blocks/lifecycle/recipes/unlock-stage-contacts.ts +25 -0
- package/blocks/lists/README.md +96 -0
- package/blocks/lists/block.json +166 -0
- package/blocks/lists/components/add-to-list-button.tsx +111 -0
- package/blocks/lists/components/list-picker.tsx +94 -0
- package/blocks/lists/components/list-table.tsx +160 -0
- package/blocks/lists/components/suppression-manager.tsx +153 -0
- package/blocks/lists/recipes/create-list-and-add-contacts.ts +35 -0
- package/blocks/lists/recipes/refresh-and-check-lists.ts +21 -0
- package/blocks/lists/recipes/suppress-and-reinstate.ts +30 -0
- package/blocks/mailboxes/README.md +71 -0
- package/blocks/mailboxes/block.json +187 -0
- package/blocks/mailboxes/components/connect-mailbox.tsx +284 -0
- package/blocks/mailboxes/components/domain-purchase.tsx +271 -0
- package/blocks/mailboxes/components/mailbox-list.tsx +185 -0
- package/blocks/mailboxes/components/warmup-health.tsx +232 -0
- package/blocks/mailboxes/lib/mailboxes-utils.ts +69 -0
- package/blocks/mailboxes/recipes/buy-domain-and-mailboxes.ts +64 -0
- package/blocks/mailboxes/recipes/connect-imap-mailbox.ts +38 -0
- package/blocks/mailboxes/recipes/is-it-safe-to-send.ts +17 -0
- package/blocks/mailboxes/recipes/warmup-report.ts +31 -0
- package/blocks/newsletter/README.md +122 -0
- package/blocks/newsletter/block.json +224 -0
- package/blocks/newsletter/components/issue-form.tsx +124 -0
- package/blocks/newsletter/components/issue-scheduler.tsx +152 -0
- package/blocks/newsletter/components/newsletter-editor.tsx +131 -0
- package/blocks/newsletter/components/newsletter-stats.tsx +93 -0
- package/blocks/newsletter/components/subscriber-table.tsx +173 -0
- package/blocks/newsletter/lib/newsletter-types.ts +97 -0
- package/blocks/newsletter/recipes/create-newsletter-and-draft-issue.ts +27 -0
- package/blocks/newsletter/recipes/import-audience-and-send.ts +43 -0
- package/blocks/newsletter/recipes/issue-performance-report.ts +26 -0
- package/blocks/nurture/README.md +110 -0
- package/blocks/nurture/block.json +191 -0
- package/blocks/nurture/components/nurture-builder.tsx +114 -0
- package/blocks/nurture/components/nurture-enroll.tsx +107 -0
- package/blocks/nurture/components/nurture-form.tsx +120 -0
- package/blocks/nurture/components/nurture-stats.tsx +81 -0
- package/blocks/nurture/lib/nurture-types.ts +109 -0
- package/blocks/nurture/recipes/create-a-nurture.ts +34 -0
- package/blocks/nurture/recipes/enroll-contacts-and-launch.ts +24 -0
- package/blocks/nurture/recipes/nurture-health-check.ts +28 -0
- package/blocks/objects/README.md +120 -0
- package/blocks/objects/block.json +243 -0
- package/blocks/objects/components/attribute-field-input.tsx +82 -0
- package/blocks/objects/components/attribute-form-dialog.tsx +159 -0
- package/blocks/objects/components/object-not-found.tsx +34 -0
- package/blocks/objects/components/object-record-detail.tsx +143 -0
- package/blocks/objects/components/object-schema-editor.tsx +197 -0
- package/blocks/objects/components/object-table.tsx +130 -0
- package/blocks/objects/components/record-form-dialog.tsx +112 -0
- package/blocks/objects/components/record-roles-panel.tsx +106 -0
- package/blocks/objects/hooks/use-object-schema.ts +36 -0
- package/blocks/objects/lib/object-types.ts +214 -0
- package/blocks/objects/recipes/define-object-and-add-records.ts +24 -0
- package/blocks/objects/recipes/update-audit-and-archive-records.ts +39 -0
- package/blocks/objects/recipes/upsert-records-from-your-system.ts +30 -0
- package/blocks/pages/README.md +104 -0
- package/blocks/pages/block.json +223 -0
- package/blocks/pages/components/brand-kit-editor.tsx +152 -0
- package/blocks/pages/components/content-library.tsx +163 -0
- package/blocks/pages/components/page-editor.tsx +253 -0
- package/blocks/pages/components/page-list.tsx +159 -0
- package/blocks/pages/components/seo-report.tsx +159 -0
- package/blocks/pages/lib/studio.ts +77 -0
- package/blocks/pages/recipes/audit-site-seo.ts +28 -0
- package/blocks/pages/recipes/create-and-publish-page.ts +26 -0
- package/blocks/pages/recipes/edit-page-section.ts +25 -0
- package/blocks/pages/recipes/refresh-brand-kit.ts +22 -0
- package/blocks/quotes/README.md +79 -0
- package/blocks/quotes/block.json +183 -0
- package/blocks/quotes/components/product-picker.tsx +80 -0
- package/blocks/quotes/components/quote-builder.tsx +195 -0
- package/blocks/quotes/components/quote-list.tsx +79 -0
- package/blocks/quotes/components/quote-viewer.tsx +135 -0
- package/blocks/quotes/components/signature-status.tsx +77 -0
- package/blocks/quotes/hooks/use-debounced.ts +13 -0
- package/blocks/quotes/lib/quotes.ts +142 -0
- package/blocks/quotes/recipes/create-and-send-quote.ts +44 -0
- package/blocks/quotes/recipes/revise-sent-quote.ts +18 -0
- package/blocks/quotes/recipes/track-quote-signature.ts +22 -0
- package/blocks/radar/README.md +106 -0
- package/blocks/radar/block.json +189 -0
- package/blocks/radar/components/competitor-battle-card.tsx +53 -0
- package/blocks/radar/components/competitor-tracker.tsx +158 -0
- package/blocks/radar/components/radar-overview.tsx +67 -0
- package/blocks/radar/components/radar-report.tsx +86 -0
- package/blocks/radar/hooks/use-competitors.ts +23 -0
- package/blocks/radar/lib/radar-types.ts +124 -0
- package/blocks/radar/recipes/battle-card.ts +28 -0
- package/blocks/radar/recipes/deep-dive-report.ts +32 -0
- package/blocks/radar/recipes/marketing-snapshot.ts +18 -0
- package/blocks/radar/recipes/track-and-scan.ts +39 -0
- package/blocks/search/README.md +129 -0
- package/blocks/search/block.json +185 -0
- package/blocks/search/components/filter-builder.tsx +149 -0
- package/blocks/search/components/prospect-results.tsx +175 -0
- package/blocks/search/components/save-to-list-dialog.tsx +135 -0
- package/blocks/search/components/saved-searches.tsx +95 -0
- package/blocks/search/hooks/use-filter-options.ts +41 -0
- package/blocks/search/lib/search-types.ts +149 -0
- package/blocks/search/recipes/explore-filter-values.ts +27 -0
- package/blocks/search/recipes/saved-searches.ts +34 -0
- package/blocks/search/recipes/search-and-save-to-list.ts +37 -0
- package/blocks/sequencer/README.md +87 -0
- package/blocks/sequencer/block.json +214 -0
- package/blocks/sequencer/components/enroll-dialog.tsx +93 -0
- package/blocks/sequencer/components/sequence-builder.tsx +306 -0
- package/blocks/sequencer/components/sequence-list.tsx +200 -0
- package/blocks/sequencer/components/sequence-stats.tsx +256 -0
- package/blocks/sequencer/components/step-editor.tsx +213 -0
- package/blocks/sequencer/lib/sequencer-utils.ts +168 -0
- package/blocks/sequencer/recipes/create-multichannel-sequence.ts +51 -0
- package/blocks/sequencer/recipes/launch-and-enroll.ts +44 -0
- package/blocks/sequencer/recipes/pause-when-unsafe.ts +30 -0
- package/blocks/sequencer/recipes/sequence-report.ts +22 -0
- package/blocks/social-ads/README.md +128 -0
- package/blocks/social-ads/block.json +256 -0
- package/blocks/social-ads/components/ad-campaign-list.tsx +136 -0
- package/blocks/social-ads/components/audience-sync.tsx +150 -0
- package/blocks/social-ads/components/creative-gallery.tsx +121 -0
- package/blocks/social-ads/components/post-composer.tsx +122 -0
- package/blocks/social-ads/components/social-post-list.tsx +116 -0
- package/blocks/social-ads/hooks/use-social-accounts.ts +16 -0
- package/blocks/social-ads/lib/social-ads-types.ts +167 -0
- package/blocks/social-ads/recipes/creative-to-gallery.ts +41 -0
- package/blocks/social-ads/recipes/schedule-post.ts +51 -0
- package/blocks/social-ads/recipes/sync-audience-and-launch.ts +41 -0
- package/blocks/visitors/README.md +56 -0
- package/blocks/visitors/block.json +139 -0
- package/blocks/visitors/components/snippet-install.tsx +169 -0
- package/blocks/visitors/components/traffic-chart.tsx +156 -0
- package/blocks/visitors/components/visitor-company-card.tsx +92 -0
- package/blocks/visitors/components/visitor-feed.tsx +157 -0
- package/blocks/visitors/lib/visitors-utils.ts +78 -0
- package/blocks/visitors/recipes/check-snippet-installed.ts +24 -0
- package/blocks/visitors/recipes/recent-company-visits.ts +27 -0
- package/blocks/visitors/recipes/traffic-summary.ts +24 -0
- package/blocks/webchat/README.md +87 -0
- package/blocks/webchat/block.json +134 -0
- package/blocks/webchat/components/chat-widget-config.tsx +160 -0
- package/blocks/webchat/components/conversation-list.tsx +74 -0
- package/blocks/webchat/lib/webchat-types.ts +83 -0
- package/blocks/webchat/recipes/create-bot-and-style-widget.ts +24 -0
- package/blocks/webchat/recipes/list-chat-visitors.ts +24 -0
- package/blocks/workbench/README.md +135 -0
- package/blocks/workbench/block.json +259 -0
- package/blocks/workbench/components/column-recipe-editor.tsx +183 -0
- package/blocks/workbench/components/run-progress.tsx +132 -0
- package/blocks/workbench/components/workbench-add-rows.tsx +100 -0
- package/blocks/workbench/components/workbench-grid.tsx +159 -0
- package/blocks/workbench/components/workbench-row-actions.tsx +102 -0
- package/blocks/workbench/components/workbench-table-list.tsx +105 -0
- package/blocks/workbench/hooks/use-workbench-table.ts +30 -0
- package/blocks/workbench/lib/waterfall-presets.ts +119 -0
- package/blocks/workbench/lib/workbench-types.ts +191 -0
- package/blocks/workbench/recipes/build-enriched-table.ts +65 -0
- package/blocks/workbench/recipes/import-from-search.ts +42 -0
- package/blocks/workbench/recipes/scrape-into-table.ts +36 -0
- package/blocks/workbench/recipes/waterfall-column.ts +55 -0
- package/blocks/workflows/README.md +100 -0
- package/blocks/workflows/block.json +188 -0
- package/blocks/workflows/components/run-history.tsx +136 -0
- package/blocks/workflows/components/trigger-config.tsx +134 -0
- package/blocks/workflows/components/workflow-editor.tsx +142 -0
- package/blocks/workflows/components/workflow-list.tsx +153 -0
- package/blocks/workflows/hooks/use-workflow.ts +51 -0
- package/blocks/workflows/lib/graph.ts +187 -0
- package/blocks/workflows/recipes/create-workflow.ts +35 -0
- package/blocks/workflows/recipes/find-failed-runs.ts +15 -0
- package/blocks/workflows/recipes/run-workflow-and-wait.ts +32 -0
- package/blocks/workflows/recipes/set-trigger.ts +22 -0
- package/dist/cli.js +5330 -29
- package/dist/index.browser.js +7740 -0
- package/dist/index.browser.js.map +1 -0
- package/dist/index.browser.mjs +7717 -0
- package/dist/index.browser.mjs.map +1 -0
- package/dist/index.d.mts +2220 -23
- package/dist/index.d.ts +2220 -23
- package/dist/index.js +64 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +64 -3
- package/dist/index.mjs.map +1 -1
- package/dist/react.browser.js +7598 -0
- package/dist/react.browser.js.map +1 -0
- package/dist/react.browser.mjs +7568 -0
- package/dist/react.browser.mjs.map +1 -0
- package/dist/react.d.mts +2082 -20
- package/dist/react.d.ts +2082 -20
- package/dist/react.js +63 -2
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +63 -2
- package/dist/react.mjs.map +1 -1
- package/dist/types/agentSetup.d.ts +6 -6
- package/dist/types/blocks/add.d.ts +53 -0
- package/dist/types/blocks/create.d.ts +46 -0
- package/dist/types/blocks/doctor.d.ts +26 -0
- package/dist/types/blocks/registry.d.ts +134 -0
- package/dist/types/cli.d.ts +16 -0
- package/dist/types/core.d.ts +1 -1
- package/dist/types/crypto-browser.d.ts +2 -0
- package/dist/types/crypto-node.d.ts +6 -0
- package/dist/types/react.d.ts +1 -1
- package/dist/types/version.d.ts +2 -2
- package/dist/types/webhooks.d.ts +2 -2
- package/dist/ui/index.d.mts +54 -0
- package/dist/ui/index.d.ts +54 -0
- package/dist/ui/index.js +105 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/index.mjs +44 -0
- package/dist/ui/index.mjs.map +1 -0
- package/dist/ui/lib/compose-refs.d.mts +19 -0
- package/dist/ui/lib/compose-refs.d.ts +19 -0
- package/dist/ui/lib/compose-refs.js +76 -0
- package/dist/ui/lib/compose-refs.js.map +1 -0
- package/dist/ui/lib/compose-refs.mjs +41 -0
- package/dist/ui/lib/compose-refs.mjs.map +1 -0
- package/dist/ui/lib/use-mobile.d.mts +4 -0
- package/dist/ui/lib/use-mobile.d.ts +4 -0
- package/dist/ui/lib/use-mobile.js +57 -0
- package/dist/ui/lib/use-mobile.js.map +1 -0
- package/dist/ui/lib/use-mobile.mjs +22 -0
- package/dist/ui/lib/use-mobile.mjs.map +1 -0
- package/dist/ui/lib/utils.d.mts +10 -0
- package/dist/ui/lib/utils.d.ts +10 -0
- package/dist/ui/lib/utils.js +33 -0
- package/dist/ui/lib/utils.js.map +1 -0
- package/dist/ui/lib/utils.mjs +9 -0
- package/dist/ui/lib/utils.mjs.map +1 -0
- package/dist/ui/primitives/alert-dialog.d.mts +22 -0
- package/dist/ui/primitives/alert-dialog.d.ts +22 -0
- package/dist/ui/primitives/alert-dialog.js +163 -0
- package/dist/ui/primitives/alert-dialog.js.map +1 -0
- package/dist/ui/primitives/alert-dialog.mjs +119 -0
- package/dist/ui/primitives/alert-dialog.mjs.map +1 -0
- package/dist/ui/primitives/avatar.d.mts +8 -0
- package/dist/ui/primitives/avatar.d.ts +8 -0
- package/dist/ui/primitives/avatar.js +91 -0
- package/dist/ui/primitives/avatar.js.map +1 -0
- package/dist/ui/primitives/avatar.mjs +55 -0
- package/dist/ui/primitives/avatar.mjs.map +1 -0
- package/dist/ui/primitives/breadcrumb.d.mts +13 -0
- package/dist/ui/primitives/breadcrumb.d.ts +13 -0
- package/dist/ui/primitives/breadcrumb.js +127 -0
- package/dist/ui/primitives/breadcrumb.js.map +1 -0
- package/dist/ui/primitives/breadcrumb.mjs +97 -0
- package/dist/ui/primitives/breadcrumb.mjs.map +1 -0
- package/dist/ui/primitives/button.d.mts +15 -0
- package/dist/ui/primitives/button.d.ts +15 -0
- package/dist/ui/primitives/button.js +91 -0
- package/dist/ui/primitives/button.js.map +1 -0
- package/dist/ui/primitives/button.mjs +56 -0
- package/dist/ui/primitives/button.mjs.map +1 -0
- package/dist/ui/primitives/card.d.mts +10 -0
- package/dist/ui/primitives/card.d.ts +10 -0
- package/dist/ui/primitives/card.js +101 -0
- package/dist/ui/primitives/card.js.map +1 -0
- package/dist/ui/primitives/card.mjs +62 -0
- package/dist/ui/primitives/card.mjs.map +1 -0
- package/dist/ui/primitives/carousel.d.mts +45 -0
- package/dist/ui/primitives/carousel.d.ts +45 -0
- package/dist/ui/primitives/carousel.js +300 -0
- package/dist/ui/primitives/carousel.js.map +1 -0
- package/dist/ui/primitives/carousel.mjs +261 -0
- package/dist/ui/primitives/carousel.mjs.map +1 -0
- package/dist/ui/primitives/chart.d.mts +68 -0
- package/dist/ui/primitives/chart.d.ts +68 -0
- package/dist/ui/primitives/chart.js +280 -0
- package/dist/ui/primitives/chart.js.map +1 -0
- package/dist/ui/primitives/chart.mjs +241 -0
- package/dist/ui/primitives/chart.mjs.map +1 -0
- package/dist/ui/primitives/collapsible.d.mts +8 -0
- package/dist/ui/primitives/collapsible.d.ts +8 -0
- package/dist/ui/primitives/collapsible.js +72 -0
- package/dist/ui/primitives/collapsible.js.map +1 -0
- package/dist/ui/primitives/collapsible.mjs +36 -0
- package/dist/ui/primitives/collapsible.mjs.map +1 -0
- package/dist/ui/primitives/context-menu.d.mts +29 -0
- package/dist/ui/primitives/context-menu.d.ts +29 -0
- package/dist/ui/primitives/context-menu.js +203 -0
- package/dist/ui/primitives/context-menu.js.map +1 -0
- package/dist/ui/primitives/context-menu.mjs +155 -0
- package/dist/ui/primitives/context-menu.mjs.map +1 -0
- package/dist/ui/primitives/dialog.d.mts +26 -0
- package/dist/ui/primitives/dialog.d.ts +26 -0
- package/dist/ui/primitives/dialog.js +192 -0
- package/dist/ui/primitives/dialog.js.map +1 -0
- package/dist/ui/primitives/dialog.mjs +148 -0
- package/dist/ui/primitives/dialog.mjs.map +1 -0
- package/dist/ui/primitives/drawer.d.mts +26 -0
- package/dist/ui/primitives/drawer.d.ts +26 -0
- package/dist/ui/primitives/drawer.js +146 -0
- package/dist/ui/primitives/drawer.js.map +1 -0
- package/dist/ui/primitives/drawer.mjs +103 -0
- package/dist/ui/primitives/drawer.mjs.map +1 -0
- package/dist/ui/primitives/input.d.mts +5 -0
- package/dist/ui/primitives/input.d.ts +5 -0
- package/dist/ui/primitives/input.js +46 -0
- package/dist/ui/primitives/input.js.map +1 -0
- package/dist/ui/primitives/input.mjs +22 -0
- package/dist/ui/primitives/input.mjs.map +1 -0
- package/dist/ui/primitives/item.d.mts +27 -0
- package/dist/ui/primitives/item.d.ts +27 -0
- package/dist/ui/primitives/item.js +221 -0
- package/dist/ui/primitives/item.js.map +1 -0
- package/dist/ui/primitives/item.mjs +188 -0
- package/dist/ui/primitives/item.mjs.map +1 -0
- package/dist/ui/primitives/kbd.d.mts +15 -0
- package/dist/ui/primitives/kbd.d.ts +15 -0
- package/dist/ui/primitives/kbd.js +45 -0
- package/dist/ui/primitives/kbd.js.map +1 -0
- package/dist/ui/primitives/kbd.mjs +21 -0
- package/dist/ui/primitives/kbd.mjs.map +1 -0
- package/dist/ui/primitives/label.d.mts +6 -0
- package/dist/ui/primitives/label.d.ts +6 -0
- package/dist/ui/primitives/label.js +55 -0
- package/dist/ui/primitives/label.js.map +1 -0
- package/dist/ui/primitives/label.mjs +21 -0
- package/dist/ui/primitives/label.mjs.map +1 -0
- package/dist/ui/primitives/resizable.d.mts +10 -0
- package/dist/ui/primitives/resizable.d.ts +10 -0
- package/dist/ui/primitives/resizable.js +68 -0
- package/dist/ui/primitives/resizable.js.map +1 -0
- package/dist/ui/primitives/resizable.mjs +42 -0
- package/dist/ui/primitives/resizable.mjs.map +1 -0
- package/dist/ui/primitives/save-status.d.mts +31 -0
- package/dist/ui/primitives/save-status.d.ts +31 -0
- package/dist/ui/primitives/save-status.js +150 -0
- package/dist/ui/primitives/save-status.js.map +1 -0
- package/dist/ui/primitives/save-status.mjs +126 -0
- package/dist/ui/primitives/save-status.mjs.map +1 -0
- package/dist/ui/primitives/separator.d.mts +6 -0
- package/dist/ui/primitives/separator.d.ts +6 -0
- package/dist/ui/primitives/separator.js +62 -0
- package/dist/ui/primitives/separator.js.map +1 -0
- package/dist/ui/primitives/separator.mjs +28 -0
- package/dist/ui/primitives/separator.mjs.map +1 -0
- package/dist/ui/primitives/sheet.d.mts +16 -0
- package/dist/ui/primitives/sheet.d.ts +16 -0
- package/dist/ui/primitives/sheet.js +170 -0
- package/dist/ui/primitives/sheet.js.map +1 -0
- package/dist/ui/primitives/sheet.mjs +129 -0
- package/dist/ui/primitives/sheet.mjs.map +1 -0
- package/dist/ui/primitives/sidebar.d.mts +74 -0
- package/dist/ui/primitives/sidebar.d.ts +74 -0
- package/dist/ui/primitives/sidebar.js +697 -0
- package/dist/ui/primitives/sidebar.js.map +1 -0
- package/dist/ui/primitives/sidebar.mjs +651 -0
- package/dist/ui/primitives/sidebar.mjs.map +1 -0
- package/dist/ui/primitives/skeleton.d.mts +5 -0
- package/dist/ui/primitives/skeleton.d.ts +5 -0
- package/dist/ui/primitives/skeleton.js +40 -0
- package/dist/ui/primitives/skeleton.js.map +1 -0
- package/dist/ui/primitives/skeleton.mjs +16 -0
- package/dist/ui/primitives/skeleton.mjs.map +1 -0
- package/dist/ui/primitives/slider.d.mts +20 -0
- package/dist/ui/primitives/slider.d.ts +20 -0
- package/dist/ui/primitives/slider.js +112 -0
- package/dist/ui/primitives/slider.js.map +1 -0
- package/dist/ui/primitives/slider.mjs +78 -0
- package/dist/ui/primitives/slider.mjs.map +1 -0
- package/dist/ui/primitives/sortable.d.mts +50 -0
- package/dist/ui/primitives/sortable.d.ts +50 -0
- package/dist/ui/primitives/sortable.js +456 -0
- package/dist/ui/primitives/sortable.js.map +1 -0
- package/dist/ui/primitives/sortable.mjs +435 -0
- package/dist/ui/primitives/sortable.mjs.map +1 -0
- package/dist/ui/primitives/switch.d.mts +6 -0
- package/dist/ui/primitives/switch.d.ts +6 -0
- package/dist/ui/primitives/switch.js +67 -0
- package/dist/ui/primitives/switch.js.map +1 -0
- package/dist/ui/primitives/switch.mjs +33 -0
- package/dist/ui/primitives/switch.mjs.map +1 -0
- package/dist/ui/primitives/table.d.mts +12 -0
- package/dist/ui/primitives/table.d.ts +12 -0
- package/dist/ui/primitives/table.js +130 -0
- package/dist/ui/primitives/table.js.map +1 -0
- package/dist/ui/primitives/table.mjs +89 -0
- package/dist/ui/primitives/table.mjs.map +1 -0
- package/dist/ui/primitives/tabs.d.mts +9 -0
- package/dist/ui/primitives/tabs.d.ts +9 -0
- package/dist/ui/primitives/tabs.js +86 -0
- package/dist/ui/primitives/tabs.js.map +1 -0
- package/dist/ui/primitives/tabs.mjs +49 -0
- package/dist/ui/primitives/tabs.mjs.map +1 -0
- package/dist/ui/primitives/textarea.d.mts +5 -0
- package/dist/ui/primitives/textarea.d.ts +5 -0
- package/dist/ui/primitives/textarea.js +43 -0
- package/dist/ui/primitives/textarea.js.map +1 -0
- package/dist/ui/primitives/textarea.mjs +19 -0
- package/dist/ui/primitives/textarea.mjs.map +1 -0
- package/dist/ui/primitives/tooltip.d.mts +9 -0
- package/dist/ui/primitives/tooltip.d.ts +9 -0
- package/dist/ui/primitives/tooltip.js +94 -0
- package/dist/ui/primitives/tooltip.js.map +1 -0
- package/dist/ui/primitives/tooltip.mjs +57 -0
- package/dist/ui/primitives/tooltip.mjs.map +1 -0
- package/docs/agents.md +72 -29
- package/docs/api/README.md +7 -3
- package/docs/api/appObjects.d.ts +3 -1
- package/docs/api/appObjects.md +2 -1
- package/docs/api/by-path.md +51 -0
- package/docs/api/dataPipelines.d.ts +149 -0
- package/docs/api/dataPipelines.md +232 -0
- package/docs/api/forms.d.ts +16 -0
- package/docs/api/forms.md +8 -0
- package/docs/api/objects.d.ts +705 -5
- package/docs/api/objects.md +1099 -14
- package/docs/api/ops.d.ts +1 -1
- package/docs/api/ops.md +1 -1
- package/docs/api/processLists.d.ts +247 -0
- package/docs/api/processLists.md +409 -0
- package/docs/api/quotes.d.ts +3 -1
- package/docs/api/quotes.md +7 -2
- package/docs/api/radar.d.ts +4 -0
- package/docs/api/radar.md +1 -0
- package/docs/api/reports.d.ts +476 -0
- package/docs/api/reports.md +539 -0
- package/docs/api/scheduleBuy.d.ts +12 -8
- package/docs/api/scheduleBuy.md +13 -10
- package/docs/api/seo.d.ts +406 -3
- package/docs/api/seo.md +505 -1
- package/docs/api/workspaceBackup.d.ts +324 -0
- package/docs/api/workspaceBackup.md +427 -0
- package/package.json +174 -5
- package/stacks/6sense.json +46 -0
- package/stacks/README.md +20 -0
- package/stacks/apollo.json +128 -0
- package/stacks/clay.json +59 -0
- package/stacks/gong.json +72 -0
- package/stacks/hubspot.json +162 -0
- package/stacks/intercom.json +53 -0
- package/stacks/mailchimp.json +71 -0
- package/stacks/outreach.json +86 -0
- package/stacks/salesforce.json +94 -0
- package/stacks/stack.schema.json +32 -0
- package/stacks/starter.json +22 -0
- package/styles/styles.css +2 -0
- package/styles/theme.css +181 -0
- package/styles/tokens.css +284 -0
- package/templates/next-app/README.md.tmpl +35 -0
- package/templates/next-app/_pages/redirect-page.tsx.tmpl +5 -0
- package/templates/next-app/_pages/route-page.tsx.tmpl +9 -0
- package/templates/next-app/app/globals.css.tmpl +8 -0
- package/templates/next-app/app/layout.tsx.tmpl +19 -0
- package/templates/next-app/components/app-shell.tsx.tmpl +34 -0
- package/templates/next-app/dot-env.local.example.tmpl +6 -0
- package/templates/next-app/dot-gitignore.tmpl +7 -0
- package/templates/next-app/lib/nav.ts.tmpl +2 -0
- package/templates/next-app/next.config.mjs.tmpl +4 -0
- package/templates/next-app/package.json.tmpl +25 -0
- package/templates/next-app/postcss.config.mjs.tmpl +4 -0
- package/templates/next-app/tsconfig.json.tmpl +21 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shapes and small helpers for the workbench components.
|
|
5
|
+
*
|
|
6
|
+
* A workbench table is a spreadsheet of records (rows) with columns that are either plain data or the output of
|
|
7
|
+
* a recipe (AI prompt, enrichment waterfall, formula). Rows and columns are open dictionaries because every
|
|
8
|
+
* customer defines their own columns: read a cell with `cellValue(row, column)`, never by guessing a key.
|
|
9
|
+
*
|
|
10
|
+
* The table id is a UUID string; row and column ids are integers. Credits: the free quote endpoints return the
|
|
11
|
+
* exact `required_credits` and the live `available_credits`; every run needs the quote's numbers echoed back.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export interface WorkbenchTable {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
description?: string | null;
|
|
18
|
+
entity_type?: string | null;
|
|
19
|
+
status?: string | null;
|
|
20
|
+
row_count?: number | null;
|
|
21
|
+
column_count?: number | null;
|
|
22
|
+
auto_enrich?: boolean | null;
|
|
23
|
+
created_at?: string | null;
|
|
24
|
+
updated_at?: string | null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type ColumnType = "data" | "formula" | "raw_json" | string;
|
|
28
|
+
|
|
29
|
+
export interface WorkbenchColumn {
|
|
30
|
+
id: number;
|
|
31
|
+
/** Machine name: rows are keyed by it. `^[a-z][a-z0-9_]*$`. */
|
|
32
|
+
name: string;
|
|
33
|
+
display_name?: string | null;
|
|
34
|
+
column_type?: ColumnType;
|
|
35
|
+
data_type?: string | null;
|
|
36
|
+
mapping_target?: string | null;
|
|
37
|
+
/** Set when a recipe fills the column: "ai" or "waterfall". */
|
|
38
|
+
enrichment_type?: "ai" | "waterfall" | null;
|
|
39
|
+
enrichment_group_id?: string | null;
|
|
40
|
+
enrichment_config_id?: string | null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface WorkbenchRow {
|
|
44
|
+
id: number;
|
|
45
|
+
raw_data?: Record<string, unknown> | null;
|
|
46
|
+
extracted_data?: Record<string, unknown> | null;
|
|
47
|
+
status?: string | null;
|
|
48
|
+
error_message?: string | null;
|
|
49
|
+
/** false means the row is LOCKED: sensitive cells are masked until it is unlocked (1 credit per row). */
|
|
50
|
+
credit_charged?: boolean | null;
|
|
51
|
+
source_type?: string | null;
|
|
52
|
+
enriched_at?: string | null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The pages the API returns for rows and columns. */
|
|
56
|
+
export interface Page<T> {
|
|
57
|
+
items: T[];
|
|
58
|
+
total?: number;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface CreditQuote {
|
|
62
|
+
can_run?: boolean;
|
|
63
|
+
can_unlock?: boolean;
|
|
64
|
+
required_credits: number;
|
|
65
|
+
available_credits?: number;
|
|
66
|
+
estimated_records?: number;
|
|
67
|
+
locked_count?: number;
|
|
68
|
+
shortfall?: number | null;
|
|
69
|
+
message?: string;
|
|
70
|
+
quote_id?: string;
|
|
71
|
+
operation_id?: string;
|
|
72
|
+
quote_expires_at?: string;
|
|
73
|
+
credits_per_record?: number | null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface OperationReceipt {
|
|
77
|
+
id: string;
|
|
78
|
+
operation_type: string;
|
|
79
|
+
status: "running" | "completed" | "partial" | "failed" | "cancelled" | string;
|
|
80
|
+
counts?: Record<string, unknown>;
|
|
81
|
+
credits_charged?: number;
|
|
82
|
+
error_message?: string | null;
|
|
83
|
+
external_ref?: string | null;
|
|
84
|
+
started_at: string;
|
|
85
|
+
completed_at?: string | null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface TableAnalytics {
|
|
89
|
+
total_rows?: number;
|
|
90
|
+
status_distribution?: Record<string, number>;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** What a run targets. */
|
|
94
|
+
export type RunTarget =
|
|
95
|
+
| { kind: "ai-column"; columnId: number }
|
|
96
|
+
| { kind: "ai-default" }
|
|
97
|
+
| { kind: "waterfall"; columnId: number };
|
|
98
|
+
|
|
99
|
+
const SENSITIVE_MASK = "***";
|
|
100
|
+
|
|
101
|
+
/** The value to show for a cell: a recipe writes `extracted_data`, imports write `raw_data`. */
|
|
102
|
+
export function cellValue(row: WorkbenchRow, column: Pick<WorkbenchColumn, "name">): unknown {
|
|
103
|
+
const extracted = row.extracted_data?.[column.name];
|
|
104
|
+
return extracted !== undefined && extracted !== null && extracted !== "" ? extracted : row.raw_data?.[column.name];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function formatCell(value: unknown): string {
|
|
108
|
+
if (value == null) return "";
|
|
109
|
+
if (typeof value === "string") return value;
|
|
110
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
111
|
+
return JSON.stringify(value);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** A locked row has masked cells: do not edit, enrich or export them as if they were real values. */
|
|
115
|
+
export function isLocked(row: WorkbenchRow): boolean {
|
|
116
|
+
return row.credit_charged === false;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function isMasked(value: unknown): boolean {
|
|
120
|
+
return value === SENSITIVE_MASK;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Turn a header such as "Company Domain" into a legal machine name: `company_domain`. */
|
|
124
|
+
export function toMachineName(label: string): string {
|
|
125
|
+
const base = label.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
126
|
+
return /^[a-z]/.test(base) ? base.slice(0, 60) : `c_${base}`.replace(/_$/, "").slice(0, 60);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Split pasted spreadsheet text (tabs, else commas; quoted cells allowed) into a header row and body rows. */
|
|
130
|
+
export function parsePasted(text: string): { headers: string[]; rows: string[][] } {
|
|
131
|
+
const clean = text.replace(/\r\n?/g, "\n").trim();
|
|
132
|
+
if (!clean) return { headers: [], rows: [] };
|
|
133
|
+
const delimiter = clean.split("\n")[0].includes("\t") ? "\t" : ",";
|
|
134
|
+
const table: string[][] = [];
|
|
135
|
+
let row: string[] = [];
|
|
136
|
+
let cell = "";
|
|
137
|
+
let quoted = false;
|
|
138
|
+
for (let i = 0; i < clean.length; i++) {
|
|
139
|
+
const ch = clean[i];
|
|
140
|
+
if (quoted) {
|
|
141
|
+
if (ch === '"' && clean[i + 1] === '"') { cell += '"'; i++; }
|
|
142
|
+
else if (ch === '"') quoted = false;
|
|
143
|
+
else cell += ch;
|
|
144
|
+
} else if (ch === '"') quoted = true;
|
|
145
|
+
else if (ch === delimiter) { row.push(cell); cell = ""; }
|
|
146
|
+
else if (ch === "\n") { row.push(cell); table.push(row); row = []; cell = ""; }
|
|
147
|
+
else cell += ch;
|
|
148
|
+
}
|
|
149
|
+
row.push(cell);
|
|
150
|
+
table.push(row);
|
|
151
|
+
const [headers, ...rows] = table;
|
|
152
|
+
return { headers: headers.map((h) => h.trim()), rows: rows.filter((r) => r.some((c) => c.trim() !== "")) };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const TERMINAL = new Set(["completed", "partial", "failed", "cancelled"]);
|
|
156
|
+
|
|
157
|
+
/** A receipt in one of these states will not change any more. */
|
|
158
|
+
export function isReceiptTerminal(status: string | undefined | null): boolean {
|
|
159
|
+
return TERMINAL.has(String(status ?? "").toLowerCase());
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* The receipt that belongs to a run: matched by job id when the history echoes it (AI runs do), else the newest receipt of
|
|
164
|
+
* the right type that was NOT in the history before the run started. That comparison uses receipt ids, never the browser's
|
|
165
|
+
* clock, so a fast or slow client clock cannot hide the run's receipt or pick up another run's.
|
|
166
|
+
*/
|
|
167
|
+
export function findRunReceipt(items: OperationReceipt[], jobId: string, knownIds: readonly string[], types: string[]): OperationReceipt | undefined {
|
|
168
|
+
const byJob = items.find((r) => r.external_ref === jobId);
|
|
169
|
+
if (byJob) return byJob;
|
|
170
|
+
const known = new Set(knownIds);
|
|
171
|
+
return items
|
|
172
|
+
.filter((r) => types.includes(r.operation_type) && !known.has(r.id))
|
|
173
|
+
.sort((a, b) => Date.parse(b.started_at) - Date.parse(a.started_at))[0];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function runTypes(target: RunTarget): string[] {
|
|
177
|
+
return target.kind === "waterfall" ? ["waterfall"] : ["ai"];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Pick the array out of whichever envelope the endpoint used: `{items}`, `{rows}`, `{columns}` or a bare array. */
|
|
181
|
+
export function pageItems<T>(data: unknown): T[] {
|
|
182
|
+
if (Array.isArray(data)) return data as T[];
|
|
183
|
+
const d = (data ?? {}) as Record<string, unknown>;
|
|
184
|
+
for (const key of ["items", "rows", "columns", "tables", "results"]) if (Array.isArray(d[key])) return d[key] as T[];
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function pageTotal(data: unknown, fallback: number): number {
|
|
189
|
+
const total = (data as { total?: unknown } | null | undefined)?.total;
|
|
190
|
+
return typeof total === "number" ? total : fallback;
|
|
191
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @title Build a workbench: rows in, an AI column, a quote, a run, the results
|
|
3
|
+
* @when Starting a Clay-style table from a pasted list. Order: create the table -> paste rows (the header line
|
|
4
|
+
* creates the columns) -> add a column and its AI prompt (free) -> QUOTE the run (free) -> a human approves the
|
|
5
|
+
* price -> run it with the quote's numbers -> poll the history until the run's receipt is final -> read the rows.
|
|
6
|
+
* A run with no `row_ids` runs EVERY row, and the quote prices exactly that: read it before approving.
|
|
7
|
+
*/
|
|
8
|
+
import { g8 } from "@graph8/sdk";
|
|
9
|
+
|
|
10
|
+
g8.init({ apiKey: process.env.G8_API_KEY! });
|
|
11
|
+
|
|
12
|
+
export async function createAndFill(name: string, headers: string[], rows: string[][]) {
|
|
13
|
+
const table = await g8.api.workbench.createWorkbenchTable({ body: { name, entity_type: "company" } });
|
|
14
|
+
const tableId = (table.data as { id: string }).id;
|
|
15
|
+
await g8.api.workbench.pasteImportWorkbenchRows({ path: { table_id: tableId }, body: { headers, rows } });
|
|
16
|
+
return tableId;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Free: a column plus its prompt. `{{column_name}}` in the prompt reads another column of the same row. */
|
|
20
|
+
export async function addAiColumn(tableId: string, machineName: string, displayName: string, prompt: string) {
|
|
21
|
+
const col = await g8.api.workbench.addWorkbenchColumn({ path: { table_id: tableId }, body: { name: machineName, display_name: displayName, data_type: "text" } });
|
|
22
|
+
const columnId = (col.data as { id: number }).id;
|
|
23
|
+
await g8.api.workbench.saveWorkbenchColumnAiConfig({ path: { table_id: tableId, col_id: columnId }, body: { prompt, usecase: "content_generation" } });
|
|
24
|
+
return columnId;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface Quote { required_credits: number; available_credits?: number; estimated_records?: number; can_run?: boolean; quote_id?: string; quote_expires_at?: string }
|
|
28
|
+
|
|
29
|
+
/** Free. Show `required_credits` and `estimated_records` to a person before calling `runColumn`. */
|
|
30
|
+
export async function quoteColumn(tableId: string, columnId: number, rowIds?: number[]): Promise<Quote> {
|
|
31
|
+
const res = await g8.api.workbench.quoteWorkbenchAiRun({ path: { table_id: tableId }, body: { column_id: columnId, ...(rowIds ? { row_ids: rowIds } : {}) } });
|
|
32
|
+
return res.data as unknown as Quote;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Billable. Only with a quote a human approved; the same numbers go back as the cap. */
|
|
36
|
+
type Receipt = { id: string; status: string; external_ref?: string; operation_type: string; started_at: string; credits_charged?: number };
|
|
37
|
+
async function receipts(tableId: string) {
|
|
38
|
+
const history = await g8.api.workbench.getWorkbenchHistory({ path: { table_id: tableId }, query: { limit: 50 } });
|
|
39
|
+
return ((history.data as { items?: Receipt[] }).items) ?? [];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function runColumn(tableId: string, columnId: number, quote: Quote, rowIds?: number[]) {
|
|
43
|
+
const before = new Set((await receipts(tableId)).map((r) => r.id)); // clock-free matching: the run's receipt is the new one
|
|
44
|
+
const started = await g8.api.workbench.runWorkbenchColumnAi({
|
|
45
|
+
path: { table_id: tableId, col_id: columnId },
|
|
46
|
+
body: { approved_max_credits: quote.required_credits, quote_expires_at: quote.quote_expires_at, quote_id: quote.quote_id, ...(rowIds ? { row_ids: rowIds } : {}) },
|
|
47
|
+
});
|
|
48
|
+
const jobId = (started.data as { job_id: string }).job_id;
|
|
49
|
+
|
|
50
|
+
let delay = 2000;
|
|
51
|
+
for (let i = 0; i < 100; i++) {
|
|
52
|
+
const items = await receipts(tableId);
|
|
53
|
+
const receipt = items.find((r) => r.external_ref === jobId) ?? items.find((r) => r.operation_type === "ai" && !before.has(r.id));
|
|
54
|
+
if (receipt && ["completed", "partial", "failed", "cancelled"].includes(receipt.status)) return { jobId, receipt };
|
|
55
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
56
|
+
delay = Math.min(15000, Math.round(delay * 1.5));
|
|
57
|
+
}
|
|
58
|
+
throw new Error(`run ${jobId} did not finish in time; keep polling the history later`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Cells: a recipe writes `extracted_data`, imports write `raw_data`. Rows with credit_charged false are locked and masked. */
|
|
62
|
+
export async function readRows(tableId: string, endRow = 100) {
|
|
63
|
+
const res = await g8.api.workbench.listWorkbenchRows({ path: { table_id: tableId }, query: { start_row: 0, end_row: endRow } });
|
|
64
|
+
return (res.data as { items: Array<{ id: number; raw_data?: Record<string, unknown>; extracted_data?: Record<string, unknown>; credit_charged?: boolean }> }).items;
|
|
65
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @title Import people from a prospect search, unlock the ones you want, send them to a list
|
|
3
|
+
* @when Filling a workbench from graph8's people index. The preview is free; the import is BILLABLE and lands
|
|
4
|
+
* LOCKED rows (names and companies visible, emails and phones masked as "***"). Order: preview (free) ->
|
|
5
|
+
* a human approves -> import -> quote the unlock (free) -> a human approves -> unlock (1 credit per row) ->
|
|
6
|
+
* export the rows to a list. Never treat "***" as a real value, and never unlock more rows than were approved.
|
|
7
|
+
*/
|
|
8
|
+
import { g8 } from "@graph8/sdk";
|
|
9
|
+
|
|
10
|
+
g8.init({ apiKey: process.env.G8_API_KEY! });
|
|
11
|
+
|
|
12
|
+
const FILTERS = [
|
|
13
|
+
{ field: "job_title", operator: "any_of", value: ["VP Sales", "Head of Sales"] },
|
|
14
|
+
{ field: "company_industry", operator: "any_of", value: ["computer software"] },
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
export async function preview(tableId: string) {
|
|
18
|
+
const res = await g8.api.workbench.previewWorkbenchSearchImport({ path: { table_id: tableId }, body: { filters: FILTERS, sample_size: 10 } });
|
|
19
|
+
return res.data;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Billable. `max_result` is the ceiling on the rows imported. */
|
|
23
|
+
export async function importPeople(tableId: string, maxResult = 100) {
|
|
24
|
+
return g8.api.workbench.importWorkbenchFromSearch({ path: { table_id: tableId }, body: { filters: FILTERS, max_result: maxResult, skip_duplicates: true } });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function quoteUnlock(tableId: string, rowIds: number[]) {
|
|
28
|
+
const res = await g8.api.workbench.quoteWorkbenchUnlock({ path: { table_id: tableId }, body: { row_ids: rowIds } });
|
|
29
|
+
return res.data as unknown as { operation_id: string; locked_count: number; required_credits: number; available_credits: number; can_unlock: boolean; quote_expires_at: string };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Billable, 1 credit per locked row. Send the SAME row_ids the quote priced. */
|
|
33
|
+
export async function unlock(tableId: string, rowIds: number[], quote: Awaited<ReturnType<typeof quoteUnlock>>) {
|
|
34
|
+
return g8.api.workbench.unlockWorkbenchRows({
|
|
35
|
+
path: { table_id: tableId },
|
|
36
|
+
body: { row_ids: rowIds, approved_max_credits: quote.required_credits, quote_expires_at: quote.quote_expires_at, operation_id: quote.operation_id },
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function exportToNewList(tableId: string, rowIds: number[], listName: string) {
|
|
41
|
+
return g8.api.workbench.exportWorkbenchTable({ path: { table_id: tableId }, body: { row_ids: rowIds, create_new_list: true, new_list_name: listName, entity_type: "contact" } });
|
|
42
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @title Scrape the web into a table with the built-in scraper
|
|
3
|
+
* @when Collecting a list of companies or people from a description ("seed-stage fintechs in Berlin"). The
|
|
4
|
+
* scraper charges per result and `auto_enrich` adds enrichment of every result on top, so quote first. Order:
|
|
5
|
+
* credit estimate (free) -> a human approves -> run with the estimate as the cap -> poll the run -> read the rows.
|
|
6
|
+
* The estimate has no quote id: the cap and an expiry you choose go back on the run.
|
|
7
|
+
*/
|
|
8
|
+
import { g8 } from "@graph8/sdk";
|
|
9
|
+
|
|
10
|
+
g8.init({ apiKey: process.env.G8_API_KEY! });
|
|
11
|
+
|
|
12
|
+
export async function estimate(tableId: string, maxResults: number, autoEnrich = false) {
|
|
13
|
+
const res = await g8.api.workbench.estimateWorkbenchScraperCredits({ query: { table_id: tableId, max_results: maxResults, auto_enrich: autoEnrich } });
|
|
14
|
+
return (res.data as { estimated_credits: number }).estimated_credits;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Billable. Only after a person approved `credits`. */
|
|
18
|
+
export async function run(tableId: string, prompt: string, maxResults: number, credits: number, autoEnrich = false) {
|
|
19
|
+
const started = await g8.api.workbench.runWorkbenchScraper({
|
|
20
|
+
path: { table_id: tableId },
|
|
21
|
+
body: { prompt, max_results: maxResults, auto_enrich: autoEnrich, approved_max_credits: credits, quote_expires_at: new Date(Date.now() + 4 * 60_000).toISOString() },
|
|
22
|
+
});
|
|
23
|
+
return started.data as { run_id?: string; status?: string };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function waitForRun(tableId: string, runId: string) {
|
|
27
|
+
let delay = 3000;
|
|
28
|
+
for (let i = 0; i < 80; i++) {
|
|
29
|
+
const res = await g8.api.workbench.getWorkbenchScraperRun({ path: { table_id: tableId, run_id: runId } });
|
|
30
|
+
const status = String((res.data as { status?: string }).status ?? "");
|
|
31
|
+
if (status && !["pending", "running"].includes(status)) return res.data;
|
|
32
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
33
|
+
delay = Math.min(20000, Math.round(delay * 1.4));
|
|
34
|
+
}
|
|
35
|
+
throw new Error(`scraper run ${runId} did not finish in time`);
|
|
36
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @title Add an enrichment waterfall column, price it, run it
|
|
3
|
+
* @when Finding work emails (or phones) for a table of people. Map the table's columns to graph8 targets
|
|
4
|
+
* first (first name, last name, website): a step whose inputs are not mapped finds nothing. Saving a waterfall
|
|
5
|
+
* is free and never runs it. Order: map columns -> save the waterfall (steps copied from graph8's own templates)
|
|
6
|
+
* -> QUOTE (free) -> a human approves -> run with the quote's numbers. Providers on graph8's key spend credits;
|
|
7
|
+
* providers on your own key do not (check `GET /enrichment/providers`).
|
|
8
|
+
*/
|
|
9
|
+
import { g8 } from "@graph8/sdk";
|
|
10
|
+
|
|
11
|
+
g8.init({ apiKey: process.env.G8_API_KEY! });
|
|
12
|
+
|
|
13
|
+
/** columns: { column id -> graph8 target }, for example { 12: "CONTACT_FIRST_NAME", 13: "CONTACT_LAST_NAME", 14: "COMPANY_WEBSITE" }. */
|
|
14
|
+
export async function mapColumns(tableId: string, columns: Record<number, string>) {
|
|
15
|
+
for (const [id, target] of Object.entries(columns)) {
|
|
16
|
+
await g8.api.workbench.mapWorkbenchColumn({ path: { table_id: tableId, column_id: Number(id) }, body: { mapping_target: target } });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const NAMES = { first_name: "CONTACT_FIRST_NAME", last_name: "CONTACT_LAST_NAME", domain: "COMPANY_WEBSITE" };
|
|
21
|
+
|
|
22
|
+
export async function saveWorkEmailWaterfall(tableId: string, name = "Work email") {
|
|
23
|
+
const step = (provider: string, action: string, display: string, output: string, order: number) => ({
|
|
24
|
+
id: `step_${provider}_${order}`, provider, action, action_display_name: display, output_field: output,
|
|
25
|
+
input_mapping: NAMES, order, enabled: true, is_system_provider: true, stop_on_success: true,
|
|
26
|
+
});
|
|
27
|
+
return g8.api.workbench.saveWorkbenchWaterfall({
|
|
28
|
+
path: { table_id: tableId },
|
|
29
|
+
body: {
|
|
30
|
+
config: {
|
|
31
|
+
name, column_id: `waterfall_${crypto.randomUUID().replace(/-/g, "_")}`, field_to_enrich: name, data_type: "text",
|
|
32
|
+
steps: [step("leadmagic", "email_finder", "Find Email", "email", 0), step("hunter", "find_email", "Find Email", "data.email", 1)],
|
|
33
|
+
skip_existing_values: true, auto_update: false,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** After saving, read the columns: the waterfall column has enrichment_type "waterfall" and a numeric id. */
|
|
40
|
+
export async function waterfallColumnId(tableId: string) {
|
|
41
|
+
const cols = await g8.api.workbench.listWorkbenchColumns({ path: { table_id: tableId } });
|
|
42
|
+
const items = (cols.data as { items: Array<{ id: number; enrichment_type?: string }> }).items;
|
|
43
|
+
return items.find((c) => c.enrichment_type === "waterfall")?.id;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function quoteAndRun(tableId: string, columnId: number, approve: (credits: number, rows: number) => Promise<boolean>) {
|
|
47
|
+
const quote = (await g8.api.workbench.quoteWorkbenchWaterfallRun({ path: { table_id: tableId, column_id: columnId }, body: { column_id: columnId } })).data as unknown as {
|
|
48
|
+
required_credits: number; estimated_records?: number; quote_id?: string; quote_expires_at?: string; can_run?: boolean;
|
|
49
|
+
};
|
|
50
|
+
if (quote.can_run === false || !(await approve(quote.required_credits, quote.estimated_records ?? 0))) return null;
|
|
51
|
+
return g8.api.workbench.runWorkbenchWaterfall({
|
|
52
|
+
path: { table_id: tableId, column_id: columnId },
|
|
53
|
+
body: { approved_max_credits: quote.required_credits, quote_expires_at: quote.quote_expires_at, quote_id: quote.quote_id },
|
|
54
|
+
});
|
|
55
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# workflows
|
|
2
|
+
|
|
3
|
+
Editable React screens for automations: a workflow list, a step-list editor (not a canvas), trigger
|
|
4
|
+
configuration and run history. Install with `npx @graph8/sdk add workflows`.
|
|
5
|
+
|
|
6
|
+
| Component | What it is |
|
|
7
|
+
|---|---|
|
|
8
|
+
| `WorkflowList` | Workflows with run (plan preview + JSON input), enable/disable, create, delete. |
|
|
9
|
+
| `WorkflowEditor` | Ordered steps: rename, edit JSON config, and on linear workflows add/move/remove steps. Save is confirmed. |
|
|
10
|
+
| `TriggerConfig` | Trigger type, polling interval, filters, live trigger status, reset cursor. |
|
|
11
|
+
| `RunHistory` | Runs with status filter, polled detail, pause / resume / stop / restart. |
|
|
12
|
+
| `useWorkflow` (hook) | Loads one workflow and saves its graph. |
|
|
13
|
+
|
|
14
|
+
## Flow, in order (and the ids passed between steps)
|
|
15
|
+
|
|
16
|
+
1. `GET /workflows` returns `{ actions: [...], total }`. Each row's `action_id` is the **workflow id** for every later call.
|
|
17
|
+
2. Create: `POST /workflows` with `{ name, description, config }` (`external`, confirmed). The new workflow starts as a manual
|
|
18
|
+
(`tool_call`) trigger and an end step; the response `action_id` goes to `onOpen`.
|
|
19
|
+
3. Edit: `GET /workflows/{action_id}` -> `config` = `{ nodes, edges, startNodeId | start_node_id, ... }`. Saving sends the whole `config`
|
|
20
|
+
back with `PUT /workflows/{action_id}`. Structure lives in two places (per-node `connections` and top-level `edges`); `lib/graph.ts`
|
|
21
|
+
rewrites both together. The "Add step" list comes from `GET /workflows/node-types/schema` (deprecated types are hidden).
|
|
22
|
+
4. Trigger: `GET /workflows/{action_id}/trigger-status` (type, interval, cursor); `POST .../trigger-reset` skips unprocessed events.
|
|
23
|
+
5. Run: `POST /workflows/{action_id}/plan` (free preview) -> confirm -> `POST /workflows/{action_id}/execute` with `{ input_data }`.
|
|
24
|
+
The response `execution_id` is the **run id**; pass it to `RunHistory initialExecutionId`.
|
|
25
|
+
6. Runs: `GET /workflows/executions?action_id=...` -> row `execution_id` -> `GET /workflows/executions/{id}` (polled while `pending`/`running`).
|
|
26
|
+
`paused` and `pending_approval` wait for a person; they are not terminal.
|
|
27
|
+
|
|
28
|
+
Workflow routes return the service's JSON as-is (no `{ data }` wrapper); the components accept both shapes.
|
|
29
|
+
|
|
30
|
+
## Mounting
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
// app/workflows/page.tsx
|
|
34
|
+
"use client";
|
|
35
|
+
import { useRouter } from "next/navigation";
|
|
36
|
+
import { WorkflowList } from "@/components/g8/workflows/components/workflow-list";
|
|
37
|
+
export default function Workflows() {
|
|
38
|
+
const router = useRouter();
|
|
39
|
+
return <WorkflowList onOpen={(id) => router.push(`/workflows/${id}`)} onRunStarted={(id, run) => router.push(`/workflows/${id}/runs?run=${run}`)} />;
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`WorkflowEditor`, `TriggerConfig` take `actionId`; `RunHistory` takes an optional `actionId` and `initialExecutionId`.
|
|
44
|
+
|
|
45
|
+
## Prerequisites
|
|
46
|
+
|
|
47
|
+
- `foundation` installed; proxy `allowedPrefixes` includes `workflows`.
|
|
48
|
+
- `where: "api"`: everything a step references (list, sequence, mailbox, form, workbench) exists. Validate with `POST /workflows/validate`.
|
|
49
|
+
- `where: "app"`: Slack / Roam / LinkedIn steps need those integrations connected by a person in graph8.
|
|
50
|
+
|
|
51
|
+
## Tiers: why so many confirmations
|
|
52
|
+
|
|
53
|
+
Saving or creating a workflow, running it, and every run control are `external` tier in the contract: a saved workflow can start acting on
|
|
54
|
+
live events, and a run can message people. Each goes through `ConfirmSpendDialog`. Deleting is `destructive`. `POST /workflows/validate` is
|
|
55
|
+
also tiered `external` by the contract although it only checks the graph, so the screens do not call it; the recipe `create-workflow.ts` does,
|
|
56
|
+
server side.
|
|
57
|
+
|
|
58
|
+
## Defaults the agent should use without asking
|
|
59
|
+
|
|
60
|
+
- New workflow = manual trigger + end step; the person adds steps.
|
|
61
|
+
- Branching workflows: edit names and configs only; the shape is changed in the graph8 canvas.
|
|
62
|
+
- Run input `{}` unless the workflow needs inputs. 25 rows per page of run history.
|
|
63
|
+
|
|
64
|
+
## Ask the person first
|
|
65
|
+
|
|
66
|
+
- **Enabling** a workflow that sends messages (leave it disabled until they saw the plan).
|
|
67
|
+
- **Resetting a trigger cursor** (skips events) and **restarting** a run (steps that message people run again).
|
|
68
|
+
|
|
69
|
+
## Known limits
|
|
70
|
+
|
|
71
|
+
- No canvas. Branches (`condition`, `loop`, `human`) are shown as steps in run order and their config is editable, but not rewired here.
|
|
72
|
+
- Step config is edited as JSON; the node catalog (`GET /workflows/node-types/schema?node_type=...&detail=full`) documents every field.
|
|
73
|
+
|
|
74
|
+
## Operations
|
|
75
|
+
|
|
76
|
+
| SDK method (`g8.api.*`) | Route | Tier | Scope |
|
|
77
|
+
|---|---|---|---|
|
|
78
|
+
| `listWorkflows` | GET `/api/v1/workflows` | read | `workflows:read` |
|
|
79
|
+
| `createWorkflow` | POST `/api/v1/workflows` | external | `workflows:run` |
|
|
80
|
+
| `planWorkflow` | POST `/api/v1/workflows/{action_id}/plan` | read | `workflows:read` |
|
|
81
|
+
| `executeWorkflow` | POST `/api/v1/workflows/{action_id}/execute` | external | `workflows:run` |
|
|
82
|
+
| `updateWorkflow` | PUT `/api/v1/workflows/{action_id}` | external | `workflows:run` |
|
|
83
|
+
| `deleteWorkflow` | DELETE `/api/v1/workflows/{action_id}` | destructive | `workflows:delete` |
|
|
84
|
+
| `getWorkflow` | GET `/api/v1/workflows/{action_id}` | read | `workflows:read` |
|
|
85
|
+
| `getNodeTypesSchema` | GET `/api/v1/workflows/node-types/schema` | read | `workflows:read` |
|
|
86
|
+
| `getTriggerStatus` | GET `/api/v1/workflows/{action_id}/trigger-status` | read | `workflows:read` |
|
|
87
|
+
| `resetTriggerCursor` | POST `/api/v1/workflows/{action_id}/trigger-reset` | external | `workflows:run` |
|
|
88
|
+
| `listWorkflowExecutions` | GET `/api/v1/workflows/executions` | read | `workflows:read` |
|
|
89
|
+
| `getExecution` | GET `/api/v1/workflows/executions/{execution_id}` | read | `workflows:read` |
|
|
90
|
+
| `pauseExecution` | POST `/api/v1/workflows/executions/{execution_id}/pause` | external | `workflows:run` |
|
|
91
|
+
| `resumeExecution` | POST `/api/v1/workflows/executions/{execution_id}/resume` | external | `workflows:run` |
|
|
92
|
+
| `stopExecution` | POST `/api/v1/workflows/executions/{execution_id}/stop` | external | `workflows:run` |
|
|
93
|
+
| `restartExecution` | POST `/api/v1/workflows/executions/{execution_id}/restart` | external | `workflows:run` |
|
|
94
|
+
|
|
95
|
+
## Recipes
|
|
96
|
+
|
|
97
|
+
- `recipes/create-workflow.ts`: Create a workflow from a list of steps, validate it, and save it
|
|
98
|
+
- `recipes/run-workflow-and-wait.ts`: Preview a workflow run, start it, and wait for the result
|
|
99
|
+
- `recipes/find-failed-runs.ts`: List a workflow's failed runs with their errors
|
|
100
|
+
- `recipes/set-trigger.ts`: Change a workflow's trigger and skip the backlog
|