@graph8/sdk 0.361.0 → 0.370.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 (388) hide show
  1. package/README.md +14 -10
  2. package/blocks/_foundation/README.md +172 -1
  3. package/blocks/_foundation/block.json +234 -13
  4. package/blocks/_foundation/components/api-error.tsx +71 -25
  5. package/blocks/_foundation/components/brand-provider.tsx +23 -0
  6. package/blocks/_foundation/components/confirmed-action.tsx +90 -0
  7. package/blocks/_foundation/components/connect-graph8.tsx +280 -0
  8. package/blocks/_foundation/components/empty-state.tsx +39 -7
  9. package/blocks/_foundation/components/screen/bar-list.tsx +71 -0
  10. package/blocks/_foundation/components/screen/detail-panel.tsx +114 -0
  11. package/blocks/_foundation/components/screen/entity-avatar.tsx +56 -0
  12. package/blocks/_foundation/components/screen/form-sheet.tsx +73 -0
  13. package/blocks/_foundation/components/screen/index.ts +22 -0
  14. package/blocks/_foundation/components/screen/key-value-list.tsx +65 -0
  15. package/blocks/_foundation/components/screen/record-link.tsx +76 -0
  16. package/blocks/_foundation/components/screen/screen-layout.tsx +124 -0
  17. package/blocks/_foundation/components/screen/section-card.tsx +47 -0
  18. package/blocks/_foundation/components/screen/sparkline.tsx +77 -0
  19. package/blocks/_foundation/components/screen/split-view.tsx +125 -0
  20. package/blocks/_foundation/components/screen/stat-strip.tsx +121 -0
  21. package/blocks/_foundation/components/screen/status-pill.tsx +104 -0
  22. package/blocks/_foundation/components/screen/time-series-chart-recharts.tsx +65 -0
  23. package/blocks/_foundation/components/screen/time-series-chart.tsx +32 -0
  24. package/blocks/_foundation/components/screen/time-series-format.ts +57 -0
  25. package/blocks/_foundation/components/screen/toolbar.tsx +144 -0
  26. package/blocks/_foundation/hooks/use-brand.ts +52 -0
  27. package/blocks/_foundation/hooks/use-g8-mutation.ts +23 -0
  28. package/blocks/_foundation/hooks/use-g8-query.ts +127 -1
  29. package/blocks/_foundation/hooks/use-paginated.ts +120 -0
  30. package/blocks/_foundation/hooks/use-row-activation.ts +57 -0
  31. package/blocks/_foundation/hooks/use-url-state.ts +74 -0
  32. package/blocks/_foundation/lib/g8-cache.ts +454 -0
  33. package/blocks/_foundation/lib/g8-client.ts +1 -1
  34. package/blocks/_foundation/lib/plural.ts +23 -0
  35. package/blocks/_foundation/server/connect-route.ts +15 -0
  36. package/blocks/_foundation/server/connect.ts +127 -0
  37. package/blocks/_foundation/server/local-key.ts +70 -0
  38. package/blocks/_foundation/server/proxy.ts +103 -3
  39. package/blocks/_foundation/server/route.ts +16 -4
  40. package/blocks/agents/README.md +14 -0
  41. package/blocks/agents/block.json +33 -1
  42. package/blocks/agents/components/agent-builder.tsx +13 -8
  43. package/blocks/agents/components/agent-knowledge-panel.tsx +2 -1
  44. package/blocks/agents/components/agent-run-detail.tsx +102 -0
  45. package/blocks/agents/components/agent-runs.tsx +16 -7
  46. package/blocks/agents/components/agents-screen.tsx +184 -0
  47. package/blocks/analytics/README.md +13 -0
  48. package/blocks/analytics/block.json +45 -1
  49. package/blocks/analytics/components/analytics-screen.tsx +209 -0
  50. package/blocks/analytics/components/leaderboard.tsx +14 -7
  51. package/blocks/analytics/components/report-builder.tsx +23 -9
  52. package/blocks/analytics/components/report-detail.tsx +84 -0
  53. package/blocks/analytics/components/trend-chart.tsx +62 -0
  54. package/blocks/appointments/README.md +21 -1
  55. package/blocks/appointments/block.json +43 -2
  56. package/blocks/appointments/components/availability-editor.tsx +17 -5
  57. package/blocks/appointments/components/event-type-editor.tsx +2 -1
  58. package/blocks/appointments/components/event-type-related.tsx +77 -0
  59. package/blocks/appointments/components/meeting-list.tsx +14 -6
  60. package/blocks/appointments/components/meetings-screen.tsx +157 -0
  61. package/blocks/appointments/components/scheduling-screen.tsx +122 -0
  62. package/blocks/appointments/hooks/use-event-types.ts +17 -0
  63. package/blocks/block.schema.json +18 -0
  64. package/blocks/block.types.ts +26 -0
  65. package/blocks/campaigns/README.md +41 -11
  66. package/blocks/campaigns/block.json +105 -42
  67. package/blocks/campaigns/components/campaign-assets.tsx +16 -5
  68. package/blocks/campaigns/components/campaign-launch-step.tsx +102 -19
  69. package/blocks/campaigns/components/campaign-list.tsx +7 -3
  70. package/blocks/campaigns/components/campaign-wizard.tsx +43 -10
  71. package/blocks/campaigns/components/campaigns-screen.tsx +267 -0
  72. package/blocks/campaigns/components/icp-detail.tsx +154 -0
  73. package/blocks/campaigns/components/icp-editor.tsx +15 -15
  74. package/blocks/campaigns/components/persona-detail.tsx +172 -0
  75. package/blocks/campaigns/components/persona-editor.tsx +14 -11
  76. package/blocks/campaigns/lib/campaigns-utils.ts +134 -1
  77. package/blocks/campaigns/recipes/attach-audience-and-launch.ts +16 -3
  78. package/blocks/campaigns/recipes/generate-ideas-and-create-campaign.ts +8 -4
  79. package/blocks/coach/README.md +10 -0
  80. package/blocks/coach/block.json +16 -1
  81. package/blocks/coach/components/coach-screen.tsx +222 -0
  82. package/blocks/copilot/README.md +13 -0
  83. package/blocks/copilot/block.json +14 -1
  84. package/blocks/copilot/components/copilot-panel.tsx +14 -5
  85. package/blocks/copilot/components/copilot-screen.tsx +148 -0
  86. package/blocks/copilot/components/copilot-team-chat.tsx +7 -5
  87. package/blocks/crm/README.md +28 -0
  88. package/blocks/crm/block.json +58 -2
  89. package/blocks/crm/components/companies-screen.tsx +345 -0
  90. package/blocks/crm/components/company-detail.tsx +8 -6
  91. package/blocks/crm/components/company-table.tsx +11 -4
  92. package/blocks/crm/components/contact-detail.tsx +6 -3
  93. package/blocks/crm/components/contact-table.tsx +10 -3
  94. package/blocks/crm/components/contacts-screen.tsx +359 -0
  95. package/blocks/crm/components/dedupe-review.tsx +7 -5
  96. package/blocks/crm/components/import-wizard.tsx +4 -2
  97. package/blocks/crm/components/notes-panel.tsx +4 -4
  98. package/blocks/crm/components/record-form.tsx +5 -3
  99. package/blocks/crm/components/task-list.tsx +4 -4
  100. package/blocks/crm/components/tasks-screen.tsx +379 -0
  101. package/blocks/crm/hooks/use-custom-fields.ts +6 -1
  102. package/blocks/crm/hooks/use-page-query.ts +7 -1
  103. package/blocks/crm/lib/crm-types.ts +12 -2
  104. package/blocks/crm/lib/screen-parts.tsx +105 -0
  105. package/blocks/deals/README.md +34 -3
  106. package/blocks/deals/block.json +45 -4
  107. package/blocks/deals/components/deal-detail.tsx +27 -13
  108. package/blocks/deals/components/deal-related.tsx +87 -0
  109. package/blocks/deals/components/deals-screen.tsx +298 -0
  110. package/blocks/deals/components/pipeline-board.tsx +4 -2
  111. package/blocks/deals/components/stage-editor.tsx +4 -4
  112. package/blocks/deals/hooks/use-default-owner.ts +60 -0
  113. package/blocks/deals/hooks/use-pipeline-deals.ts +5 -1
  114. package/blocks/deals/hooks/use-pipelines.ts +1 -1
  115. package/blocks/deals/lib/deals-types.ts +14 -0
  116. package/blocks/dialer/README.md +34 -2
  117. package/blocks/dialer/block.json +67 -1
  118. package/blocks/dialer/components/call-contact-button.tsx +252 -0
  119. package/blocks/dialer/components/call-log-screen.tsx +205 -0
  120. package/blocks/dialer/components/call-log.tsx +7 -3
  121. package/blocks/dialer/components/call-queue.tsx +1 -1
  122. package/blocks/dialer/components/dialer-screen.tsx +319 -0
  123. package/blocks/dialer/components/dialer-session-detail.tsx +153 -0
  124. package/blocks/dialer/components/live-calls-table.tsx +7 -3
  125. package/blocks/dialer/components/session-start-form.tsx +4 -2
  126. package/blocks/dialer/components/softphone.tsx +3 -1
  127. package/blocks/enrichment/README.md +14 -2
  128. package/blocks/enrichment/block.json +32 -4
  129. package/blocks/enrichment/components/bulk-enrich-panel.tsx +10 -5
  130. package/blocks/enrichment/components/email-verify-badge.tsx +28 -8
  131. package/blocks/enrichment/components/enrich-button.tsx +32 -9
  132. package/blocks/enrichment/components/enrichment-job-detail.tsx +78 -0
  133. package/blocks/enrichment/components/enrichment-screen.tsx +288 -0
  134. package/blocks/enrichment/components/waterfall-config.tsx +39 -10
  135. package/blocks/enrichment/recipes/waterfall-pipeline.ts +3 -2
  136. package/blocks/forms/README.md +12 -0
  137. package/blocks/forms/block.json +39 -17
  138. package/blocks/forms/components/form-builder.tsx +3 -1
  139. package/blocks/forms/components/form-embed.tsx +3 -1
  140. package/blocks/forms/components/forms-screen.tsx +353 -0
  141. package/blocks/forms/components/submissions-table.tsx +16 -6
  142. package/blocks/inbox/README.md +27 -3
  143. package/blocks/inbox/block.json +14 -2
  144. package/blocks/inbox/components/inbox-list.tsx +7 -3
  145. package/blocks/inbox/components/inbox-screen.tsx +294 -27
  146. package/blocks/inbox/components/reply-composer.tsx +65 -9
  147. package/blocks/inbox/components/tag-and-assign.tsx +4 -2
  148. package/blocks/inbox/hooks/use-inbox-thread.ts +1 -1
  149. package/blocks/inbox/hooks/use-inbox-threads.ts +1 -0
  150. package/blocks/inbox/lib/inbox-types.ts +47 -1
  151. package/blocks/inbox/recipes/01-triage-and-reply.ts +12 -2
  152. package/blocks/integrations/README.md +9 -0
  153. package/blocks/integrations/block.json +46 -1
  154. package/blocks/integrations/components/crm-detail.tsx +100 -0
  155. package/blocks/integrations/components/integration-list.tsx +9 -4
  156. package/blocks/integrations/components/integrations-screen.tsx +135 -0
  157. package/blocks/integrations/components/sync-status.tsx +5 -3
  158. package/blocks/integrations/components/webhook-detail.tsx +160 -0
  159. package/blocks/integrations/components/webhook-manager.tsx +13 -8
  160. package/blocks/integrations/lib/integrations-utils.ts +6 -1
  161. package/blocks/intent/README.md +14 -0
  162. package/blocks/intent/block.json +27 -1
  163. package/blocks/intent/components/intent-feed.tsx +5 -2
  164. package/blocks/intent/components/intent-keyword-manager.tsx +8 -4
  165. package/blocks/intent/components/intent-screen.tsx +265 -0
  166. package/blocks/intent/components/keyword-detail.tsx +124 -0
  167. package/blocks/lifecycle/README.md +16 -0
  168. package/blocks/lifecycle/block.json +34 -7
  169. package/blocks/lifecycle/components/account-related.tsx +66 -0
  170. package/blocks/lifecycle/components/lifecycle-board.tsx +3 -1
  171. package/blocks/lifecycle/components/lifecycle-screen.tsx +149 -0
  172. package/blocks/lifecycle/lib/lifecycle.ts +1 -1
  173. package/blocks/lists/README.md +13 -0
  174. package/blocks/lists/block.json +39 -1
  175. package/blocks/lists/components/add-to-list-button.tsx +3 -2
  176. package/blocks/lists/components/list-picker.tsx +5 -1
  177. package/blocks/lists/components/list-table.tsx +11 -5
  178. package/blocks/lists/components/lists-screen.tsx +344 -0
  179. package/blocks/lists/components/suppression-manager.tsx +6 -2
  180. package/blocks/lists/components/suppressions-screen.tsx +255 -0
  181. package/blocks/mailboxes/README.md +17 -0
  182. package/blocks/mailboxes/block.json +65 -17
  183. package/blocks/mailboxes/components/connect-mailbox.tsx +23 -13
  184. package/blocks/mailboxes/components/domain-detail.tsx +142 -0
  185. package/blocks/mailboxes/components/domain-purchase.tsx +6 -3
  186. package/blocks/mailboxes/components/mailbox-detail.tsx +156 -0
  187. package/blocks/mailboxes/components/mailboxes-screen.tsx +223 -0
  188. package/blocks/mailboxes/components/warmup-health.tsx +4 -2
  189. package/blocks/mailboxes/lib/mailboxes-utils.ts +22 -1
  190. package/blocks/newsletter/README.md +13 -0
  191. package/blocks/newsletter/block.json +47 -23
  192. package/blocks/newsletter/components/issue-scheduler.tsx +2 -1
  193. package/blocks/newsletter/components/newsletter-screen.tsx +404 -0
  194. package/blocks/newsletter/components/subscriber-table.tsx +12 -6
  195. package/blocks/nurture/README.md +12 -0
  196. package/blocks/nurture/block.json +35 -13
  197. package/blocks/nurture/components/nurture-builder.tsx +3 -1
  198. package/blocks/nurture/components/nurture-enroll.tsx +6 -3
  199. package/blocks/nurture/components/nurture-form.tsx +23 -3
  200. package/blocks/nurture/components/nurture-screen.tsx +283 -0
  201. package/blocks/nurture/lib/nurture-types.ts +1 -1
  202. package/blocks/objects/README.md +17 -0
  203. package/blocks/objects/block.json +29 -16
  204. package/blocks/objects/components/object-not-found.tsx +3 -1
  205. package/blocks/objects/components/object-record-detail.tsx +2 -1
  206. package/blocks/objects/components/object-schema-editor.tsx +3 -1
  207. package/blocks/objects/components/object-table.tsx +13 -3
  208. package/blocks/objects/components/objects-screen.tsx +147 -0
  209. package/blocks/objects/lib/object-types.ts +1 -1
  210. package/blocks/pages/README.md +12 -0
  211. package/blocks/pages/block.json +35 -22
  212. package/blocks/pages/components/brand-kit-editor.tsx +5 -2
  213. package/blocks/pages/components/content-library.tsx +46 -11
  214. package/blocks/pages/components/page-list.tsx +6 -3
  215. package/blocks/pages/components/pages-screen.tsx +154 -0
  216. package/blocks/quotes/README.md +17 -0
  217. package/blocks/quotes/block.json +38 -9
  218. package/blocks/quotes/components/product-picker.tsx +3 -1
  219. package/blocks/quotes/components/quote-list.tsx +7 -3
  220. package/blocks/quotes/components/quote-related.tsx +123 -0
  221. package/blocks/quotes/components/quotes-screen.tsx +169 -0
  222. package/blocks/radar/README.md +10 -0
  223. package/blocks/radar/block.json +41 -1
  224. package/blocks/radar/components/competitor-detail.tsx +117 -0
  225. package/blocks/radar/components/competitor-tracker.tsx +3 -1
  226. package/blocks/radar/components/radar-report.tsx +4 -2
  227. package/blocks/radar/components/radar-screen.tsx +214 -0
  228. package/blocks/radar/components/report-detail.tsx +104 -0
  229. package/blocks/search/README.md +10 -0
  230. package/blocks/search/block.json +16 -1
  231. package/blocks/search/components/filter-builder.tsx +3 -3
  232. package/blocks/search/components/prospect-results.tsx +5 -3
  233. package/blocks/search/components/prospect-search-screen.tsx +235 -0
  234. package/blocks/search/components/saved-searches.tsx +4 -1
  235. package/blocks/search/hooks/use-filter-options.ts +20 -6
  236. package/blocks/search/lib/search-types.ts +2 -0
  237. package/blocks/sequencer/README.md +49 -9
  238. package/blocks/sequencer/block.json +99 -19
  239. package/blocks/sequencer/components/enroll-dialog.tsx +9 -3
  240. package/blocks/sequencer/components/sequence-builder.tsx +211 -46
  241. package/blocks/sequencer/components/sequence-detail.tsx +259 -0
  242. package/blocks/sequencer/components/sequence-enrollments.tsx +114 -0
  243. package/blocks/sequencer/components/sequence-list.tsx +33 -8
  244. package/blocks/sequencer/components/sequence-stats.tsx +3 -2
  245. package/blocks/sequencer/components/sequence-steps-panel.tsx +103 -0
  246. package/blocks/sequencer/components/sequences-screen.tsx +159 -0
  247. package/blocks/sequencer/components/step-editor.tsx +3 -1
  248. package/blocks/sequencer/lib/sequencer-utils.ts +128 -5
  249. package/blocks/sequencer/recipes/create-multichannel-sequence.ts +33 -5
  250. package/blocks/sequencer/recipes/launch-and-enroll.ts +19 -2
  251. package/blocks/social-ads/README.md +11 -0
  252. package/blocks/social-ads/block.json +101 -2
  253. package/blocks/social-ads/components/ad-campaign-list.tsx +9 -5
  254. package/blocks/social-ads/components/ads-screen.tsx +152 -0
  255. package/blocks/social-ads/components/audience-sync-detail.tsx +188 -0
  256. package/blocks/social-ads/components/audience-sync.tsx +11 -7
  257. package/blocks/social-ads/components/campaign-detail.tsx +133 -0
  258. package/blocks/social-ads/components/creative-detail.tsx +150 -0
  259. package/blocks/social-ads/components/creative-gallery.tsx +15 -5
  260. package/blocks/social-ads/components/post-composer.tsx +3 -1
  261. package/blocks/social-ads/components/post-detail.tsx +160 -0
  262. package/blocks/social-ads/components/social-post-list.tsx +11 -3
  263. package/blocks/social-ads/components/social-screen.tsx +160 -0
  264. package/blocks/visitors/README.md +10 -0
  265. package/blocks/visitors/block.json +25 -1
  266. package/blocks/visitors/components/snippet-install.tsx +3 -1
  267. package/blocks/visitors/components/visitor-company-card.tsx +2 -2
  268. package/blocks/visitors/components/visitor-detail.tsx +105 -0
  269. package/blocks/visitors/components/visitor-feed.tsx +11 -2
  270. package/blocks/visitors/components/visitors-screen.tsx +166 -0
  271. package/blocks/webchat/README.md +10 -0
  272. package/blocks/webchat/block.json +21 -7
  273. package/blocks/webchat/components/chat-widget-config.tsx +7 -4
  274. package/blocks/webchat/components/conversation-list.tsx +7 -3
  275. package/blocks/webchat/components/webchat-screen.tsx +245 -0
  276. package/blocks/workbench/README.md +11 -0
  277. package/blocks/workbench/block.json +48 -1
  278. package/blocks/workbench/components/column-recipe-editor.tsx +4 -2
  279. package/blocks/workbench/components/workbench-grid.tsx +9 -3
  280. package/blocks/workbench/components/workbench-row-detail.tsx +104 -0
  281. package/blocks/workbench/components/workbench-screen.tsx +192 -0
  282. package/blocks/workbench/components/workbench-sources.tsx +194 -0
  283. package/blocks/workflows/README.md +13 -0
  284. package/blocks/workflows/block.json +28 -9
  285. package/blocks/workflows/components/run-history.tsx +31 -9
  286. package/blocks/workflows/components/trigger-config.tsx +3 -1
  287. package/blocks/workflows/components/workflow-editor.tsx +3 -1
  288. package/blocks/workflows/components/workflow-list.tsx +13 -6
  289. package/blocks/workflows/components/workflows-screen.tsx +193 -0
  290. package/dist/cli.js +2785 -1637
  291. package/dist/errors.d.mts +34 -0
  292. package/dist/errors.d.ts +34 -0
  293. package/dist/errors.js +46 -0
  294. package/dist/errors.js.map +1 -0
  295. package/dist/errors.mjs +21 -0
  296. package/dist/errors.mjs.map +1 -0
  297. package/dist/index.browser.js +11 -5
  298. package/dist/index.browser.js.map +1 -1
  299. package/dist/index.browser.mjs +11 -5
  300. package/dist/index.browser.mjs.map +1 -1
  301. package/dist/index.d.mts +164 -25
  302. package/dist/index.d.ts +164 -25
  303. package/dist/index.js +11 -5
  304. package/dist/index.js.map +1 -1
  305. package/dist/index.mjs +11 -5
  306. package/dist/index.mjs.map +1 -1
  307. package/dist/react.browser.js +10 -4
  308. package/dist/react.browser.js.map +1 -1
  309. package/dist/react.browser.mjs +10 -4
  310. package/dist/react.browser.mjs.map +1 -1
  311. package/dist/react.d.mts +142 -5
  312. package/dist/react.d.ts +142 -5
  313. package/dist/react.js +10 -4
  314. package/dist/react.js.map +1 -1
  315. package/dist/react.mjs +10 -4
  316. package/dist/react.mjs.map +1 -1
  317. package/dist/types/blocks/add.d.ts +11 -4
  318. package/dist/types/blocks/brand.d.ts +113 -0
  319. package/dist/types/blocks/compose.d.ts +53 -0
  320. package/dist/types/blocks/create.d.ts +25 -8
  321. package/dist/types/blocks/doctor.d.ts +6 -1
  322. package/dist/types/blocks/flows.d.ts +20 -0
  323. package/dist/types/blocks/navfile.d.ts +35 -0
  324. package/dist/types/blocks/paths.d.ts +4 -0
  325. package/dist/types/blocks/prefixes.d.ts +37 -0
  326. package/dist/types/blocks/registry.d.ts +32 -0
  327. package/dist/types/blocks/screens.d.ts +53 -0
  328. package/dist/types/cli.d.ts +2 -0
  329. package/dist/types/errors.d.ts +33 -0
  330. package/dist/types/http.d.ts +2 -18
  331. package/dist/types/version.d.ts +2 -2
  332. package/docs/agents.md +15 -4
  333. package/docs/api/README.md +2 -2
  334. package/docs/api/by-path.md +1 -0
  335. package/docs/api/desk.d.ts +98 -2
  336. package/docs/api/desk.md +123 -1
  337. package/docs/api/inbox.d.ts +10 -2
  338. package/docs/api/inbox.md +10 -2
  339. package/docs/api/seo.d.ts +19 -1
  340. package/docs/api/seo.md +24 -1
  341. package/package.json +16 -2
  342. package/stacks/6sense.json +41 -16
  343. package/stacks/apollo/pages/companies.tsx.tmpl +50 -0
  344. package/stacks/apollo/pages/contacts.tsx.tmpl +117 -0
  345. package/stacks/apollo.json +118 -36
  346. package/stacks/calendly.json +60 -0
  347. package/stacks/clay/pages/companies.tsx.tmpl +15 -0
  348. package/stacks/clay/pages/contacts.tsx.tmpl +19 -0
  349. package/stacks/clay.json +59 -26
  350. package/stacks/gong.json +81 -35
  351. package/stacks/hootsuite.json +99 -0
  352. package/stacks/hubspot/pages/companies.tsx.tmpl +47 -0
  353. package/stacks/hubspot/pages/contacts.tsx.tmpl +111 -0
  354. package/stacks/hubspot/pages/deals.tsx.tmpl +11 -0
  355. package/stacks/hubspot/pages/inbox.tsx.tmpl +92 -0
  356. package/stacks/hubspot/pages/quotes.tsx.tmpl +11 -0
  357. package/stacks/hubspot.json +167 -66
  358. package/stacks/intercom.json +48 -20
  359. package/stacks/mailchimp.json +52 -33
  360. package/stacks/outreach/pages/contacts.tsx.tmpl +85 -0
  361. package/stacks/outreach.json +88 -25
  362. package/stacks/pipedrive/pages/companies.tsx.tmpl +47 -0
  363. package/stacks/pipedrive/pages/contacts.tsx.tmpl +47 -0
  364. package/stacks/pipedrive/pages/deals.tsx.tmpl +11 -0
  365. package/stacks/pipedrive/pages/inbox.tsx.tmpl +92 -0
  366. package/stacks/pipedrive/pages/quotes.tsx.tmpl +11 -0
  367. package/stacks/pipedrive.json +115 -0
  368. package/stacks/salesforce/pages/companies.tsx.tmpl +47 -0
  369. package/stacks/salesforce/pages/contacts.tsx.tmpl +47 -0
  370. package/stacks/salesforce/pages/deals.tsx.tmpl +11 -0
  371. package/stacks/salesforce/pages/quotes.tsx.tmpl +11 -0
  372. package/stacks/salesforce.json +81 -31
  373. package/stacks/stack.schema.json +6 -1
  374. package/stacks/zoominfo/pages/companies.tsx.tmpl +15 -0
  375. package/stacks/zoominfo/pages/contacts.tsx.tmpl +19 -0
  376. package/stacks/zoominfo.json +102 -0
  377. package/styles/styles.css +1 -1
  378. package/templates/next-app/README.md.tmpl +55 -8
  379. package/templates/next-app/app/globals.css.tmpl +105 -0
  380. package/templates/next-app/app/layout.tsx.tmpl +18 -4
  381. package/templates/next-app/components/app-shell.tsx.tmpl +248 -23
  382. package/templates/next-app/components/shell/connection-status.tsx.tmpl +109 -0
  383. package/templates/next-app/components/shell/nav-model.ts.tmpl +65 -0
  384. package/templates/next-app/components/shell/quick-jump.tsx.tmpl +131 -0
  385. package/templates/next-app/components/shell/theme-toggle.tsx.tmpl +40 -0
  386. package/templates/next-app/dot-gitignore.tmpl +2 -0
  387. package/templates/next-app/lib/nav.ts.tmpl +1 -1
  388. package/templates/next-app/package.json.tmpl +1 -1
package/dist/cli.js CHANGED
@@ -36,8 +36,8 @@ __export(cli_exports, {
36
36
  });
37
37
  module.exports = __toCommonJS(cli_exports);
38
38
  var import_node_child_process = require("child_process");
39
- var import_node_fs5 = __toESM(require("fs"));
40
- var import_node_path5 = __toESM(require("path"));
39
+ var import_node_fs9 = __toESM(require("fs"));
40
+ var import_node_path10 = __toESM(require("path"));
41
41
 
42
42
  // src/agentSetup.ts
43
43
  var ALL_TARGETS = ["claude", "codex", "cursor"];
@@ -119,9 +119,9 @@ ${section}
119
119
  }
120
120
  function planAgentSetup(targets, read, catalog) {
121
121
  const plans = [];
122
- const add = (target, path6, next) => {
123
- const current = read(path6);
124
- plans.push({ target, path: path6, content: next, action: current === null ? "create" : current === next ? "unchanged" : "update" });
122
+ const add = (target, path11, next) => {
123
+ const current = read(path11);
124
+ plans.push({ target, path: path11, content: next, action: current === null ? "create" : current === next ? "unchanged" : "update" });
125
125
  };
126
126
  for (const target of targets) {
127
127
  if (target === "claude") add(target, ".claude/skills/graph8-sdk/SKILL.md", claudeSkill(catalog));
@@ -154,1622 +154,21 @@ ${INIT_AGENT_USAGE}`);
154
154
 
155
155
  // src/blocks/add.ts
156
156
  var import_node_crypto = require("crypto");
157
- var import_node_fs2 = __toESM(require("fs"));
158
- var import_node_path2 = __toESM(require("path"));
157
+ var import_node_fs4 = __toESM(require("fs"));
158
+ var import_node_path5 = __toESM(require("path"));
159
159
 
160
160
  // src/version.ts
161
- var SDK_VERSION = "0.361.0";
161
+ var SDK_VERSION = "0.370.0";
162
162
  var SDK_CLIENT_ID = `js-sdk/${SDK_VERSION}`;
163
163
 
164
- // src/blocks/registry.ts
165
- var import_node_fs = __toESM(require("fs"));
164
+ // src/blocks/paths.ts
166
165
  var import_node_path = __toESM(require("path"));
167
- function arr(v) {
168
- return Array.isArray(v) ? v : [];
169
- }
170
- function readJson(file) {
171
- try {
172
- const parsed = JSON.parse(import_node_fs.default.readFileSync(file, "utf8"));
173
- return parsed && typeof parsed === "object" ? parsed : null;
174
- } catch {
175
- return null;
176
- }
177
- }
178
- function toBlock(raw, dir, root) {
179
- if (typeof raw.id !== "string" || !raw.id) return null;
180
- return {
181
- id: raw.id,
182
- title: typeof raw.title === "string" ? raw.title : raw.id,
183
- summary: typeof raw.summary === "string" ? raw.summary : "",
184
- features: arr(raw.features),
185
- dependsOn: arr(raw.dependsOn),
186
- operations: arr(raw.operations),
187
- components: arr(raw.components),
188
- hooks: arr(raw.hooks),
189
- lib: arr(raw.lib),
190
- server: arr(raw.server),
191
- recipes: arr(raw.recipes),
192
- prerequisites: arr(raw.prerequisites),
193
- scopes: arr(raw.scopes),
194
- defaults: arr(raw.defaults),
195
- peerDependencies: raw.peerDependencies ?? {},
196
- dir,
197
- root
198
- };
199
- }
200
- function loadRegistry(blocksRoot) {
201
- const blocks = [];
202
- if (import_node_fs.default.existsSync(blocksRoot)) {
203
- for (const entry of import_node_fs.default.readdirSync(blocksRoot, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
204
- if (!entry.isDirectory()) continue;
205
- const root = import_node_path.default.join(blocksRoot, entry.name);
206
- const raw = readJson(import_node_path.default.join(root, "block.json"));
207
- const block = raw && toBlock(raw, entry.name, root);
208
- if (block) blocks.push(block);
209
- }
210
- }
211
- const stacks = [];
212
- const stacksDir = import_node_path.default.join(import_node_path.default.dirname(blocksRoot), "stacks");
213
- if (import_node_fs.default.existsSync(stacksDir)) {
214
- for (const file of import_node_fs.default.readdirSync(stacksDir).filter((f) => f.endsWith(".json")).sort()) {
215
- const raw = readJson(import_node_path.default.join(stacksDir, file));
216
- if (!raw || typeof raw.id !== "string") continue;
217
- stacks.push({
218
- id: raw.id,
219
- title: typeof raw.title === "string" ? raw.title : raw.id,
220
- summary: typeof raw.summary === "string" ? raw.summary : "",
221
- status: raw.status === "planned" ? "planned" : "available",
222
- blocks: arr(raw.blocks),
223
- nav: arr(raw.nav)
224
- });
225
- }
226
- }
227
- return { blocks, stacks };
228
- }
229
- function findBlock(reg, id) {
230
- return reg.blocks.find((b) => b.id === id);
231
- }
232
- function resolveWithDeps(reg, ids) {
233
- const ordered = [];
234
- const done = /* @__PURE__ */ new Set();
235
- const visiting = [];
236
- const visit = (id) => {
237
- if (done.has(id)) return;
238
- if (visiting.includes(id)) throw new Error(`dependency cycle: ${[...visiting, id].join(" -> ")}`);
239
- const block = findBlock(reg, id);
240
- if (!block) {
241
- const from = visiting.length ? ` (required by ${visiting[visiting.length - 1]})` : "";
242
- throw new Error(`unknown block "${id}"${from}. Run \`graph8-sdk list\` to see the available blocks.`);
243
- }
244
- visiting.push(id);
245
- for (const dep of block.dependsOn) visit(dep);
246
- visiting.pop();
247
- done.add(id);
248
- ordered.push(block);
249
- };
250
- ids.forEach(visit);
251
- return ordered;
252
- }
253
- function searchBlocks(reg, text) {
254
- const needle = text.trim().toLowerCase();
255
- if (!needle) return [];
256
- return reg.blocks.filter(
257
- (b) => [b.id, b.title, b.summary, ...b.features, ...b.components.map((c) => c.name)].some((s) => s.toLowerCase().includes(needle))
258
- );
259
- }
260
- function searchStacks(reg, text) {
261
- const needle = text.trim().toLowerCase();
262
- if (!needle) return [];
263
- return reg.stacks.filter((s) => [s.id, s.title, s.summary].some((x) => x.toLowerCase().includes(needle)));
264
- }
265
- function blockSummary(b) {
266
- return { id: b.id, title: b.title, summary: b.summary, dependsOn: b.dependsOn, components: b.components.map((c) => c.name) };
267
- }
268
- function stackStatus(reg, s) {
269
- const missing = s.blocks.filter((id) => !findBlock(reg, id));
270
- return { status: missing.length ? "planned" : "available", missing };
271
- }
272
- function stackSummary(s, reg) {
273
- const base = { id: s.id, title: s.title, summary: s.summary, blocks: s.blocks };
274
- if (!reg) return base;
275
- const { status, missing } = stackStatus(reg, s);
276
- return { ...base, status, ...missing.length ? { missingBlocks: missing } : {} };
277
- }
278
- function catalogSection(reg) {
279
- if (!reg.blocks.length && !reg.stacks.length) return void 0;
280
- const lines = ["### graph8 blocks", "", "Ready-made, copy-in UI + hooks per graph8 feature. Pick from this list instead of reading every README:", ""];
281
- for (const b of reg.blocks) {
282
- const deps = b.dependsOn.length ? ` (needs ${b.dependsOn.join(", ")})` : "";
283
- lines.push(`- block \`${b.id}\`: ${b.summary}${deps}`);
284
- }
285
- for (const s of reg.stacks) {
286
- const { missing } = stackStatus(reg, s);
287
- const planned = missing.length ? ` [planned, not creatable yet: missing blocks ${missing.join(", ")}]` : "";
288
- lines.push(`- stack \`${s.id}\`: ${s.summary} (blocks: ${s.blocks.join(", ")})${planned}`);
289
- }
290
- lines.push(
291
- "",
292
- "Add one: `npx @graph8/sdk add <block>[/<Component>]` (dependencies and the `foundation` block come with it). Find one:",
293
- "`npx @graph8/sdk list` / `search <text>`. Check setup: `npx @graph8/sdk doctor --blocks a,b`."
294
- );
295
- if (reg.stacks.some((s) => !stackStatus(reg, s).missing.length)) lines.push("Start a whole app from an available stack: `npx @graph8/sdk create <stack> <dir>` (Next.js + the stack's blocks wired into a sidebar).");
296
- return lines.join("\n");
297
- }
298
-
299
- // src/blocks/add.ts
300
- var ADD_USAGE = `Usage: npx @graph8/sdk add <block>[/<Component>] [options]
301
-
302
- Copy a graph8 block (components, hooks, recipes) into your app. The block's
303
- dependencies (always including \`foundation\`) are added first.
304
- --dir <path> project root (default: current directory)
305
- --target <path> where to copy, relative to --dir (default: components/g8)
306
- --dry-run print what would change, write and install nothing
307
- --force overwrite files that differ from the template
308
- --no-install do not install missing peer dependencies
309
- Existing files are never overwritten without --force. Installed blocks are
310
- recorded in graph8.json so re-runs do nothing.`;
311
- function parseAddArgs(argv) {
312
- const o = { spec: "", dir: ".", dryRun: false, force: false, install: true };
313
- for (let i = 0; i < argv.length; i++) {
314
- const arg = argv[i];
315
- const [flag, inline] = arg.startsWith("--") && arg.includes("=") ? [arg.slice(0, arg.indexOf("=")), arg.slice(arg.indexOf("=") + 1)] : [arg, void 0];
316
- const value = () => {
317
- const v = inline ?? argv[++i];
318
- if (v === void 0 || v.startsWith("--")) throw new Error(`${flag} needs a value
319
-
320
- ${ADD_USAGE}`);
321
- return v;
322
- };
323
- if (flag === "--dir") o.dir = value();
324
- else if (flag === "--target") o.target = value();
325
- else if (arg === "--dry-run") o.dryRun = true;
326
- else if (arg === "--force") o.force = true;
327
- else if (arg === "--no-install") o.install = false;
328
- else if (arg.startsWith("-")) throw new Error(`unknown option ${arg}
329
-
330
- ${ADD_USAGE}`);
331
- else if (!o.spec) o.spec = arg;
332
- else throw new Error(`unexpected argument ${arg}
333
-
334
- ${ADD_USAGE}`);
335
- }
336
- if (!o.spec) throw new Error(`add needs a block name
337
-
338
- ${ADD_USAGE}`);
339
- return o;
340
- }
341
- function detectPackageManager(root) {
342
- const has = (f) => import_node_fs2.default.existsSync(import_node_path2.default.join(root, f));
343
- if (has("pnpm-lock.yaml")) return "pnpm";
344
- if (has("yarn.lock")) return "yarn";
345
- if (has("bun.lockb") || has("bun.lock")) return "bun";
346
- return "npm";
347
- }
348
- function installCommand(pm, packages) {
349
- const verb = pm === "npm" ? "install" : "add";
350
- return { cmd: pm, args: [verb, ...packages] };
351
- }
352
- var sha = (s) => (0, import_node_crypto.createHash)("sha256").update(s).digest("hex");
353
- var posix = (p) => p.split(import_node_path2.default.sep).join("/");
166
+ var posix = (p) => p.split(import_node_path.default.sep).join("/");
354
167
  function installedRel(rel) {
355
168
  const r = posix(rel);
356
169
  if (r === "block.json") return null;
357
170
  return r.startsWith("components/") ? r.slice("components/".length) : r;
358
171
  }
359
- function appTargeted(block) {
360
- const m = /* @__PURE__ */ new Map();
361
- for (const f of block.server) if (f.target && !f.target.startsWith("g8/")) m.set(posix(f.file), f.target);
362
- return m;
363
- }
364
- function listFiles(dir, base = dir) {
365
- const out = [];
366
- for (const e of import_node_fs2.default.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
367
- const full = import_node_path2.default.join(dir, e.name);
368
- if (e.isDirectory()) out.push(...listFiles(full, base));
369
- else out.push(posix(import_node_path2.default.relative(base, full)));
370
- }
371
- return out;
372
- }
373
- var IMPORT_RE = /(\bfrom\s*|\bimport\s*\(?\s*|\brequire\s*\(\s*)(["'])([^"'\n]+)\2/g;
374
- function relSpec(fromDir, toFile) {
375
- const noExt = toFile;
376
- let r = posix(import_node_path2.default.relative(fromDir, noExt));
377
- if (!r.startsWith(".")) r = `./${r}`;
378
- return r;
379
- }
380
- function g8AliasTarget(spec, ctx) {
381
- const [seg, ...rest] = spec.split("/");
382
- const reserved = ["components", "hooks", "lib", "server", "recipes"].includes(seg);
383
- const owner = rest.length && !reserved ? ctx.reg.blocks.find((b) => b.id === seg || b.dir === seg) : void 0;
384
- const block = owner ?? ctx.reg.blocks.find((b) => b.id === "foundation");
385
- const sub = owner ? rest.join("/") : spec;
386
- return import_node_path2.default.join(ctx.targetAbs, block?.dir ?? "_foundation", installedRel(sub) ?? sub);
387
- }
388
- function rewriteImports(content, ctx) {
389
- return content.replace(IMPORT_RE, (whole, pre, q, spec) => {
390
- if (spec.startsWith("@/g8/")) {
391
- return `${pre}${q}${relSpec(ctx.destDir, g8AliasTarget(spec.slice("@/g8/".length), ctx))}${q}`;
392
- }
393
- if (spec.startsWith(".") && ctx.block && ctx.srcFile) {
394
- const abs = import_node_path2.default.resolve(import_node_path2.default.dirname(ctx.srcFile), spec);
395
- const relInBlock = import_node_path2.default.relative(ctx.block.root, abs);
396
- if (relInBlock.startsWith("..")) return whole;
397
- const mapped = installedRel(relInBlock);
398
- if (mapped === null) return whole;
399
- return `${pre}${q}${relSpec(ctx.destDir, import_node_path2.default.join(ctx.targetAbs, ctx.block.dir, mapped))}${q}`;
400
- }
401
- return whole;
402
- });
403
- }
404
- function diffSummary(before, after) {
405
- const count = (s) => {
406
- const m = /* @__PURE__ */ new Map();
407
- for (const l of s.split("\n")) m.set(l, (m.get(l) ?? 0) + 1);
408
- return m;
409
- };
410
- const a = count(before);
411
- const b = count(after);
412
- let removed = 0;
413
- let added = 0;
414
- for (const [l, n] of a) removed += Math.max(0, n - (b.get(l) ?? 0));
415
- for (const [l, n] of b) added += Math.max(0, n - (a.get(l) ?? 0));
416
- return `+${added} -${removed} lines vs the template`;
417
- }
418
- function classify(abs, rel, content, force) {
419
- if (!import_node_fs2.default.existsSync(abs)) return { rel, content, action: "create" };
420
- const current = import_node_fs2.default.readFileSync(abs, "utf8");
421
- if (current === content) return { rel, content, action: "unchanged" };
422
- if (force) return { rel, content, action: "update", detail: diffSummary(current, content) };
423
- return { rel, content, action: "skip", detail: `exists and differs (${diffSummary(current, content)}); use --force to overwrite` };
424
- }
425
- function readPkg(root) {
426
- try {
427
- return JSON.parse(import_node_fs2.default.readFileSync(import_node_path2.default.join(root, "package.json"), "utf8"));
428
- } catch {
429
- return null;
430
- }
431
- }
432
- function missingPeers(blocks, pkg) {
433
- const have = /* @__PURE__ */ new Set([
434
- ...Object.keys(pkg.dependencies ?? {}),
435
- ...Object.keys(pkg.devDependencies ?? {}),
436
- ...Object.keys(pkg.peerDependencies ?? {}),
437
- ...Object.keys(pkg.optionalDependencies ?? {})
438
- ]);
439
- const want = /* @__PURE__ */ new Map();
440
- for (const b of blocks) for (const [name, range] of Object.entries(b.peerDependencies)) if (!want.has(name)) want.set(name, range);
441
- return [...want].filter(([name]) => !have.has(name)).map(([name, range]) => `${name}@${range}`);
442
- }
443
- function parseSpec(spec) {
444
- const [blockId, component, ...extra] = spec.split("/");
445
- if (!blockId || extra.length) throw new Error(`invalid block "${spec}": use <block> or <block>/<Component>`);
446
- return { blockId, component: component || void 0 };
447
- }
448
- function planBlock(block, only, reg, targetAbs, root, force) {
449
- const skipComponentFiles = /* @__PURE__ */ new Set();
450
- if (only) {
451
- for (const c of block.components) if (c.name !== only) skipComponentFiles.add(posix(c.file));
452
- const byStem = new Map(block.components.map((c) => [posix(c.file).replace(/\.[tj]sx?$/, ""), posix(c.file)]));
453
- const start = block.components.find((c) => c.name === only);
454
- const queue = start ? [posix(start.file)] : [];
455
- while (queue.length) {
456
- const file = queue.pop();
457
- const text = import_node_fs2.default.readFileSync(import_node_path2.default.join(block.root, file), "utf8");
458
- for (const m of text.matchAll(IMPORT_RE)) {
459
- if (!m[3].startsWith(".")) continue;
460
- const stem = posix(import_node_path2.default.relative(block.root, import_node_path2.default.resolve(block.root, import_node_path2.default.dirname(file), m[3]))).replace(/\.[tj]sx?$/, "");
461
- const sibling = byStem.get(stem);
462
- if (sibling && skipComponentFiles.delete(sibling)) queue.push(sibling);
463
- }
464
- }
465
- }
466
- const files = [];
467
- const toApp = appTargeted(block);
468
- for (const rel of listFiles(block.root)) {
469
- if (skipComponentFiles.has(rel) || toApp.has(rel)) continue;
470
- const mapped = installedRel(rel);
471
- if (mapped === null) continue;
472
- const destAbs = import_node_path2.default.join(targetAbs, block.dir, mapped);
473
- const src = import_node_path2.default.join(block.root, rel);
474
- const text = import_node_fs2.default.readFileSync(src, "utf8");
475
- const content = rewriteImports(text, { reg, targetAbs, block, srcFile: src, destDir: import_node_path2.default.dirname(destAbs) });
476
- files.push(classify(destAbs, posix(import_node_path2.default.relative(root, destAbs)), content, force));
477
- }
478
- return files;
479
- }
480
- var PROXY_CONTRACT = [
481
- "This is not a Next.js project, so the proxy route was not written. Your server must provide the same contract:",
482
- " - handle every method on /api/g8/* (the browser calls these paths)",
483
- " - forward to ${G8_API_URL:-https://be.graph8.com}/api/v1/<rest of path>, same method, query and body",
484
- " - add the header `Authorization: Bearer $G8_API_KEY` on the server; never send the key to the browser",
485
- " - return the upstream status and JSON body unchanged"
486
- ];
487
- function writeFile(root, f) {
488
- const abs = import_node_path2.default.join(root, f.rel);
489
- import_node_fs2.default.mkdirSync(import_node_path2.default.dirname(abs), { recursive: true });
490
- import_node_fs2.default.writeFileSync(abs, f.content, "utf8");
491
- }
492
- function readLedger(file) {
493
- try {
494
- const j = JSON.parse(import_node_fs2.default.readFileSync(file, "utf8"));
495
- return j && typeof j === "object" && j.blocks ? j : null;
496
- } catch {
497
- return null;
498
- }
499
- }
500
- function ledgerContent(prev, target, entries, only) {
501
- const blocks = { ...prev?.blocks ?? {} };
502
- for (const [id, files] of entries) {
503
- const old = blocks[id];
504
- const recorded = { ...old?.files ?? {} };
505
- for (const f of files) {
506
- if (f.action === "skip") continue;
507
- recorded[f.rel] = sha(f.content);
508
- }
509
- const comp = only.get(id);
510
- let components;
511
- if (!comp) components = "all";
512
- else if (old?.components === "all") components = "all";
513
- else components = [.../* @__PURE__ */ new Set([...old?.components ?? [], comp])].sort();
514
- blocks[id] = { files: Object.fromEntries(Object.entries(recorded).sort(([a], [b]) => a.localeCompare(b))), components };
515
- }
516
- const sorted = Object.fromEntries(Object.entries(blocks).sort(([a], [b]) => a.localeCompare(b)));
517
- const stack = prev?.stack ? { stack: prev.stack } : {};
518
- return `${JSON.stringify({ sdkVersion: SDK_VERSION, target, ...stack, blocks: sorted }, null, 2)}
519
- `;
520
- }
521
- function ensureEnvExample(root, dryRun, out) {
522
- const file = import_node_path2.default.join(root, ".env.local.example");
523
- const current = import_node_fs2.default.existsSync(file) ? import_node_fs2.default.readFileSync(file, "utf8") : null;
524
- if (current !== null && /^G8_API_KEY=/m.test(current)) {
525
- out("keep .env.local.example (G8_API_KEY already listed)");
526
- return;
527
- }
528
- out(`${dryRun ? "would " : ""}${current === null ? "create" : "update"} .env.local.example (+ G8_API_KEY=)`);
529
- if (dryRun) return;
530
- const base = current === null ? "" : current.endsWith("\n") || current === "" ? current : `${current}
531
- `;
532
- import_node_fs2.default.writeFileSync(file, `${base}G8_API_KEY=
533
- `, "utf8");
534
- }
535
- function runAdd(opts, reg, io, cwd) {
536
- const root = import_node_path2.default.resolve(cwd, opts.dir);
537
- const pkg = readPkg(root);
538
- if (!pkg) {
539
- io.err(`no package.json in ${root}. Run this from your app's root, or pass --dir.`);
540
- return 1;
541
- }
542
- let parsed;
543
- let blocks;
544
- try {
545
- parsed = parseSpec(opts.spec);
546
- blocks = resolveWithDeps(reg, [parsed.blockId]);
547
- const main2 = blocks[blocks.length - 1];
548
- if (parsed.component && !main2.components.some((c) => c.name === parsed.component)) {
549
- const names = main2.components.map((c) => c.name).join(", ") || "none";
550
- throw new Error(`block "${main2.id}" has no component "${parsed.component}" (available: ${names})`);
551
- }
552
- } catch (e) {
553
- io.err(e.message);
554
- return 1;
555
- }
556
- const exists = (p) => import_node_fs2.default.existsSync(import_node_path2.default.join(root, p));
557
- const target = opts.target ?? (exists("src") && !exists("components") && !exists("app") ? "src/components/g8" : "components/g8");
558
- const targetAbs = import_node_path2.default.resolve(root, target);
559
- const isNext = Boolean(pkg.dependencies?.next ?? pkg.devDependencies?.next);
560
- const dry = opts.dryRun;
561
- const verb = (a) => a === "unchanged" ? "keep" : a === "skip" ? "skip" : dry ? `would ${a}` : a;
562
- const entries = /* @__PURE__ */ new Map();
563
- const only = /* @__PURE__ */ new Map();
564
- const main = blocks[blocks.length - 1];
565
- for (const b of blocks) {
566
- const comp = b === main ? parsed.component : void 0;
567
- only.set(b.id, comp);
568
- entries.set(b.id, planBlock(b, comp, reg, targetAbs, root, opts.force));
569
- }
570
- const all = [...entries.values()].flat();
571
- const route = planAppFiles(blocks, reg, targetAbs, root, isNext, opts.force);
572
- io.out(`${dry ? "Dry run: " : ""}adding ${blocks.map((b) => b.id).join(" + ")} to ${target}`);
573
- for (const f of [...all, ...route.files]) io.out(`${verb(f.action)} ${f.rel}${f.detail ? ` (${f.detail})` : ""}`);
574
- if (route.note) route.note.forEach((l) => io.out(l));
575
- if (!dry) {
576
- for (const f of [...all, ...route.files]) if (f.action === "create" || f.action === "update") writeFile(root, f);
577
- }
578
- ensureEnvExample(root, dry, io.out);
579
- const ledgerPath = import_node_path2.default.join(root, "graph8.json");
580
- const next = ledgerContent(readLedger(ledgerPath), posix(import_node_path2.default.relative(root, targetAbs)), entries, only);
581
- const prevText = import_node_fs2.default.existsSync(ledgerPath) ? import_node_fs2.default.readFileSync(ledgerPath, "utf8") : null;
582
- if (next !== prevText) {
583
- io.out(`${dry ? "would " : ""}${prevText === null ? "create" : "update"} graph8.json`);
584
- if (!dry) import_node_fs2.default.writeFileSync(ledgerPath, next, "utf8");
585
- } else io.out("keep graph8.json");
586
- return installPeers(blocks, pkg, root, opts, io);
587
- }
588
- function installPeers(blocks, pkg, root, opts, io) {
589
- const missing = missingPeers(blocks, pkg);
590
- if (!missing.length) return 0;
591
- const pm = detectPackageManager(root);
592
- const { cmd, args } = installCommand(pm, missing);
593
- const shown = `${cmd} ${args.join(" ")}`;
594
- if (!opts.install || opts.dryRun) {
595
- io.out(`${opts.dryRun ? "would run" : "not installing (--no-install). Run"}: ${shown}`);
596
- return 0;
597
- }
598
- io.out(`installing peer dependencies: ${shown}`);
599
- const code = io.spawn(cmd, args, root);
600
- if (code !== 0) {
601
- io.err(`peer dependency install failed (exit ${code}). Run it yourself: ${shown}`);
602
- return 1;
603
- }
604
- return 0;
605
- }
606
- function appTargetPath(root, target) {
607
- const useSrc = import_node_fs2.default.existsSync(import_node_path2.default.join(root, "src", "app")) && !import_node_fs2.default.existsSync(import_node_path2.default.join(root, "app"));
608
- return useSrc && target.startsWith("app/") ? `src/${target}` : target;
609
- }
610
- function planAppFiles(blocks, reg, targetAbs, root, isNext, force) {
611
- const files = [];
612
- let skipped = false;
613
- for (const b of blocks) {
614
- for (const [file, target] of appTargeted(b)) {
615
- if (!isNext) {
616
- skipped = true;
617
- continue;
618
- }
619
- const destAbs = import_node_path2.default.join(root, appTargetPath(root, target));
620
- const src = import_node_path2.default.join(b.root, file);
621
- const content = rewriteImports(import_node_fs2.default.readFileSync(src, "utf8"), { reg, targetAbs, destDir: import_node_path2.default.dirname(destAbs) });
622
- files.push(classify(destAbs, posix(import_node_path2.default.relative(root, destAbs)), content, force));
623
- }
624
- }
625
- return { files, note: skipped ? PROXY_CONTRACT : void 0 };
626
- }
627
-
628
- // src/blocks/create.ts
629
- var import_node_fs3 = __toESM(require("fs"));
630
- var import_node_path3 = __toESM(require("path"));
631
- var CREATE_USAGE = `Usage: npx @graph8/sdk create <stack> <dir> [options]
632
-
633
- Scaffold a Next.js (App Router, TypeScript, Tailwind CSS v4) app for a stack and
634
- install the stack's blocks into it. Run \`graph8-sdk list\` to see the stacks.
635
- --package-manager <npm|pnpm|yarn|bun> default: the one running the command, else npm
636
- --no-install write the files and blocks, do not install dependencies
637
- --dry-run print what would be written, write and install nothing
638
- --force allow a non-empty <dir> (template files are overwritten, and graph8.json
639
- is reset: blocks you added to that app earlier are no longer recorded)
640
- Then: set G8_API_KEY in <dir>/.env.local and run \`npx @graph8/sdk doctor --stack <stack>\`.`;
641
- var PACKAGE_MANAGERS = ["npm", "pnpm", "yarn", "bun"];
642
- function parseCreateArgs(argv) {
643
- const o = { stack: "", dir: "", install: true, dryRun: false, force: false };
644
- const positional = [];
645
- for (let i = 0; i < argv.length; i++) {
646
- const arg = argv[i];
647
- const eq = arg.startsWith("--") ? arg.indexOf("=") : -1;
648
- const [flag, inline] = eq > 0 ? [arg.slice(0, eq), arg.slice(eq + 1)] : [arg, void 0];
649
- if (flag === "--package-manager") {
650
- const v = inline ?? argv[++i];
651
- if (!PACKAGE_MANAGERS.includes(v)) throw new Error(`--package-manager must be one of ${PACKAGE_MANAGERS.join(", ")}
652
-
653
- ${CREATE_USAGE}`);
654
- o.packageManager = v;
655
- } else if (arg === "--no-install") o.install = false;
656
- else if (arg === "--dry-run") o.dryRun = true;
657
- else if (arg === "--force") o.force = true;
658
- else if (arg.startsWith("-")) throw new Error(`unknown option ${arg}
659
-
660
- ${CREATE_USAGE}`);
661
- else positional.push(arg);
662
- }
663
- if (positional.length !== 2) throw new Error(`create needs a stack and a directory
664
-
665
- ${CREATE_USAGE}`);
666
- [o.stack, o.dir] = positional;
667
- return o;
668
- }
669
- function packageManagerFromUserAgent(ua) {
670
- const name = /^(npm|pnpm|yarn|bun)\//.exec(ua ?? "")?.[1];
671
- return name ?? "npm";
672
- }
673
- function appNameFor(dir) {
674
- const base = import_node_path3.default.basename(import_node_path3.default.resolve(dir)).toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[._-]+|-+$/g, "");
675
- return base || "g8-app";
676
- }
677
- function render(template, vars) {
678
- return template.replace(/\{\{(\w+)\}\}/g, (_m, key) => {
679
- if (!(key in vars)) throw new Error(`template placeholder {{${key}}} has no value`);
680
- return vars[key];
681
- });
682
- }
683
- var quoted = (p) => /^[\w@%+=:,./-]+$/.test(p) ? p : JSON.stringify(p);
684
- var posix2 = (p) => p.split(import_node_path3.default.sep).join("/");
685
- function walk(dir, base = dir) {
686
- const out = [];
687
- for (const e of import_node_fs3.default.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
688
- const full = import_node_path3.default.join(dir, e.name);
689
- if (e.isDirectory()) out.push(...walk(full, base));
690
- else out.push(posix2(import_node_path3.default.relative(base, full)));
691
- }
692
- return out;
693
- }
694
- function outputName(rel) {
695
- const noExt = rel.replace(/\.tmpl$/, "");
696
- const dir = import_node_path3.default.posix.dirname(noExt);
697
- const base = import_node_path3.default.posix.basename(noExt).replace(/^dot-/, ".");
698
- return dir === "." ? base : `${dir}/${base}`;
699
- }
700
- function pageFileFor(navPath) {
701
- const segs = navPath.split("/").filter(Boolean);
702
- return ["app", ...segs, "page.tsx"].join("/");
703
- }
704
- function componentImportPath(block, file) {
705
- const rel = installedRel(file) ?? file;
706
- return `@/components/g8/${block.dir}/${rel.replace(/\.(tsx|ts|jsx|js)$/, "")}`;
707
- }
708
- function stackProblem(reg, stack) {
709
- const { missing } = stackStatus(reg, stack);
710
- if (missing.length) {
711
- const ready = reg.stacks.filter((s) => !stackStatus(reg, s).missing.length).map((s) => s.id);
712
- return [
713
- `stack "${stack.id}" is planned: these blocks are not in this version of @graph8/sdk yet: ${missing.join(", ")}.`,
714
- `Stacks you can create now: ${ready.join(", ") || "none"}. Or create one of those and add blocks as they ship (\`graph8-sdk add <block>\`).`
715
- ].join("\n");
716
- }
717
- const seen = /* @__PURE__ */ new Set();
718
- for (const n of stack.nav) {
719
- const block = findBlock(reg, n.block);
720
- if (!stack.blocks.includes(n.block)) return `stack "${stack.id}" nav "${n.label}" uses block "${n.block}" which the stack does not include.`;
721
- if (!block?.components.some((c) => c.name === n.component)) return `stack "${stack.id}" nav "${n.label}" uses ${n.component}, which block "${n.block}" does not declare.`;
722
- if (/[\[\]]/.test(n.path)) return `stack "${stack.id}" nav "${n.label}" uses ${n.path}: dynamic segments cannot be a sidebar link.`;
723
- if (seen.has(n.path)) return `stack "${stack.id}" has two nav entries at ${n.path}.`;
724
- seen.add(n.path);
725
- }
726
- return null;
727
- }
728
- function renderPages(reg, stack, templates) {
729
- const pageTpl = import_node_fs3.default.readFileSync(import_node_path3.default.join(templates, "_pages", "route-page.tsx.tmpl"), "utf8");
730
- const files = stack.nav.map((n) => {
731
- const block = findBlock(reg, n.block);
732
- const comp = block.components.find((c) => c.name === n.component);
733
- const content = render(pageTpl, {
734
- stackId: stack.id,
735
- block: block.id,
736
- blockDir: `components/g8/${block.dir}`,
737
- component: n.component,
738
- importPath: componentImportPath(block, comp.file),
739
- propsJson: JSON.stringify(n.props ?? {}, null, 2)
740
- });
741
- return { rel: pageFileFor(n.path), content };
742
- });
743
- if (stack.nav.length && !stack.nav.some((n) => n.path === "/")) {
744
- const redirect = import_node_fs3.default.readFileSync(import_node_path3.default.join(templates, "_pages", "redirect-page.tsx.tmpl"), "utf8");
745
- files.push({ rel: "app/page.tsx", content: render(redirect, { firstPathJson: JSON.stringify(stack.nav[0].path) }) });
746
- }
747
- return files;
748
- }
749
- function peerDependenciesFor(blocks, io) {
750
- const peers = {};
751
- for (const b of blocks) for (const [name, range] of Object.entries(b.peerDependencies)) peers[name] ??= range;
752
- const usesUi = blocks.some((b) => walk(b.root).some((f) => /\.(tsx?|jsx?)$/.test(f) && /["']@graph8\/sdk\/ui["'/]/.test(import_node_fs3.default.readFileSync(import_node_path3.default.join(b.root, f), "utf8"))));
753
- if (usesUi && io.sdkPackageJson && import_node_fs3.default.existsSync(io.sdkPackageJson)) {
754
- const sdk = JSON.parse(import_node_fs3.default.readFileSync(io.sdkPackageJson, "utf8"));
755
- for (const [name, range] of Object.entries(sdk.peerDependencies ?? {})) peers[name] ??= range;
756
- }
757
- return peers;
758
- }
759
- function mergedPackageJson(rendered, peers) {
760
- const pkg = JSON.parse(rendered);
761
- for (const [name, range] of Object.entries(peers)) pkg.dependencies[name] ??= range;
762
- pkg.dependencies = Object.fromEntries(Object.entries(pkg.dependencies).sort(([a], [b]) => a.localeCompare(b)));
763
- return `${JSON.stringify(pkg, null, 2)}
764
- `;
765
- }
766
- function planCreate(reg, stack, appName, pm, io) {
767
- const templates = import_node_path3.default.join(io.templatesRoot, "next-app");
768
- const blocks = resolveWithDeps(reg, stack.blocks);
769
- const devCommand = pm === "npm" ? "npm run dev" : `${pm} dev`;
770
- const vars = {
771
- appName,
772
- sdkVersion: SDK_VERSION,
773
- stackId: stack.id,
774
- title: stack.title,
775
- titleJson: JSON.stringify(stack.title),
776
- navJson: JSON.stringify(stack.nav.map((n) => ({ label: n.label, path: n.path })), null, 2),
777
- devCommand,
778
- screens: stack.nav.map((n) => `- ${n.label}: \`${n.path}\` (${n.block} block, \`${n.component}\`)`).join("\n") || "- (none yet: add a block with `npx @graph8/sdk add <block>`)"
779
- };
780
- const files = [];
781
- for (const rel of walk(templates).filter((f) => !f.startsWith("_pages/"))) {
782
- let content = render(import_node_fs3.default.readFileSync(import_node_path3.default.join(templates, rel), "utf8"), vars);
783
- if (rel === "package.json.tmpl") content = mergedPackageJson(content, peerDependenciesFor(blocks, io));
784
- files.push({ rel: outputName(rel), content });
785
- }
786
- files.push(...renderPages(reg, stack, templates));
787
- const ledger = { sdkVersion: SDK_VERSION, target: "components/g8", stack: stack.id, blocks: {} };
788
- files.push({ rel: "graph8.json", content: `${JSON.stringify(ledger, null, 2)}
789
- ` });
790
- return files;
791
- }
792
- function dirProblem(abs, force) {
793
- if (!import_node_fs3.default.existsSync(abs)) return null;
794
- if (!import_node_fs3.default.statSync(abs).isDirectory()) return `${abs} exists and is not a directory.`;
795
- if (force || import_node_fs3.default.readdirSync(abs).length === 0) return null;
796
- return `${abs} is not empty. Pick a new directory, or pass --force to write into it (template files are overwritten).`;
797
- }
798
- function runCreate(opts, reg, io, cwd) {
799
- const stack = reg.stacks.find((s) => s.id === opts.stack);
800
- if (!stack) {
801
- io.err(`unknown stack "${opts.stack}". Stacks: ${reg.stacks.map((s) => s.id).join(", ") || "none"} (\`graph8-sdk list\`).`);
802
- return 1;
803
- }
804
- const problem = stackProblem(reg, stack);
805
- if (problem) return io.err(problem), 1;
806
- const abs = import_node_path3.default.resolve(cwd, opts.dir);
807
- const dirIssue = dirProblem(abs, opts.force);
808
- if (dirIssue) return io.err(dirIssue), 1;
809
- const pm = opts.packageManager ?? packageManagerFromUserAgent(io.userAgent);
810
- let files;
811
- try {
812
- files = planCreate(reg, stack, appNameFor(opts.dir), pm, io);
813
- } catch (e) {
814
- io.err(e.message);
815
- return 1;
816
- }
817
- const blocks = resolveWithDeps(reg, stack.blocks);
818
- io.out(`${opts.dryRun ? "Dry run: " : ""}creating "${stack.title}" (${stack.id}) in ${opts.dir}`);
819
- for (const f of files) io.out(`${opts.dryRun ? "would write" : "write"} ${f.rel}`);
820
- if (opts.dryRun) {
821
- io.out(`would add blocks: ${blocks.map((b) => b.id).join(", ")}`);
822
- io.out(opts.install ? `would run: ${pm} install` : "would not install dependencies (--no-install)");
823
- return 0;
824
- }
825
- for (const f of files) {
826
- const dest = import_node_path3.default.join(abs, f.rel);
827
- import_node_fs3.default.mkdirSync(import_node_path3.default.dirname(dest), { recursive: true });
828
- import_node_fs3.default.writeFileSync(dest, f.content, "utf8");
829
- }
830
- return finish(opts, reg, stack, blocks, abs, pm, io, cwd);
831
- }
832
- function finish(opts, reg, stack, blocks, abs, pm, io, cwd) {
833
- for (const b of blocks) {
834
- const code = runAdd({ spec: b.id, dir: abs, target: "components/g8", dryRun: false, force: opts.force, install: false }, reg, io, cwd);
835
- if (code !== 0) {
836
- io.err(`adding block "${b.id}" failed; the app in ${opts.dir} is incomplete.`);
837
- return code;
838
- }
839
- }
840
- if (opts.install) {
841
- io.out(`installing dependencies: ${pm} install`);
842
- const code = io.spawn(pm, ["install"], abs);
843
- if (code !== 0) {
844
- io.err(`dependency install failed (exit ${code}). Run it yourself: cd ${quoted(opts.dir)} && ${pm} install`);
845
- return 1;
846
- }
847
- }
848
- const cd = opts.dir === "." ? "" : `cd ${quoted(opts.dir)} && `;
849
- io.out(
850
- [
851
- "",
852
- `Done. Next:`,
853
- ` 1. set G8_API_KEY in ${import_node_path3.default.posix.join(opts.dir, ".env.local")} (copy .env.local.example)`,
854
- ` 2. ${cd}npx @graph8/sdk doctor --stack ${stack.id}`,
855
- ` 3. ${cd}${opts.install ? "" : `${pm} install && `}${pm === "npm" ? "npm run dev" : `${pm} dev`}`
856
- ].join("\n")
857
- );
858
- return 0;
859
- }
860
-
861
- // src/blocks/doctor.ts
862
- var import_node_fs4 = __toESM(require("fs"));
863
- var import_node_path4 = __toESM(require("path"));
864
-
865
- // src/generated/billable.ts
866
- var BILLABLE_ROUTES = {
867
- GET: [
868
- "/apps/{app_id}/runtime",
869
- "/audience-syncs/{config_id}/runs",
870
- "/campaigns/{campaign_id}/dashboard",
871
- "/companies/{company_id}/next-best-step",
872
- "/contacts/{contact_id}/next-best-step",
873
- "/desk/kickoff",
874
- "/desk/kickoff/audio",
875
- "/radar/competitors/{competitor_id}/discover-pages",
876
- "/repos/{repo_id}/scan",
877
- "/seo/domains/{domain}/backlinks",
878
- "/seo/domains/{domain}/overview",
879
- "/voice/campaigns/call-scripts",
880
- "/voice/campaigns/email-templates",
881
- "/voice/campaigns/templates",
882
- "/voice/research/business-data",
883
- "/voice/research/competitors",
884
- "/voice/research/faq",
885
- "/voice/research/keywords",
886
- "/voice/research/prospect-company",
887
- "/work/routines/{routine_id}/runs"
888
- ],
889
- PATCH: [
890
- "/enrichment/auto-enrichment/configs/{config_id}",
891
- "/intent/keywords/{keyword_id}",
892
- "/intent/keywords/{keyword_id}/sync-config",
893
- "/marketplace/agencies/{slug}",
894
- "/marketplace/agencies/{slug}/reps/{sdr_id}",
895
- "/marketplace/agencies/{slug}/service-statements/{statement_id}",
896
- "/marketplace/applications/{application_id}",
897
- "/marketplace/conversations/{conversation_id}/state",
898
- "/marketplace/job-posts/{post_id}",
899
- "/marketplace/me/meetings/{meeting_id}",
900
- "/marketplace/me/messages/{conversation_id}/state",
901
- "/marketplace/me/profile",
902
- "/marketplace/me/service-statements/{statement_id}",
903
- "/marketplace/org/meetings/{meeting_id}",
904
- "/radar/competitors/{competitor_id}",
905
- "/radar/gaps/{gap_id}",
906
- "/research-reports/{report_id}",
907
- "/social-listeners/settings",
908
- "/social-listeners/{listener_id}"
909
- ],
910
- POST: [
911
- "/accounts/{account_id}/generate-outreach-plan",
912
- "/accounts/{account_id}/security-questionnaires",
913
- "/accounts/{account_id}/stakeholders/{stakeholder_id}/generate-outreach",
914
- "/ad-creatives",
915
- "/ad-creatives/batch",
916
- "/ad-creatives/concepts",
917
- "/ad-creatives/video/clips",
918
- "/ad-creatives/video/clips/prompt-enhance",
919
- "/ad-creatives/video/clips/{clip_id}/continue",
920
- "/ad-creatives/video/clips/{clip_id}/extend",
921
- "/ad-creatives/video/clips/{clip_id}/regenerate",
922
- "/ad-creatives/{creative_id}/ai-review",
923
- "/ad-creatives/{creative_id}/chat-edit",
924
- "/ad-creatives/{creative_id}/image-edit-at-point",
925
- "/ad-creatives/{creative_id}/persona-lens",
926
- "/ad-creatives/{creative_id}/regenerate-copy",
927
- "/ad-creatives/{creative_id}/regenerate-image",
928
- "/ad-creatives/{creative_id}/retry",
929
- "/ad-creatives/{creative_id}/rewrite-field",
930
- "/ad-creatives/{creative_id}/size-set",
931
- "/agent/chat",
932
- "/agent/onboarding/discovery/turn",
933
- "/agent/operators/{operator}/approvals/{task_id}/decide",
934
- "/agent/sessions/{session_id}/voice",
935
- "/agent/work-items",
936
- "/app-pages/{page_id}/generate",
937
- "/campaigns/generate-ideas",
938
- "/campaigns/generate-ideas-async",
939
- "/campaigns/recommend-personas",
940
- "/campaigns/{campaign_id}/audience-pipeline/accept-filters",
941
- "/campaigns/{campaign_id}/audience-pipeline/trigger",
942
- "/campaigns/{campaign_id}/documents/{document_id}/regenerate",
943
- "/campaigns/{campaign_id}/sequence/steps/{step_id}/generate",
944
- "/companies/enrich/bulk",
945
- "/companies/enrich/people/bulk",
946
- "/companies/enrich/stakeholders/bulk",
947
- "/companies/import",
948
- "/companies/{company_id}/documents/types/{doc_type}/generate",
949
- "/companies/{company_id}/documents/types/{doc_type}/regenerate",
950
- "/companies/{company_id}/enrich",
951
- "/companies/{company_id}/retry-intelligence",
952
- "/companies/{company_id}/retry-synthesis",
953
- "/companies/{company_id}/workflows/{workflow_type}/start",
954
- "/company-profile/generate",
955
- "/company-profile/reindex",
956
- "/company-profile/retry",
957
- "/contacts/unlock-info",
958
- "/contacts/verify-email",
959
- "/contacts/verify-email/async",
960
- "/contacts/workflow-executions/run",
961
- "/contacts/{contact_id}/documents/generate-all",
962
- "/contacts/{contact_id}/documents/types/{doc_type}/generate",
963
- "/contacts/{contact_id}/documents/types/{doc_type}/regenerate",
964
- "/content-workspace/import/crawl",
965
- "/content/calendar/ideas/{idea_id}/create-content",
966
- "/content/calendar/mine-signals",
967
- "/content/calendar/repurpose/ad",
968
- "/content/calendar/repurpose/linkedin-post",
969
- "/content/grids/{grid_id}/columns/{column_id}/run",
970
- "/content/grids/{grid_id}/rows/{row_id}/run/{column_id}",
971
- "/content/pages/enrich-seo-bulk",
972
- "/content/pages/{page_id}/enrich-seo",
973
- "/copilot/chat",
974
- "/copilot/landing-page-action",
975
- "/copilot/memory/promote",
976
- "/customer-success/companies/{company_id}/brief/synthesize",
977
- "/deals/{deal_id}/documents/generate-all",
978
- "/deals/{deal_id}/documents/types/{doc_type}/generate",
979
- "/deals/{deal_id}/documents/types/{doc_type}/regenerate",
980
- "/deals/{deal_id}/next-best-step",
981
- "/desk/accounts/{account_id}/committee/{contact_id}/find-linkedin",
982
- "/desk/accounts/{account_id}/plan/draft",
983
- "/desk/ask",
984
- "/desk/decisions/{decision_id}/replan",
985
- "/desk/email-improve",
986
- "/desk/email-score",
987
- "/desk/org-settings/plan-stages/run",
988
- "/desk/voice-memo",
989
- "/dialer/parallel-calls/grading/backfill",
990
- "/dialer/sessions/prewarm-voicemails",
991
- "/enrichment/ai-formula/generate",
992
- "/enrichment/ai-formula/generate-condition",
993
- "/enrichment/ai/batch/enrich",
994
- "/enrichment/ai/enrich",
995
- "/enrichment/ai/qualification/generate-templates",
996
- "/enrichment/ai/qualification/run",
997
- "/enrichment/auto-enrichment/configs",
998
- "/enrichment/enrich",
999
- "/enrichment/find-people/configure",
1000
- "/enrichment/find-people/search-import",
1001
- "/enrichment/find-people/{config_id}/import",
1002
- "/enrichment/find-people/{config_id}/preview",
1003
- "/enrichment/find-people/{config_id}/run",
1004
- "/enrichment/generate-prompt",
1005
- "/enrichment/jobs/{job_id}/resume",
1006
- "/enrichment/list-routing/generate-filter",
1007
- "/enrichment/lists/{list_id}/pipelines",
1008
- "/enrichment/lists/{list_id}/pipelines/ai-draft",
1009
- "/enrichment/lists/{list_id}/pipelines/{pipeline_id}/enabled",
1010
- "/enrichment/lists/{list_id}/pipelines/{pipeline_id}/runs",
1011
- "/enrichment/lookup/company",
1012
- "/enrichment/lookup/person",
1013
- "/enrichment/person",
1014
- "/enrichment/record-sync/{entity_type}/run",
1015
- "/enrichment/verify-email",
1016
- "/enrichment/waterfall/configs",
1017
- "/enrichment/waterfall/enrich",
1018
- "/global-context/generate-all",
1019
- "/global-context/generate/{doc_type}",
1020
- "/hiring-signals/aggregators",
1021
- "/hiring-signals/companies",
1022
- "/hiring-signals/companies/job-counts",
1023
- "/hiring-signals/companies/resolve-ids",
1024
- "/hiring-signals/decision-makers",
1025
- "/hiring-signals/job-changes/match",
1026
- "/hiring-signals/jobs",
1027
- "/hiring-signals/recruiters",
1028
- "/icps/generate",
1029
- "/inbox/drafts/generate",
1030
- "/intelligence/analyze",
1031
- "/intelligence/retry",
1032
- "/intent/contacts/metadata",
1033
- "/intent/keywords/add",
1034
- "/intent/keywords/auto-seed-proposals",
1035
- "/intent/keywords/create-from-domain",
1036
- "/intent/keywords/create-from-search",
1037
- "/intent/keywords/list",
1038
- "/intent/keywords/{keyword_id}/companies",
1039
- "/intent/keywords/{keyword_id}/contacts",
1040
- "/intent/keywords/{keyword_id}/filters",
1041
- "/intent/keywords/{keyword_id}/job-resolution",
1042
- "/intent/keywords/{keyword_id}/sync",
1043
- "/intent/keywords/{keyword_id}/urls",
1044
- "/intent/pages-by-domain",
1045
- "/intent/pages/contacts",
1046
- "/intent/pages/search",
1047
- "/intent/pages/visitor-counts",
1048
- "/intent/pages/visitors",
1049
- "/landing-pages",
1050
- "/landing-pages/clone-homepage",
1051
- "/landing-pages/clone-html",
1052
- "/landing-pages/clone-url",
1053
- "/landing-pages/components/extract",
1054
- "/landing-pages/ensure-base-template",
1055
- "/landing-pages/{landing_page_id}/ai-review",
1056
- "/landing-pages/{landing_page_id}/ai-review/fix-with-ai",
1057
- "/landing-pages/{landing_page_id}/edit",
1058
- "/landing-pages/{landing_page_id}/edit-element",
1059
- "/landing-pages/{landing_page_id}/edit-page",
1060
- "/landing-pages/{landing_page_id}/generate-seo-metadata",
1061
- "/landing-pages/{landing_page_id}/rewrite-text",
1062
- "/leads/generate-content/batches",
1063
- "/leads/{lead_id}/generate-content",
1064
- "/library/case-studies/backfill",
1065
- "/lifecycle-contacts/unlock",
1066
- "/mailboxes/schedule-buy/plans",
1067
- "/mailboxes/schedule-buy/plans/{plan_id}/resume",
1068
- "/mailboxes/uncancel",
1069
- "/mailboxes/{mailbox_id}/health/placement-test",
1070
- "/marketplace/agencies/{slug}/intake/upload",
1071
- "/marketplace/agencies/{slug}/reps/{sdr_id}/unlist",
1072
- "/marketplace/agencies/{slug}/reps/{sdr_id}/video-intro",
1073
- "/marketplace/agencies/{slug}/service-statements",
1074
- "/marketplace/agencies/{slug}/service-statements/{statement_id}/line-items",
1075
- "/marketplace/agencies/{slug}/service-statements/{statement_id}/submit",
1076
- "/marketplace/commissions/entries",
1077
- "/marketplace/commissions/entries/{entry_id}/quality",
1078
- "/marketplace/commissions/entries/{entry_id}/reversals",
1079
- "/marketplace/commissions/entries/{entry_id}/statement",
1080
- "/marketplace/commissions/plans",
1081
- "/marketplace/commissions/plans/{version_id}/activate",
1082
- "/marketplace/commissions/preview",
1083
- "/marketplace/conversations/{conversation_id}/read",
1084
- "/marketplace/job-posts",
1085
- "/marketplace/job-posts/{post_id}/jd",
1086
- "/marketplace/me/headshot",
1087
- "/marketplace/me/invoices",
1088
- "/marketplace/me/invoices/{invoice_id}/approve",
1089
- "/marketplace/me/messages/{conversation_id}/read",
1090
- "/marketplace/me/offers/{hiring_id}/accept",
1091
- "/marketplace/me/offers/{hiring_id}/reject",
1092
- "/marketplace/me/payout-account/w8-certify",
1093
- "/marketplace/me/resume",
1094
- "/marketplace/me/service-statements",
1095
- "/marketplace/me/service-statements/{statement_id}/line-items",
1096
- "/marketplace/me/service-statements/{statement_id}/revise",
1097
- "/marketplace/me/service-statements/{statement_id}/submit",
1098
- "/marketplace/me/video-intro",
1099
- "/marketplace/me/work-certificates",
1100
- "/marketplace/opportunities/{post_id}/apply",
1101
- "/marketplace/org/hirings/{hiring_id}/meetings",
1102
- "/marketplace/org/hirings/{hiring_id}/terminate",
1103
- "/marketplace/org/hirings/{hiring_id}/withdraw",
1104
- "/marketplace/org/invoices/{invoice_id}/approve",
1105
- "/marketplace/org/invoices/{invoice_id}/dispute",
1106
- "/marketplace/org/service-statements/{statement_id}/approve",
1107
- "/marketplace/org/service-statements/{statement_id}/line-items",
1108
- "/marketplace/org/service-statements/{statement_id}/reject",
1109
- "/marketplace/org/service-statements/{statement_id}/request-changes",
1110
- "/marketplace/shortlist/export",
1111
- "/marketplace/talent/{sdr_id}/hire",
1112
- "/marketplace/talent/{sdr_id}/match/analyze",
1113
- "/marketplace/talent/{sdr_id}/match/refresh",
1114
- "/marketplace/terms-acknowledgements/ack-all",
1115
- "/meeting-transcripts/{transcript_id}/follow-up-draft",
1116
- "/meeting-transcripts/{transcript_id}/review/regenerate",
1117
- "/newsletters/templates/clone",
1118
- "/newsletters/templates/custom",
1119
- "/newsletters/templates/layout",
1120
- "/newsletters/{newsletter_id}/issues/{issue_id}/generate",
1121
- "/newsletters/{newsletter_id}/issues/{issue_id}/rewrite-text",
1122
- "/newsletters/{newsletter_id}/subscribers/import-audience",
1123
- "/newsletters/{newsletter_id}/subscribers/verify",
1124
- "/org-videos/generate",
1125
- "/org/brand-snapshot/refresh",
1126
- "/personas/generate",
1127
- "/phone-numbers/purchase",
1128
- "/pipelines/suggest",
1129
- "/placement/tests",
1130
- "/provider-domains/add-external",
1131
- "/provider-domains/check-availability",
1132
- "/provider-domains/checkout-purchase",
1133
- "/provider-domains/checkout-transfer",
1134
- "/provider-domains/{domain_id}/auto-warmup",
1135
- "/provider-domains/{domain_id}/check-propagation",
1136
- "/provider-domains/{domain_id}/checkout-mailboxes",
1137
- "/provider-domains/{domain_id}/connect-provider",
1138
- "/provider-domains/{domain_id}/dns",
1139
- "/public/enrich/lookup",
1140
- "/radar/aeo/run",
1141
- "/radar/competitors",
1142
- "/radar/competitors/bulk",
1143
- "/radar/competitors/{competitor_id}/scan",
1144
- "/radar/gaps/analyze",
1145
- "/radar/initiatives/measure",
1146
- "/radar/opportunities/approve-all",
1147
- "/radar/opportunities/{gap_id}/approve",
1148
- "/radar/reports",
1149
- "/radar/scan-all",
1150
- "/repos/{repo_id}/scan",
1151
- "/research-reports/generate",
1152
- "/research-reports/generate-all",
1153
- "/research-reports/retry",
1154
- "/research-reports/workflow/resume",
1155
- "/revenue/stage-docs/{lifecycle_entry_id}/{entity}/backfill",
1156
- "/scrape/requests",
1157
- "/search/companies",
1158
- "/search/companies/save",
1159
- "/search/contacts",
1160
- "/search/contacts/save",
1161
- "/seo/audits",
1162
- "/seo/keywords/research",
1163
- "/sequencer/content/email/generate",
1164
- "/sequencer/content/instructions/improve",
1165
- "/sequencer/content/linkedin/generate",
1166
- "/sequencer/templates/steps/generate",
1167
- "/social-listeners",
1168
- "/social-listeners/auto-seed/all",
1169
- "/social-listeners/auto-seed/brand",
1170
- "/social-listeners/auto-seed/competitors",
1171
- "/social-listeners/auto-seed/engagement",
1172
- "/social-listeners/auto-seed/keywords",
1173
- "/social-listeners/bulk-from-list",
1174
- "/social-listeners/integrations/x",
1175
- "/social-listeners/integrations/x/test",
1176
- "/social-listeners/posts/{post_id}/engagements/add-to-list",
1177
- "/social-listeners/posts/{post_id}/mark",
1178
- "/social-listeners/posts/{post_id}/save-contact",
1179
- "/social-listeners/queries/generate",
1180
- "/social/posts/generate",
1181
- "/tam/generate",
1182
- "/tam/regenerate",
1183
- "/tasks/{task_id}/start",
1184
- "/team-members/me/enrich-linkedin",
1185
- "/team-members/{team_member_id}/enrich-linkedin",
1186
- "/team-members/{team_member_id}/generate-post",
1187
- "/teams/auto-compose/suggest",
1188
- "/voice/agents/{agent_id}/documents/upload",
1189
- "/voice/call-flow-examples/transcribe",
1190
- "/voice/call-scripts/generate",
1191
- "/voice/campaigns/call-scripts",
1192
- "/voice/campaigns/email-enhancements",
1193
- "/voice/campaigns/email-spintax",
1194
- "/voice/campaigns/emails",
1195
- "/voice/campaigns/linkedin-messages",
1196
- "/voice/campaigns/onboarding/messages",
1197
- "/voice/campaigns/onboarding/threads/actions",
1198
- "/voice/campaigns/voicemails",
1199
- "/voice/chat-agent/messages",
1200
- "/voice/collections/{collection_id}/files/paste-text",
1201
- "/voice/collections/{collection_id}/files/upload",
1202
- "/voice/collections/{collection_id}/files/upload-multiple",
1203
- "/voice/collections/{collection_id}/reindex",
1204
- "/voice/collections/{collection_id}/scrape-sources",
1205
- "/voice/collections/{collection_id}/scrape-sources/{source_id}/run",
1206
- "/voice/email-agent/batch",
1207
- "/voice/email-agent/compose",
1208
- "/voice/email-agent/draft-reply",
1209
- "/voice/icp/queries",
1210
- "/voice/knowledge-base/files/upload",
1211
- "/voice/receptionist/messages",
1212
- "/voice/research/company",
1213
- "/voice/research/org-chart",
1214
- "/voice/research/prospect-data",
1215
- "/voice/research/prospect-profile",
1216
- "/voice/topics/classify",
1217
- "/voice/twins",
1218
- "/voice/twins/extract-linkedin",
1219
- "/voice/video-recordings/upload",
1220
- "/voice/voice-library/onboarding-voice",
1221
- "/voice/voice-library/voices",
1222
- "/voice/worksheets/calculate",
1223
- "/work/signals/promote",
1224
- "/workbench/tables/{table_id}/ai/generate-prompt",
1225
- "/workbench/tables/{table_id}/ai/groups/{group_id}/run",
1226
- "/workbench/tables/{table_id}/ai/preview",
1227
- "/workbench/tables/{table_id}/ai/run",
1228
- "/workbench/tables/{table_id}/ai/run-all",
1229
- "/workbench/tables/{table_id}/columns/{col_id}/ai/run",
1230
- "/workbench/tables/{table_id}/find-people/configure",
1231
- "/workbench/tables/{table_id}/find-people/{config_id}/run",
1232
- "/workbench/tables/{table_id}/import/search",
1233
- "/workbench/tables/{table_id}/import/search/research/preview",
1234
- "/workbench/tables/{table_id}/pipelines",
1235
- "/workbench/tables/{table_id}/scraper/providers/{provider}/runs",
1236
- "/workbench/tables/{table_id}/scraper/runs",
1237
- "/workbench/tables/{table_id}/suggest-mappings",
1238
- "/workbench/tables/{table_id}/triggers",
1239
- "/workbench/tables/{table_id}/unlock-rows",
1240
- "/workbench/tables/{table_id}/waterfall/configs/{column_id}/run"
1241
- ],
1242
- PUT: [
1243
- "/agent/operators/{operator}/enabled",
1244
- "/agent/operators/{operator}/skills/{skill_key}",
1245
- "/deliverability/autopilot/preferences",
1246
- "/enrichment/auto-enrichment/settings/{entity_type}",
1247
- "/enrichment/lists/{list_id}/pipelines/{pipeline_id}",
1248
- "/enrichment/record-sync/{entity_type}/policy",
1249
- "/enrichment/waterfall/configs/{column_id}",
1250
- "/marketplace/me/invoices/{invoice_id}",
1251
- "/marketplace/me/rates",
1252
- "/radar/settings",
1253
- "/workbench/tables/{table_id}/auto-enrich",
1254
- "/workbench/tables/{table_id}/pipelines/{pipeline_id}",
1255
- "/workbench/tables/{table_id}/schedule",
1256
- "/workbench/tables/{table_id}/triggers/{trigger_id}"
1257
- ]
1258
- };
1259
-
1260
- // src/generated/external.ts
1261
- var EXTERNAL_ROUTES = {
1262
- GET: [
1263
- "/dialer/sessions/token",
1264
- "/marketplace/me/connect/dashboard-link",
1265
- "/marketplace/me/connect/onboarding-link",
1266
- "/marketplace/me/payout-account/login-link",
1267
- "/marketplace/me/payout-account/onboarding-link",
1268
- "/voice/mcp-servers/{mcp_server_id}/tools"
1269
- ],
1270
- PATCH: [
1271
- "/ad-campaigns/{campaign_id}/ads/{ad_id}",
1272
- "/appointments/bookings/{booking_uid}/location",
1273
- "/appointments/bookings/{booking_uid}/sequence",
1274
- "/appointments/workflows/{workflow_id}",
1275
- "/audience-syncs/{config_id}",
1276
- "/campaigns/{campaign_id}",
1277
- "/campaigns/{campaign_id}/full",
1278
- "/content/calendar/{content_type}/{content_id}/schedule",
1279
- "/inbox/mailboxes/bulk",
1280
- "/inbox/mailboxes/{mailbox}/settings",
1281
- "/integrations/crm/meetings/{event_id}",
1282
- "/newsletters/{newsletter_id}/subscribers/{subscriber_id}/unsubscribe",
1283
- "/quotes/{quote_id}",
1284
- "/sequences/{sequence_id}",
1285
- "/sequences/{sequence_id}/steps/{step_id}",
1286
- "/voice/dialer/sessions/{session_id}/status",
1287
- "/voice/dispositions/{disposition_id}",
1288
- "/webhooks/{webhook_id}"
1289
- ],
1290
- POST: [
1291
- "/ad-campaigns/images",
1292
- "/ad-campaigns/status-refresh",
1293
- "/ad-campaigns/{campaign_id}/ads",
1294
- "/ad-campaigns/{campaign_id}/ads/batch",
1295
- "/ad-campaigns/{campaign_id}/ads/{ad_id}/status",
1296
- "/ad-campaigns/{campaign_id}/edit-preview",
1297
- "/ad-campaigns/{campaign_id}/retry",
1298
- "/ad-campaigns/{campaign_id}/status-refresh",
1299
- "/appointments/bookings",
1300
- "/appointments/bookings/import",
1301
- "/appointments/bookings/{booking_uid}/confirm",
1302
- "/appointments/bookings/{booking_uid}/guests",
1303
- "/appointments/bookings/{booking_uid}/mark-absent",
1304
- "/appointments/bookings/{booking_uid}/notes",
1305
- "/appointments/bookings/{booking_uid}/request-reschedule",
1306
- "/appointments/bookings/{booking_uid}/reschedule",
1307
- "/appointments/calendars/selected",
1308
- "/appointments/conferencing/connect/{provider}",
1309
- "/appointments/conferencing/{provider}/default",
1310
- "/appointments/workflows",
1311
- "/appointments/workflows/{workflow_id}/toggle",
1312
- "/audience-syncs",
1313
- "/audience-syncs/google/conversion-actions",
1314
- "/audience-syncs/google/conversions",
1315
- "/audience-syncs/google/video-assets/library",
1316
- "/audience-syncs/google/video-assets/links",
1317
- "/audience-syncs/google/video-assets/upload-urls",
1318
- "/audience-syncs/google/video-assets/uploads",
1319
- "/audience-syncs/{config_id}/launch",
1320
- "/audience-syncs/{config_id}/trigger",
1321
- "/campaigns",
1322
- "/campaigns/{campaign_id}/ad-images/{image_id}/hide",
1323
- "/campaigns/{campaign_id}/documents",
1324
- "/campaigns/{campaign_id}/launch",
1325
- "/campaigns/{campaign_id}/sequence/steps",
1326
- "/content/calendar/{content_type}/{content_id}/retry-publish",
1327
- "/crm-syncs/{provider}/companies/push",
1328
- "/crm-syncs/{provider}/contacts/push",
1329
- "/crm-syncs/{provider}/lists/push",
1330
- "/customer-success/companies/{company_id}/sync",
1331
- "/desk/accounts/{account_id}/committee/plays/{action_id}/run",
1332
- "/desk/accounts/{account_id}/committee/{contact_id}/linkedin-invite",
1333
- "/desk/decisions/{decision_id}/plays/{action_id}/run",
1334
- "/desk/decisions/{decision_id}/send-reply",
1335
- "/desk/decisions/{decision_id}/send-sms",
1336
- "/desk/plays/approvals/{execution_id}/decide",
1337
- "/dialer/parallel-calls/calls/{livekit_room_name}/drop-voicemail",
1338
- "/dialer/parallel-calls/manual-dial",
1339
- "/dialer/parallel-calls/redial-contact",
1340
- "/dialer/parallel-calls/sessions/start",
1341
- "/dialer/sessions/calls/drop-voicemail",
1342
- "/dialer/sessions/{session_id}/next-batch",
1343
- "/envelopes/{envelope_id}/resend",
1344
- "/envelopes/{envelope_id}/send",
1345
- "/feedback/sending-domains/verify",
1346
- "/feedback/surveys/{survey_id}/send",
1347
- "/forms/deliveries/{receipt_id}/retry",
1348
- "/forms/{form_key}/backfills",
1349
- "/forms/{form_key}/delivery/pause",
1350
- "/forms/{form_key}/delivery/resume",
1351
- "/inbox/emails/compose",
1352
- "/inbox/emails/{email_id}/send",
1353
- "/inbox/linkedin/accounts/sync",
1354
- "/inbox/linkedin/accounts/{account_id}/enable",
1355
- "/inbox/linkedin/compose",
1356
- "/inbox/mailboxes/activate",
1357
- "/inbox/mailboxes/{mailbox}/sync",
1358
- "/inbox/sms/compose",
1359
- "/inbox/{reply_id}/assign",
1360
- "/inbox/{reply_id}/send",
1361
- "/inbox/{reply_id}/tag",
1362
- "/integrations/connections",
1363
- "/integrations/connections/session-token",
1364
- "/integrations/crm-sync/audiences",
1365
- "/integrations/crm-sync/contacts/pull",
1366
- "/integrations/crm-sync/contacts/push",
1367
- "/integrations/crm-sync/deals/pull",
1368
- "/integrations/crm-sync/deals/push",
1369
- "/integrations/crm-sync/leads/previews",
1370
- "/integrations/crm-sync/leads/pull",
1371
- "/integrations/crm-sync/leads/push",
1372
- "/integrations/crm-sync/pull",
1373
- "/integrations/crm-sync/push",
1374
- "/integrations/crm/connections/{connection_id}/batch/contacts",
1375
- "/integrations/crm/connections/{connection_id}/companies",
1376
- "/integrations/crm/connections/{connection_id}/contacts",
1377
- "/integrations/crm/connections/{connection_id}/deals",
1378
- "/integrations/crm/connections/{connection_id}/leads",
1379
- "/integrations/crm/lists",
1380
- "/integrations/crm/lists/memberships",
1381
- "/integrations/crm/meetings",
1382
- "/integrations/linkedin-outreach/campaigns/{campaign_id}/pause",
1383
- "/integrations/linkedin-outreach/campaigns/{campaign_id}/resume",
1384
- "/integrations/linkedin-outreach/connection/reconnect",
1385
- "/integrations/linkedin-outreach/lists",
1386
- "/integrations/linkedin-outreach/lists/{list_id}/leads",
1387
- "/integrations/linkedin-outreach/workspaces/sync",
1388
- "/integrations/migrations/delta-sync",
1389
- "/integrations/migrations/discovery",
1390
- "/integrations/migrations/{migration_id}/confirm",
1391
- "/integrations/sugarcrm/connect",
1392
- "/integrations/sugarcrm/reconnect",
1393
- "/landing-pages/{landing_page_id}/publish",
1394
- "/landing-pages/{landing_page_id}/unpublish",
1395
- "/linkedin/accounts/sync",
1396
- "/marketplace/agencies/{slug}/reps/{sdr_id}/publish",
1397
- "/marketplace/me/messages/{conversation_id}/reply",
1398
- "/marketplace/me/payout-account/bank-details",
1399
- "/marketplace/me/payout-account/methods",
1400
- "/marketplace/me/payout-account/setup",
1401
- "/marketplace/me/sync-workbench",
1402
- "/marketplace/talent/{sdr_id}/messages",
1403
- "/marketplace/talent/{sdr_id}/request",
1404
- "/newsletters",
1405
- "/newsletters/{newsletter_id}/activate",
1406
- "/newsletters/{newsletter_id}/issues",
1407
- "/newsletters/{newsletter_id}/issues/{issue_id}/duplicate",
1408
- "/newsletters/{newsletter_id}/issues/{issue_id}/send",
1409
- "/newsletters/{newsletter_id}/issues/{issue_id}/send-test",
1410
- "/newsletters/{newsletter_id}/issues/{issue_id}/send/resume",
1411
- "/newsletters/{newsletter_id}/resume",
1412
- "/newsletters/{newsletter_id}/settings/verify-domain",
1413
- "/newsletters/{newsletter_id}/subscribers",
1414
- "/nurtures/{nurture_id}/launch",
1415
- "/org/sso/sync",
1416
- "/phone-numbers/a2p/register",
1417
- "/profile/deal-automations/sync-now",
1418
- "/programmatic-campaigns/{campaign_id}/pages/actions",
1419
- "/programmatic-campaigns/{campaign_id}/pages/{page_id}/actions",
1420
- "/provider-domains/{domain_id}/forwarding",
1421
- "/provider-domains/{domain_id}/mailbox-profile-picture",
1422
- "/provider-domains/{domain_id}/regenerate-nameservers",
1423
- "/provider-domains/{domain_id}/resend-transfer-password",
1424
- "/provider-domains/{domain_id}/resend-verification",
1425
- "/provider-domains/{domain_id}/set-nameservers",
1426
- "/provider-usage/refresh",
1427
- "/provider-usage/sync",
1428
- "/quote-billing-contact",
1429
- "/quote-contacts",
1430
- "/quote-settings/documents",
1431
- "/quote-templates",
1432
- "/quotes",
1433
- "/quotes/batch",
1434
- "/quotes/{quote_id}/duplicate",
1435
- "/quotes/{quote_id}/edit-as-draft",
1436
- "/quotes/{quote_id}/mark-accepted",
1437
- "/quotes/{quote_id}/send",
1438
- "/quotes/{quote_id}/unarchive",
1439
- "/radar/alerts/dispatch",
1440
- "/repos/{repo_id}/campaigns/{campaign_id}/launch",
1441
- "/roles/invite",
1442
- "/sales-coach/interviews/{interview_id}/members",
1443
- "/sales-coach/interviews/{interview_id}/members/{member_id}/send-invite",
1444
- "/sequencer/content/voice/test-call",
1445
- "/sequences",
1446
- "/sequences/generate-spintax",
1447
- "/sequences/{sequence_id}/contacts",
1448
- "/sequences/{sequence_id}/contacts/{contact_id}/resume",
1449
- "/sequences/{sequence_id}/pause",
1450
- "/sequences/{sequence_id}/resume",
1451
- "/sequences/{sequence_id}/run",
1452
- "/sequences/{sequence_id}/sync",
1453
- "/settings/scrape-config/crawl",
1454
- "/skills/{action_id}/execute",
1455
- "/skills/{action_id}/test-runs",
1456
- "/social-listeners/posts/{post_id}/add-to-sequence",
1457
- "/social/posts/{post_id}/engagers/add-to-audience",
1458
- "/social/posts/{post_id}/engagers/enroll",
1459
- "/social/posts/{post_id}/publish",
1460
- "/social/posts/{post_id}/schedule",
1461
- "/stripe/api-key",
1462
- "/stripe/sync",
1463
- "/team-members/sync",
1464
- "/voice/calls",
1465
- "/voice/dialer/sessions",
1466
- "/voice/dialer/sessions/{session_id}/resume",
1467
- "/voice/dispositions",
1468
- "/voice/inbound-calls/decline",
1469
- "/voice/inbound-calls/dialer/{call_id}/decline",
1470
- "/voice/inbound-calls/{room_name}/claim",
1471
- "/voice/mcp-servers/{mcp_server_id}/test",
1472
- "/webhooks",
1473
- "/webhooks/{webhook_id}/rotate-secret",
1474
- "/website-editor/publish",
1475
- "/website-editor/sync",
1476
- "/workbench/scraper/providers/{provider}/connection/test",
1477
- "/workbench/tables/{table_id}/import/crm",
1478
- "/workbench/tables/{table_id}/send-to-sequence",
1479
- "/workflows",
1480
- "/workflows/executions/approve",
1481
- "/workflows/executions/bulk-restart",
1482
- "/workflows/executions/request-changes",
1483
- "/workflows/executions/{execution_id}/pause",
1484
- "/workflows/executions/{execution_id}/restart",
1485
- "/workflows/executions/{execution_id}/resume",
1486
- "/workflows/executions/{execution_id}/stop",
1487
- "/workflows/integrations/{provider_name}/test",
1488
- "/workflows/validate",
1489
- "/workflows/workbenches",
1490
- "/workflows/{action_id}/execute",
1491
- "/workflows/{action_id}/trigger-reset"
1492
- ],
1493
- PUT: [
1494
- "/ad-campaigns/{campaign_id}",
1495
- "/appointments/calendars/destination",
1496
- "/appointments/conferencing/{provider}/link",
1497
- "/campaigns/{campaign_id}/audience",
1498
- "/campaigns/{campaign_id}/documents/{document_id}",
1499
- "/campaigns/{campaign_id}/sequence",
1500
- "/campaigns/{campaign_id}/sequence/steps/{step_id}",
1501
- "/desk/autonomy",
1502
- "/desk/org-settings",
1503
- "/inbox/linkedin/accounts/{account_id}",
1504
- "/integrations/crm-sync/pull-enabled",
1505
- "/integrations/crm-sync/push-enabled",
1506
- "/integrations/crm/connections/{connection_id}/batch/contacts",
1507
- "/integrations/crm/connections/{connection_id}/companies/{company_id}",
1508
- "/integrations/crm/connections/{connection_id}/contacts/{contact_id}",
1509
- "/integrations/crm/connections/{connection_id}/deals/{deal_id}",
1510
- "/integrations/crm/connections/{connection_id}/leads/{lead_id}",
1511
- "/integrations/linkedin-outreach/workspaces/active",
1512
- "/linkedin/accounts/{account_id}/schedule",
1513
- "/newsletters/{newsletter_id}/issues/{issue_id}",
1514
- "/quote-settings",
1515
- "/quote-templates/{template_id}",
1516
- "/quotes/{quote_id}",
1517
- "/sms/phone-numbers/{phone_number}",
1518
- "/social/autopilot/{account_id}",
1519
- "/workflows/{action_id}"
1520
- ]
1521
- };
1522
-
1523
- // src/http.ts
1524
- var G8Error = class _G8Error extends Error {
1525
- constructor(args) {
1526
- super(args.message);
1527
- this.name = "G8Error";
1528
- this.status = args.status;
1529
- this.type = args.type;
1530
- this.code = args.code;
1531
- this.requestId = args.requestId;
1532
- this.detail = args.detail;
1533
- this.retryable = args.retryable ?? false;
1534
- Object.setPrototypeOf(this, _G8Error.prototype);
1535
- }
1536
- };
1537
- function isRetryableStatus(status) {
1538
- return status === 429 || status >= 500 && status <= 599;
1539
- }
1540
- function isWriteMethod(method) {
1541
- const m = method.toUpperCase();
1542
- return m === "POST" || m === "PUT" || m === "PATCH" || m === "DELETE";
1543
- }
1544
- var API_BASE_SEGMENT = "/api/v1";
1545
- var billableMatchers = null;
1546
- var externalMatchers = null;
1547
- function templateToRegexSource(template) {
1548
- return template.split(/(\{[^}/]+\})/).map((part) => /^\{[^}/]+\}$/.test(part) ? "[^/]+" : part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("");
1549
- }
1550
- function buildMatchers(routes) {
1551
- const out = /* @__PURE__ */ new Map();
1552
- for (const [m, templates] of Object.entries(routes)) {
1553
- if (!templates.length) continue;
1554
- out.set(m, new RegExp(`^(?:${templates.map(templateToRegexSource).join("|")})/?$`));
1555
- }
1556
- return out;
1557
- }
1558
- function matcherFor(method) {
1559
- if (!billableMatchers) billableMatchers = buildMatchers(BILLABLE_ROUTES);
1560
- return billableMatchers.get(method.toUpperCase());
1561
- }
1562
- function externalMatcherFor(method) {
1563
- if (!externalMatchers) externalMatchers = buildMatchers(EXTERNAL_ROUTES);
1564
- return externalMatchers.get(method.toUpperCase());
1565
- }
1566
- function apiRelativePath(urlOrPath) {
1567
- let p = urlOrPath.replace(/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^/]*/, "");
1568
- p = p.split(/[?#]/, 1)[0];
1569
- const at = p.indexOf(API_BASE_SEGMENT + "/");
1570
- if (at < 0) return null;
1571
- return p.slice(at + API_BASE_SEGMENT.length);
1572
- }
1573
- function isBillableOperation(method, urlOrPath) {
1574
- const matcher = matcherFor(method);
1575
- if (!matcher) return false;
1576
- const p = apiRelativePath(urlOrPath);
1577
- return p != null && matcher.test(p);
1578
- }
1579
- function isExternalOperation(method, urlOrPath) {
1580
- const matcher = externalMatcherFor(method);
1581
- if (!matcher) return false;
1582
- const p = apiRelativePath(urlOrPath);
1583
- return p != null && matcher.test(p);
1584
- }
1585
- function isNoBlindRetryOperation(method, urlOrPath) {
1586
- return isBillableOperation(method, urlOrPath) || isExternalOperation(method, urlOrPath);
1587
- }
1588
- var PRE_SEND_ERROR_CODES = /* @__PURE__ */ new Set([
1589
- "ECONNREFUSED",
1590
- "ENOTFOUND",
1591
- "EAI_AGAIN",
1592
- "ENETUNREACH",
1593
- "EHOSTUNREACH",
1594
- "UND_ERR_CONNECT_TIMEOUT"
1595
- ]);
1596
- function isPreSendNetworkError(err) {
1597
- let cur = err;
1598
- for (let depth = 0; cur && depth < 4; depth++) {
1599
- if (typeof cur.code === "string" && PRE_SEND_ERROR_CODES.has(cur.code)) return true;
1600
- if (Array.isArray(cur.errors) && cur.errors.some((e) => isPreSendNetworkError(e))) return true;
1601
- cur = cur.cause;
1602
- }
1603
- return false;
1604
- }
1605
- function newIdempotencyKey() {
1606
- const c = globalThis.crypto;
1607
- if (c?.randomUUID) return c.randomUUID();
1608
- if (c?.getRandomValues) {
1609
- const bytes = c.getRandomValues(new Uint8Array(16));
1610
- return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
1611
- }
1612
- return `g8-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;
1613
- }
1614
- function parseRetryAfter(header, nowMs = Date.now()) {
1615
- if (!header) return null;
1616
- const secs = Number(header);
1617
- if (Number.isFinite(secs) && secs >= 0) return Math.round(secs * 1e3);
1618
- const dateMs = Date.parse(header);
1619
- if (!Number.isNaN(dateMs)) {
1620
- const delta = dateMs - nowMs;
1621
- return delta > 0 ? delta : 0;
1622
- }
1623
- return null;
1624
- }
1625
- function backoffDelayMs(attempt, baseMs = 200, rand = Math.random) {
1626
- const expo = Math.min(baseMs * 2 ** attempt, 1e4);
1627
- return Math.floor(expo / 2 + rand() * (expo / 2));
1628
- }
1629
- function defaultSleep(ms) {
1630
- return new Promise((resolve) => setTimeout(resolve, ms));
1631
- }
1632
- function buildQuery(query) {
1633
- if (!query) return "";
1634
- const qs = new URLSearchParams();
1635
- for (const [k, v] of Object.entries(query)) {
1636
- if (v != null) qs.set(k, String(v));
1637
- }
1638
- const s = qs.toString();
1639
- return s ? `?${s}` : "";
1640
- }
1641
- async function toG8Error(resp, retryable = isRetryableStatus(resp.status)) {
1642
- let body = void 0;
1643
- try {
1644
- const text = await resp.text();
1645
- body = text ? JSON.parse(text) : void 0;
1646
- } catch {
1647
- body = void 0;
1648
- }
1649
- const env = body ?? {};
1650
- const message = typeof env.message === "string" && env.message || typeof env.error === "string" && env.error || `Request failed with status ${resp.status}`;
1651
- return new G8Error({
1652
- message,
1653
- status: resp.status,
1654
- type: typeof env.type === "string" && env.type || typeof env.error === "string" && env.error || "api_error",
1655
- code: typeof env.code === "string" ? env.code : String(resp.status),
1656
- requestId: typeof env.request_id === "string" && env.request_id || resp.headers.get("x-request-id") || void 0,
1657
- detail: env.detail,
1658
- retryable
1659
- });
1660
- }
1661
- var TARGET_ORG_HEADER = "x-target-org-id";
1662
- var nonAgencyKeys = /* @__PURE__ */ new Set();
1663
- var warnedTargetOrgKeys = /* @__PURE__ */ new Set();
1664
- function keyFingerprint(apiKey) {
1665
- let h = 2166136261;
1666
- for (let i = 0; i < apiKey.length; i++) {
1667
- h ^= apiKey.charCodeAt(i);
1668
- h = Math.imul(h, 16777619) >>> 0;
1669
- }
1670
- return `${apiKey.length}:${h.toString(36)}`;
1671
- }
1672
- function warnIfTargetOrgIgnored(apiKey, headers) {
1673
- if (!Object.keys(headers).some((h) => h.toLowerCase() === TARGET_ORG_HEADER)) return;
1674
- const fp = keyFingerprint(apiKey);
1675
- if (!nonAgencyKeys.has(fp) || warnedTargetOrgKeys.has(fp)) return;
1676
- warnedTargetOrgKeys.add(fp);
1677
- if (typeof console !== "undefined" && typeof console.warn === "function") {
1678
- console.warn(
1679
- "[@graph8/sdk] X-Target-Org-Id is set, but the server reported this API key is not an agency key. The header is ignored for non-agency keys: this call runs against the key's own org. Use an agency key (minted with is_agency=true) to target a client org."
1680
- );
1681
- }
1682
- }
1683
- async function request(baseUrl, path6, apiKey, opts = {}) {
1684
- const {
1685
- method = "GET",
1686
- body,
1687
- headers = {},
1688
- query,
1689
- idempotencyKey,
1690
- maxRetries = 2,
1691
- retryBaseMs = 200,
1692
- signal,
1693
- fetchImpl = fetch,
1694
- sleepImpl = defaultSleep
1695
- } = opts;
1696
- const url = baseUrl.replace(/\/+$/, "") + path6 + buildQuery(query);
1697
- const finalHeaders = {
1698
- "Content-Type": "application/json",
1699
- Authorization: `Bearer ${apiKey}`,
1700
- ...clientIdentityHeaders(headers),
1701
- ...headers
1702
- };
1703
- warnIfTargetOrgIgnored(apiKey, headers);
1704
- const effectiveIdempotencyKey = idempotencyKey ?? (maxRetries > 0 && isWriteMethod(method) ? newIdempotencyKey() : void 0);
1705
- if (effectiveIdempotencyKey) finalHeaders["Idempotency-Key"] = effectiveIdempotencyKey;
1706
- const billable = isNoBlindRetryOperation(method, url);
1707
- let attempt = 0;
1708
- for (; ; ) {
1709
- let resp;
1710
- try {
1711
- resp = await fetchImpl(url, {
1712
- method,
1713
- headers: finalHeaders,
1714
- body: body == null ? void 0 : JSON.stringify(body),
1715
- signal
1716
- });
1717
- } catch (err) {
1718
- const safeToRetry = !billable || isPreSendNetworkError(err);
1719
- if (attempt < maxRetries && safeToRetry) {
1720
- await sleepImpl(backoffDelayMs(attempt, retryBaseMs));
1721
- attempt++;
1722
- continue;
1723
- }
1724
- throw new G8Error({
1725
- message: `Network error: ${err?.message ?? "request failed"}`,
1726
- status: 0,
1727
- type: "network_error",
1728
- retryable: safeToRetry
1729
- });
1730
- }
1731
- if (resp.ok) {
1732
- const text = await resp.text();
1733
- return text ? JSON.parse(text) : void 0;
1734
- }
1735
- const statusRetryable = isRetryableStatus(resp.status) && (!billable || resp.status === 429);
1736
- if (statusRetryable && attempt < maxRetries) {
1737
- const retryAfter = parseRetryAfter(resp.headers.get("retry-after"));
1738
- const delay = retryAfter != null ? retryAfter : backoffDelayMs(attempt, retryBaseMs);
1739
- await sleepImpl(delay);
1740
- attempt++;
1741
- continue;
1742
- }
1743
- throw await toG8Error(resp, billable ? statusRetryable : isRetryableStatus(resp.status));
1744
- }
1745
- }
1746
- function isServerRuntime() {
1747
- const g = globalThis;
1748
- if (typeof g.window !== "undefined") return false;
1749
- return typeof g.process?.versions?.node === "string";
1750
- }
1751
- function runtimeUserAgent() {
1752
- const g = globalThis;
1753
- const node = typeof g.process?.version === "string" ? ` node/${g.process.version.replace(/^v/, "")}` : "";
1754
- return `graph8-sdk-js/${SDK_VERSION}${node}`;
1755
- }
1756
- function clientIdentityHeaders(callerHeaders = {}) {
1757
- const taken = new Set(Object.keys(callerHeaders).map((h) => h.toLowerCase()));
1758
- const out = {};
1759
- if (!taken.has("x-g8-client")) out["X-G8-Client"] = SDK_CLIENT_ID;
1760
- if (!taken.has("user-agent") && isServerRuntime()) out["User-Agent"] = runtimeUserAgent();
1761
- return out;
1762
- }
1763
- async function* paginate(fetchPage) {
1764
- let cursor = void 0;
1765
- for (; ; ) {
1766
- const page = await fetchPage(cursor);
1767
- for (const item of page.data ?? []) yield item;
1768
- const next = page.pagination?.next_cursor;
1769
- if (!next) break;
1770
- cursor = next;
1771
- }
1772
- }
1773
172
 
1774
173
  // src/generated/operations.ts
1775
174
  var API_OPERATIONS = {
@@ -2113,6 +512,7 @@ var API_OPERATIONS = {
2113
512
  create_deal_deals_post: ["POST", "/deals", "deals", "createDeal", "write", "deals:write"],
2114
513
  create_deal_note_deals__deal_id__notes_post: ["POST", "/deals/{deal_id}/notes", "notes", "createDealNote", "write", "notes:write"],
2115
514
  create_default_waterfall_config_enrichment_waterfall_configs_post: ["POST", "/enrichment/waterfall/configs", "enrichment", "createDefaultWaterfallConfig", "billable", "enrichment:run"],
515
+ create_desk_card_desk_cards_post: ["POST", "/desk/cards", "desk", "createDeskCard", "write", "deals:write"],
2116
516
  create_dialer_session_voice_dialer_sessions_post: ["POST", "/voice/dialer/sessions", "voice", "createDialerSession", "external", "voice:run"],
2117
517
  create_dynamic_column_enrichment_columns_post: ["POST", "/enrichment/columns", "enrichment", "createDynamicColumn", "write", "enrichment:write"],
2118
518
  create_empty_list_lists_create_empty_post: ["POST", "/lists/create-empty", "lists", "createEmptyList", "write", "lists:write"],
@@ -5089,7 +3489,2542 @@ var API_OPERATIONS = {
5089
3489
  withdraw_contacts_from_sequences_contacts_withdraw_from_sequences_post: ["POST", "/contacts/withdraw-from-sequences", "contacts", "withdrawContactsFromSequences", "write", "contacts:write"],
5090
3490
  withdraw_org_hiring_marketplace_org_hirings__hiring_id__withdraw_post: ["POST", "/marketplace/org/hirings/{hiring_id}/withdraw", "marketplace", "withdrawOrgHiring", "billable", "marketplace:run"]
5091
3491
  };
5092
- var API_OPERATION_IDS = Object.keys(API_OPERATIONS);
3492
+ var API_OPERATION_IDS = Object.keys(API_OPERATIONS);
3493
+
3494
+ // src/blocks/prefixes.ts
3495
+ var PREFIX_START = "// graph8:allowed-prefixes:start";
3496
+ var PREFIX_END = "// graph8:allowed-prefixes:end";
3497
+ var LEGACY_LINE = /^const allowedPrefixes: string\[\] \| undefined = undefined;[^\n]*$/m;
3498
+ var OPS = API_OPERATIONS;
3499
+ function prefixOf(operationId) {
3500
+ const p = OPS[operationId]?.[1];
3501
+ if (typeof p !== "string") return null;
3502
+ const first = p.replace(/^\/+/, "").split("/")[0];
3503
+ return first && !first.startsWith("{") ? first : null;
3504
+ }
3505
+ function derivePrefixes(blocks) {
3506
+ const out = /* @__PURE__ */ new Set();
3507
+ for (const b of blocks) for (const id of b.operations) {
3508
+ const p = prefixOf(id);
3509
+ if (p) out.add(p);
3510
+ }
3511
+ return [...out].sort();
3512
+ }
3513
+ function formatPrefixes(prefixes) {
3514
+ if (!prefixes.length) return "[]";
3515
+ const items = prefixes.map((p) => JSON.stringify(p));
3516
+ const one = `[${items.join(", ")}]`;
3517
+ if (one.length <= 80) return one;
3518
+ const lines = [];
3519
+ let cur = "";
3520
+ for (const it of items) {
3521
+ const next = cur ? `${cur} ${it},` : `${it},`;
3522
+ if (cur && next.length + 2 > 110) {
3523
+ lines.push(cur);
3524
+ cur = `${it},`;
3525
+ } else cur = next;
3526
+ }
3527
+ if (cur) lines.push(cur);
3528
+ return `[
3529
+ ${lines.map((l) => ` ${l}`).join("\n")}
3530
+ ]`;
3531
+ }
3532
+ function prefixRegion(prefixes) {
3533
+ return [
3534
+ PREFIX_START,
3535
+ `// Generated from the operations of the installed blocks. Regenerate: npx @graph8/sdk doctor --fix-prefixes`,
3536
+ `const allowedPrefixes: string[] | undefined = ${formatPrefixes(prefixes)};`,
3537
+ PREFIX_END
3538
+ ].join("\n");
3539
+ }
3540
+ function hasPrefixRegion(source) {
3541
+ const a = source.indexOf(PREFIX_START);
3542
+ return a >= 0 && source.indexOf(PREFIX_END, a) > a;
3543
+ }
3544
+ function applyPrefixes(source, prefixes) {
3545
+ const a = source.indexOf(PREFIX_START);
3546
+ const b = a >= 0 ? source.indexOf(PREFIX_END, a) : -1;
3547
+ if (a < 0 || b < 0) return null;
3548
+ return `${source.slice(0, a)}${prefixRegion(prefixes)}${source.slice(b + PREFIX_END.length)}`;
3549
+ }
3550
+ function upgradeLegacyRoute(source, prefixes) {
3551
+ return LEGACY_LINE.test(source) ? source.replace(LEGACY_LINE, prefixRegion(prefixes)) : null;
3552
+ }
3553
+ function stripComments(source) {
3554
+ return source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:"'`])\/\/[^\n]*/g, "$1");
3555
+ }
3556
+ function inspectRoute(source) {
3557
+ const code = stripComments(source);
3558
+ const flag = (name) => new RegExp(`\\b${name}\\s*:\\s*true\\b`).test(code);
3559
+ const m = /\bconst\s+allowedPrefixes\b[^=]*=\s*([^;]*);/.exec(code);
3560
+ let prefixes = "custom";
3561
+ const expr = m?.[1].trim();
3562
+ if (!m || expr === "undefined") prefixes = void 0;
3563
+ else if (expr && /^\[[\s\S]*\]$/.test(expr) && /^\[(\s*(["'])[^"'\n]*\2\s*,?)*\s*\]$/.test(expr)) {
3564
+ prefixes = [...expr.matchAll(/(["'])([^"'\n]*)\1/g)].map((x) => x[2]);
3565
+ }
3566
+ if (!m && /\ballowedPrefixes\s*:/.test(code)) prefixes = "custom";
3567
+ return {
3568
+ // A property of the config object: `authorize: fn`, `authorize,` (shorthand) or `authorize(req) {` (method).
3569
+ authorize: /[{,]\s*authorize\s*[:,}(]/.test(code),
3570
+ localPreview: flag("localPreview"),
3571
+ dangerouslyAllowUnauthenticated: flag("dangerouslyAllowUnauthenticated"),
3572
+ dangerouslyAllowAllPaths: flag("dangerouslyAllowAllPaths"),
3573
+ prefixes,
3574
+ generated: hasPrefixRegion(source)
3575
+ };
3576
+ }
3577
+
3578
+ // src/blocks/screens.ts
3579
+ var import_node_fs3 = __toESM(require("fs"));
3580
+ var import_node_path4 = __toESM(require("path"));
3581
+
3582
+ // src/blocks/compose.ts
3583
+ var import_node_fs2 = __toESM(require("fs"));
3584
+ var import_node_path3 = __toESM(require("path"));
3585
+
3586
+ // src/blocks/registry.ts
3587
+ var import_node_fs = __toESM(require("fs"));
3588
+ var import_node_path2 = __toESM(require("path"));
3589
+ function arr(v) {
3590
+ return Array.isArray(v) ? v : [];
3591
+ }
3592
+ function readJson(file) {
3593
+ try {
3594
+ const parsed = JSON.parse(import_node_fs.default.readFileSync(file, "utf8"));
3595
+ return parsed && typeof parsed === "object" ? parsed : null;
3596
+ } catch {
3597
+ return null;
3598
+ }
3599
+ }
3600
+ function toBlock(raw, dir, root) {
3601
+ if (typeof raw.id !== "string" || !raw.id) return null;
3602
+ return {
3603
+ id: raw.id,
3604
+ title: typeof raw.title === "string" ? raw.title : raw.id,
3605
+ summary: typeof raw.summary === "string" ? raw.summary : "",
3606
+ features: arr(raw.features),
3607
+ dependsOn: arr(raw.dependsOn),
3608
+ operations: arr(raw.operations),
3609
+ components: arr(raw.components),
3610
+ ...Array.isArray(raw.screens) ? { screens: arr(raw.screens) } : {},
3611
+ hooks: arr(raw.hooks),
3612
+ lib: arr(raw.lib),
3613
+ server: arr(raw.server),
3614
+ recipes: arr(raw.recipes),
3615
+ prerequisites: arr(raw.prerequisites),
3616
+ scopes: arr(raw.scopes),
3617
+ defaults: arr(raw.defaults),
3618
+ peerDependencies: raw.peerDependencies ?? {},
3619
+ dir,
3620
+ root
3621
+ };
3622
+ }
3623
+ function loadRegistry(blocksRoot) {
3624
+ const blocks = [];
3625
+ if (import_node_fs.default.existsSync(blocksRoot)) {
3626
+ for (const entry of import_node_fs.default.readdirSync(blocksRoot, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
3627
+ if (!entry.isDirectory()) continue;
3628
+ const root = import_node_path2.default.join(blocksRoot, entry.name);
3629
+ const raw = readJson(import_node_path2.default.join(root, "block.json"));
3630
+ const block = raw && toBlock(raw, entry.name, root);
3631
+ if (block) blocks.push(block);
3632
+ }
3633
+ }
3634
+ const stacks = [];
3635
+ const stacksDir = import_node_path2.default.join(import_node_path2.default.dirname(blocksRoot), "stacks");
3636
+ if (import_node_fs.default.existsSync(stacksDir)) {
3637
+ for (const file of import_node_fs.default.readdirSync(stacksDir).filter((f) => f.endsWith(".json")).sort()) {
3638
+ const raw = readJson(import_node_path2.default.join(stacksDir, file));
3639
+ if (!raw || typeof raw.id !== "string") continue;
3640
+ stacks.push({
3641
+ id: raw.id,
3642
+ title: typeof raw.title === "string" ? raw.title : raw.id,
3643
+ summary: typeof raw.summary === "string" ? raw.summary : "",
3644
+ status: raw.status === "planned" ? "planned" : "available",
3645
+ blocks: arr(raw.blocks),
3646
+ nav: arr(raw.nav),
3647
+ ...typeof raw.home === "string" ? { home: raw.home } : {}
3648
+ });
3649
+ }
3650
+ }
3651
+ return { blocks, stacks, stacksRoot: stacksDir };
3652
+ }
3653
+ function findBlock(reg, id) {
3654
+ return reg.blocks.find((b) => b.id === id);
3655
+ }
3656
+ function resolveWithDeps(reg, ids) {
3657
+ const ordered = [];
3658
+ const done = /* @__PURE__ */ new Set();
3659
+ const visiting = [];
3660
+ const visit = (id) => {
3661
+ if (done.has(id)) return;
3662
+ if (visiting.includes(id)) throw new Error(`dependency cycle: ${[...visiting, id].join(" -> ")}`);
3663
+ const block = findBlock(reg, id);
3664
+ if (!block) {
3665
+ const from = visiting.length ? ` (required by ${visiting[visiting.length - 1]})` : "";
3666
+ throw new Error(`unknown block "${id}"${from}. Run \`graph8-sdk list\` to see the available blocks.`);
3667
+ }
3668
+ visiting.push(id);
3669
+ for (const dep of block.dependsOn) visit(dep);
3670
+ visiting.pop();
3671
+ done.add(id);
3672
+ ordered.push(block);
3673
+ };
3674
+ ids.forEach(visit);
3675
+ return ordered;
3676
+ }
3677
+ function searchBlocks(reg, text) {
3678
+ const needle = text.trim().toLowerCase();
3679
+ if (!needle) return [];
3680
+ return reg.blocks.filter(
3681
+ (b) => [b.id, b.title, b.summary, ...b.features, ...b.components.map((c) => c.name)].some((s) => s.toLowerCase().includes(needle))
3682
+ );
3683
+ }
3684
+ function searchStacks(reg, text) {
3685
+ const needle = text.trim().toLowerCase();
3686
+ if (!needle) return [];
3687
+ return reg.stacks.filter((s) => [s.id, s.title, s.summary].some((x) => x.toLowerCase().includes(needle)));
3688
+ }
3689
+ function screenNames(b) {
3690
+ return b.components.map((c) => c.name).filter((n) => n.endsWith("Screen"));
3691
+ }
3692
+ function blockSummary(b) {
3693
+ const screens = [.../* @__PURE__ */ new Set([...screenNames(b), ...(b.screens ?? []).map((x) => x.component)])];
3694
+ return {
3695
+ id: b.id,
3696
+ title: b.title,
3697
+ summary: b.summary,
3698
+ dependsOn: b.dependsOn,
3699
+ components: b.components.map((c) => c.name),
3700
+ ...screens.length ? { screens } : {}
3701
+ };
3702
+ }
3703
+ function stackStatus(reg, s) {
3704
+ const missing = s.blocks.filter((id) => !findBlock(reg, id));
3705
+ return { status: missing.length ? "planned" : "available", missing };
3706
+ }
3707
+ function stackSummary(s, reg) {
3708
+ const base = { id: s.id, title: s.title, summary: s.summary, blocks: s.blocks };
3709
+ if (!reg) return base;
3710
+ const { status, missing } = stackStatus(reg, s);
3711
+ return { ...base, status, ...missing.length ? { missingBlocks: missing } : {} };
3712
+ }
3713
+ function catalogSection(reg) {
3714
+ if (!reg.blocks.length && !reg.stacks.length) return void 0;
3715
+ const lines = [
3716
+ "### Build an app from graph8 blocks",
3717
+ "",
3718
+ "To build an app: `npx @graph8/sdk create --blocks <ids> <dir>` (for example `--blocks crm,deals,inbox`). Dependencies come with it, and every block's",
3719
+ "finished screens become routes plus a sidebar entry. Use a stack below instead when one matches the product you are building.",
3720
+ "",
3721
+ "### graph8 blocks",
3722
+ "",
3723
+ "Ready-made, copy-in UI + hooks per graph8 feature. Pick from this list instead of reading every README:",
3724
+ ""
3725
+ ];
3726
+ for (const b of reg.blocks) {
3727
+ const deps = b.dependsOn.length ? ` (needs ${b.dependsOn.join(", ")})` : "";
3728
+ const screens = [.../* @__PURE__ */ new Set([...screenNames(b), ...(b.screens ?? []).map((x) => x.component)])];
3729
+ lines.push(`- block \`${b.id}\`: ${b.summary}${deps}${screens.length ? ` Screens (start here): ${screens.join(", ")}.` : ""}`);
3730
+ }
3731
+ for (const s of reg.stacks) {
3732
+ const { missing } = stackStatus(reg, s);
3733
+ const planned = missing.length ? ` [planned, not creatable yet: missing blocks ${missing.join(", ")}]` : "";
3734
+ lines.push(`- stack \`${s.id}\`: ${s.summary} (blocks: ${s.blocks.join(", ")})${planned}`);
3735
+ }
3736
+ lines.push(
3737
+ "",
3738
+ "Add one: `npx @graph8/sdk add <block>[/<Component>]` (dependencies and the `foundation` block come with it). Find one:",
3739
+ "`npx @graph8/sdk list` / `search <text>`. Check setup: `npx @graph8/sdk doctor --blocks a,b`."
3740
+ );
3741
+ if (reg.stacks.some((s) => !stackStatus(reg, s).missing.length)) lines.push("Start a whole app from an available stack: `npx @graph8/sdk create <stack> <dir>` (Next.js + the stack's blocks wired into a sidebar).");
3742
+ return lines.join("\n");
3743
+ }
3744
+
3745
+ // src/blocks/compose.ts
3746
+ var BLOCK_ICONS = {
3747
+ agents: "Bot",
3748
+ analytics: "ChartBar",
3749
+ appointments: "CalendarDays",
3750
+ campaigns: "Rocket",
3751
+ coach: "GraduationCap",
3752
+ copilot: "MessageCircle",
3753
+ crm: "Users",
3754
+ deals: "Handshake",
3755
+ dialer: "Phone",
3756
+ enrichment: "Sparkles",
3757
+ forms: "ClipboardList",
3758
+ inbox: "Inbox",
3759
+ integrations: "Plug",
3760
+ intent: "Target",
3761
+ lifecycle: "Repeat",
3762
+ lists: "ListChecks",
3763
+ mailboxes: "Mail",
3764
+ newsletter: "Newspaper",
3765
+ nurture: "Sprout",
3766
+ objects: "Boxes",
3767
+ pages: "LayoutTemplate",
3768
+ quotes: "FileText",
3769
+ radar: "Radar",
3770
+ search: "Search",
3771
+ sequencer: "Send",
3772
+ "social-ads": "Megaphone",
3773
+ visitors: "Eye",
3774
+ webchat: "MessageSquare",
3775
+ workbench: "Table2",
3776
+ workflows: "Workflow"
3777
+ };
3778
+ var FALLBACK_ICON = "LayoutGrid";
3779
+ var words = (pascal) => pascal.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").split(" ").filter(Boolean);
3780
+ var kebab = (pascal) => words(pascal).map((w) => w.toLowerCase()).join("-");
3781
+ var sentence = (pascal) => {
3782
+ const w = words(pascal).map((x) => x.toLowerCase());
3783
+ return w.length ? w[0][0].toUpperCase() + w[0].slice(1) + (w.length > 1 ? ` ${w.slice(1).join(" ")}` : "") : pascal;
3784
+ };
3785
+ function matching(s, open, pair) {
3786
+ let depth = 0;
3787
+ for (let i = open; i < s.length; i++) {
3788
+ if (s[i] === pair[0]) depth++;
3789
+ else if (s[i] === pair[1] && --depth === 0) return i;
3790
+ }
3791
+ return -1;
3792
+ }
3793
+ function requiredMembers(body) {
3794
+ const out = [];
3795
+ let depth = 0;
3796
+ let cur = "";
3797
+ const flush = () => {
3798
+ const m = /^\s*(?:readonly\s+)?([A-Za-z_$][\w$]*)\s*(\?)?\s*:/.exec(cur.replace(/\/\*[\s\S]*?\*\/|\/\/[^\n]*/g, ""));
3799
+ if (m && !m[2]) out.push(m[1]);
3800
+ cur = "";
3801
+ };
3802
+ for (const ch of body) {
3803
+ if ("{[(<".includes(ch)) depth++;
3804
+ else if ("}])>".includes(ch) && !(ch === ">" && cur.endsWith("="))) depth--;
3805
+ if (depth === 0 && (ch === ";" || ch === "," || ch === "\n")) flush();
3806
+ else cur += ch;
3807
+ }
3808
+ flush();
3809
+ return out;
3810
+ }
3811
+ function typeBody(source, typeName) {
3812
+ const m = new RegExp(`(?:interface\\s+${typeName}\\b[^{=]*|type\\s+${typeName}\\s*=\\s*)\\{`).exec(source);
3813
+ if (!m) return null;
3814
+ const open = m.index + m[0].length - 1;
3815
+ const close = matching(source, open, ["{", "}"]);
3816
+ return close < 0 ? null : source.slice(open + 1, close);
3817
+ }
3818
+ function takesNoRequiredProps(source, name) {
3819
+ const m = new RegExp(`export\\s+(?:default\\s+)?(?:async\\s+)?function\\s+${name}\\s*(?:<[^>]*>)?\\(|export\\s+const\\s+${name}\\s*(?::[^=]+)?=\\s*(?:async\\s*)?(?:<[^>]*>)?\\(`).exec(source);
3820
+ if (!m) return false;
3821
+ const open = m.index + m[0].length - 1;
3822
+ const close = matching(source, open, ["(", ")"]);
3823
+ if (close < 0) return false;
3824
+ const params = source.slice(open + 1, close).trim();
3825
+ if (!params) return true;
3826
+ if (/=\s*\{\s*\}\s*$/.test(params)) return true;
3827
+ const colon = params.indexOf(":");
3828
+ const type = (colon < 0 ? "" : params.slice(colon + 1)).trim();
3829
+ if (!type) return false;
3830
+ if (type.startsWith("{")) {
3831
+ const close2 = matching(type, 0, ["{", "}"]);
3832
+ return close2 > 0 && requiredMembers(type.slice(1, close2)).length === 0;
3833
+ }
3834
+ const named = /^([A-Za-z_$][\w$]*)$/.exec(type);
3835
+ const body = named ? typeBody(source, named[1]) : null;
3836
+ return body !== null && requiredMembers(body).length === 0;
3837
+ }
3838
+ function readSource(block, file) {
3839
+ try {
3840
+ return import_node_fs2.default.readFileSync(import_node_path3.default.join(block.root, file), "utf8");
3841
+ } catch {
3842
+ return "";
3843
+ }
3844
+ }
3845
+ function screensOf(block) {
3846
+ const base = BLOCK_ICONS[block.id] ?? FALLBACK_ICON;
3847
+ const have = new Set(block.components.map((c) => c.name));
3848
+ const make = (component, p, label, extra = {}, fallback2 = false) => ({
3849
+ block: block.id,
3850
+ component,
3851
+ path: p,
3852
+ label,
3853
+ icon: extra.icon ?? base,
3854
+ group: extra.group ?? block.title,
3855
+ ...extra.description ? { description: extra.description } : {},
3856
+ fallback: fallback2
3857
+ });
3858
+ if (block.screens?.length) {
3859
+ const declared = block.screens.filter((s) => have.has(s.component));
3860
+ return declared.map((s, i) => ({ s, i })).sort((a, b) => (a.s.order ?? 1e6) - (b.s.order ?? 1e6) || a.i - b.i).map(({ s }) => make(s.component, s.path, s.label, s));
3861
+ }
3862
+ const derived = block.components.map((c) => c.name).filter((n) => n.endsWith("Screen") && n.length > "Screen".length);
3863
+ if (derived.length) return derived.map((n) => make(n, `/${kebab(n.slice(0, -"Screen".length))}`, sentence(n.slice(0, -"Screen".length))));
3864
+ const fallback = block.components.find((c) => takesNoRequiredProps(readSource(block, c.file), c.name));
3865
+ return fallback ? [make(fallback.name, `/${block.id}`, block.title, {}, true)] : [];
3866
+ }
3867
+ var hasRealScreen = (block) => screensOf(block).some((s) => !s.fallback);
3868
+ function uniquePaths(screens) {
3869
+ const taken = /* @__PURE__ */ new Set();
3870
+ return screens.map((s) => {
3871
+ let p = s.path;
3872
+ const prefixed = `/${s.block}${s.path === "/" ? "" : s.path}`;
3873
+ if (taken.has(p)) p = prefixed;
3874
+ for (let n = 2; taken.has(p); n++) p = `${prefixed}-${n}`;
3875
+ taken.add(p);
3876
+ return { ...s, path: p };
3877
+ });
3878
+ }
3879
+ function composeApp(reg, ids, opts = {}) {
3880
+ const requested = [...new Set(ids)];
3881
+ const closure = resolveWithDeps(reg, requested);
3882
+ const depOnly = closure.filter((b) => !requested.includes(b.id) && b.id !== "foundation");
3883
+ const warnings = [];
3884
+ const chosen = [];
3885
+ for (const id of requested) {
3886
+ const block = findBlock(reg, id);
3887
+ if (id === "foundation") continue;
3888
+ const screens2 = screensOf(block);
3889
+ if (!screens2.length) warnings.push(`block "${id}" has no screen and no component that renders without props: it adds no page (wire its components yourself).`);
3890
+ else if (screens2[0].fallback) warnings.push(`block "${id}" has no finished Screen yet: its page is the plain ${screens2[0].component} component.`);
3891
+ chosen.push(...screens2);
3892
+ }
3893
+ if (!opts.noDepsScreens) {
3894
+ for (const block of depOnly) chosen.push(...screensOf(block).filter((s) => opts.withDepsScreens || !s.fallback));
3895
+ }
3896
+ const screens = uniquePaths(chosen);
3897
+ const nav = screens.map((s) => ({
3898
+ label: s.label,
3899
+ path: s.path,
3900
+ block: s.block,
3901
+ component: s.component,
3902
+ icon: s.icon,
3903
+ group: s.group,
3904
+ ...s.description ? { description: s.description } : {}
3905
+ }));
3906
+ const home = screens.find((s) => s.block === "analytics" && !s.fallback)?.path ?? screens[0]?.path;
3907
+ const stack = {
3908
+ id: "custom",
3909
+ title: opts.title?.trim() || "graph8 app",
3910
+ summary: `Custom app: ${requested.join(", ")}`,
3911
+ status: "available",
3912
+ blocks: closure.map((b) => b.id),
3913
+ nav,
3914
+ ...home ? { home } : {}
3915
+ };
3916
+ return { stack, screens, warnings };
3917
+ }
3918
+
3919
+ // src/blocks/navfile.ts
3920
+ function render(template, vars2) {
3921
+ return template.replace(/\{\{(\w+)\}\}/g, (_m, key) => {
3922
+ if (!(key in vars2)) throw new Error(`template placeholder {{${key}}} has no value`);
3923
+ return vars2[key];
3924
+ });
3925
+ }
3926
+ function pageFileFor(navPath) {
3927
+ const segs = navPath.split("/").filter(Boolean);
3928
+ return ["app", ...segs, "page.tsx"].join("/");
3929
+ }
3930
+ function componentImportPath(block, file) {
3931
+ const rel = installedRel(file) ?? file;
3932
+ return `@/components/g8/${block.dir}/${rel.replace(/\.(tsx|ts|jsx|js)$/, "")}`;
3933
+ }
3934
+ function navUsesExtras(nav) {
3935
+ return nav.some((n) => n.icon || n.group || n.description);
3936
+ }
3937
+ function navPartsFor(nav) {
3938
+ if (!navUsesExtras(nav)) {
3939
+ return { navImports: "", navType: "{ label: string; path: string }[]", navJson: JSON.stringify(nav.map((n) => ({ label: n.label, path: n.path })), null, 2) };
3940
+ }
3941
+ const icons = [...new Set(nav.flatMap((n) => n.icon ? [n.icon] : []))].sort();
3942
+ const named = [...icons, "type LucideIcon"].join(", ");
3943
+ const items = nav.map((n) => ({
3944
+ label: n.label,
3945
+ path: n.path,
3946
+ ...n.icon ? { icon: `__ICON__${n.icon}` } : {},
3947
+ ...n.group ? { group: n.group } : {},
3948
+ ...n.description ? { description: n.description } : {}
3949
+ }));
3950
+ return {
3951
+ navImports: [
3952
+ `import { ${named} } from "lucide-react";`,
3953
+ "",
3954
+ "export type NavItem = { label: string; path: string; icon?: LucideIcon; group?: string; description?: string };",
3955
+ "",
3956
+ ""
3957
+ ].join("\n"),
3958
+ navType: "NavItem[]",
3959
+ navJson: JSON.stringify(items, null, 2).replace(/"__ICON__(\w+)"/g, "$1")
3960
+ };
3961
+ }
3962
+ function navParts(stack) {
3963
+ return navPartsFor(stack.nav);
3964
+ }
3965
+
3966
+ // src/blocks/screens.ts
3967
+ var NAV_FILES = ["lib/nav.ts", "src/lib/nav.ts"];
3968
+ function parseNavFile(text) {
3969
+ const decl = /export const nav\s*:[^=]*=\s*(\[[\s\S]*\]);?\s*$/.exec(text);
3970
+ if (!decl) return null;
3971
+ const first = /^(?:import |export )/m.exec(text);
3972
+ try {
3973
+ const quoted2 = decl[1].replace(/("icon":\s*)([A-Za-z_][A-Za-z0-9_]*)/g, '$1"$2"');
3974
+ const items = JSON.parse(quoted2);
3975
+ if (!Array.isArray(items) || items.some((i) => typeof i?.path !== "string" || typeof i?.label !== "string")) return null;
3976
+ return { header: first ? text.slice(0, first.index) : "", items };
3977
+ } catch {
3978
+ return null;
3979
+ }
3980
+ }
3981
+ function toNavEntry(s) {
3982
+ return { label: s.label, path: s.path, icon: s.icon, group: s.group, ...s.description ? { description: s.description } : {} };
3983
+ }
3984
+ function candidates(blocks) {
3985
+ const main = blocks[blocks.length - 1];
3986
+ return [main, ...blocks.filter((b) => b !== main)].filter((b) => b.id !== "foundation").flatMap((b) => screensOf(b).filter((s) => b === main || !s.fallback));
3987
+ }
3988
+ function importAlias(target) {
3989
+ const t = target.replace(/^src\//, "");
3990
+ return `@/${t}`;
3991
+ }
3992
+ function planScreenWiring(input) {
3993
+ const navRel = NAV_FILES.find((f) => import_node_fs3.default.existsSync(import_node_path4.default.join(input.root, f)));
3994
+ const none = { files: [], notes: [], screens: [], usesIcons: false };
3995
+ if (!navRel) return null;
3996
+ const navText = import_node_fs3.default.readFileSync(import_node_path4.default.join(input.root, navRel), "utf8");
3997
+ const parsed = parseNavFile(navText);
3998
+ const useSrc = navRel.startsWith("src/");
3999
+ const appRel = (rel) => useSrc ? `src/${rel}` : rel;
4000
+ const pageTpl = import_node_fs3.default.readFileSync(import_node_path4.default.join(input.templatesRoot, "next-app", "_pages", "route-page.tsx.tmpl"), "utf8");
4001
+ const items = parsed ? [...parsed.items] : [];
4002
+ const taken = /* @__PURE__ */ new Set([...items.map((i) => i.path), ...input.recorded.map((r) => r.path)]);
4003
+ const files = [];
4004
+ const notes = [];
4005
+ const out = { ...none, screens: [] };
4006
+ const manual = [];
4007
+ for (const s of candidates(input.blocks)) {
4008
+ if (input.recorded.some((r) => r.block === s.block && r.component === s.component)) continue;
4009
+ const block = input.blocks.find((b) => b.id === s.block);
4010
+ const comp = block.components.find((c) => c.name === s.component);
4011
+ if (!comp) continue;
4012
+ const prefixed = `/${s.block}${s.path === "/" ? "" : s.path}`;
4013
+ const importPath = componentImportPath(block, comp.file).replace("@/components/g8", importAlias(input.target));
4014
+ const content = render(pageTpl, {
4015
+ stackId: input.stackId,
4016
+ block: block.id,
4017
+ blockDir: `${input.target}/${block.dir}`,
4018
+ component: s.component,
4019
+ importPath,
4020
+ propsJson: "{}"
4021
+ });
4022
+ const handWritten = (x) => {
4023
+ const file = import_node_path4.default.join(input.root, appRel(pageFileFor(x)));
4024
+ return import_node_fs3.default.existsSync(file) && import_node_fs3.default.readFileSync(file, "utf8") !== content;
4025
+ };
4026
+ const isTaken = (x) => taken.has(x) || handWritten(x);
4027
+ let p = s.path;
4028
+ if (isTaken(p)) p = prefixed;
4029
+ for (let n = 2; isTaken(p); n++) p = `${prefixed}-${n}`;
4030
+ if (p !== s.path && !taken.has(s.path) && handWritten(s.path)) notes.push(`kept your edited ${appRel(pageFileFor(s.path))} (not generated by graph8); wiring ${p} instead`);
4031
+ const screen = { ...s, path: p };
4032
+ taken.add(p);
4033
+ const rel = appRel(pageFileFor(p));
4034
+ const abs = import_node_path4.default.join(input.root, rel);
4035
+ if (import_node_fs3.default.existsSync(abs)) {
4036
+ files.push({ rel, content, action: "unchanged" });
4037
+ } else files.push({ rel, content, action: "create" });
4038
+ out.screens.push({ block: s.block, component: s.component, path: p });
4039
+ (parsed ? items : manual).push(toNavEntry(screen));
4040
+ }
4041
+ if (!out.screens.length) return { ...out, files, notes };
4042
+ if (parsed) {
4043
+ const { navImports, navType, navJson } = navPartsFor(items);
4044
+ const next = `${parsed.header}${navImports}export const nav: ${navType} = ${navJson};
4045
+ `;
4046
+ files.push({ rel: navRel, content: next, action: next === navText ? "unchanged" : "update" });
4047
+ out.usesIcons = items.some((i) => i.icon);
4048
+ } else {
4049
+ notes.push(`could not update ${navRel} (it no longer has the generated shape). Add these entries yourself:`);
4050
+ for (const m of manual) notes.push(` ${JSON.stringify(m)}`);
4051
+ }
4052
+ return { ...out, files, notes };
4053
+ }
4054
+
4055
+ // src/blocks/add.ts
4056
+ var ADD_USAGE = `Usage: npx @graph8/sdk add <block>[/<Component>] [options]
4057
+
4058
+ Copy a graph8 block (components, hooks, recipes) into your app. The block's
4059
+ dependencies (always including \`foundation\`) are added first.
4060
+ --dir <path> project root (default: current directory)
4061
+ --target <path> where to copy, relative to --dir (default: components/g8)
4062
+ --dry-run print what would change, write and install nothing
4063
+ --force overwrite files that differ from the template
4064
+ --no-install do not install missing peer dependencies
4065
+ --routes also add the block's screens as routes and sidebar entries (lib/nav.ts, plus the
4066
+ lucide-react icon dependency). Automatic in apps made with \`create --blocks\`; off by
4067
+ default in apps made from a named stack (create apollo, starter, ...), where running it once opts the
4068
+ app in, so later add calls wire routes without the flag
4069
+ Existing files are never overwritten without --force. Installed blocks are
4070
+ recorded in graph8.json so re-runs do nothing.`;
4071
+ function parseAddArgs(argv) {
4072
+ const o = { spec: "", dir: ".", dryRun: false, force: false, install: true };
4073
+ for (let i = 0; i < argv.length; i++) {
4074
+ const arg = argv[i];
4075
+ const [flag, inline] = arg.startsWith("--") && arg.includes("=") ? [arg.slice(0, arg.indexOf("=")), arg.slice(arg.indexOf("=") + 1)] : [arg, void 0];
4076
+ const value = () => {
4077
+ const v = inline ?? argv[++i];
4078
+ if (v === void 0 || v.startsWith("--")) throw new Error(`${flag} needs a value
4079
+
4080
+ ${ADD_USAGE}`);
4081
+ return v;
4082
+ };
4083
+ if (flag === "--dir") o.dir = value();
4084
+ else if (flag === "--target") o.target = value();
4085
+ else if (arg === "--dry-run") o.dryRun = true;
4086
+ else if (arg === "--force") o.force = true;
4087
+ else if (arg === "--no-install") o.install = false;
4088
+ else if (arg === "--routes") o.routes = true;
4089
+ else if (arg.startsWith("-")) throw new Error(`unknown option ${arg}
4090
+
4091
+ ${ADD_USAGE}`);
4092
+ else if (!o.spec) o.spec = arg;
4093
+ else throw new Error(`unexpected argument ${arg}
4094
+
4095
+ ${ADD_USAGE}`);
4096
+ }
4097
+ if (!o.spec) throw new Error(`add needs a block name
4098
+
4099
+ ${ADD_USAGE}`);
4100
+ return o;
4101
+ }
4102
+ function detectPackageManager(root) {
4103
+ const has = (f) => import_node_fs4.default.existsSync(import_node_path5.default.join(root, f));
4104
+ if (has("pnpm-lock.yaml")) return "pnpm";
4105
+ if (has("yarn.lock")) return "yarn";
4106
+ if (has("bun.lockb") || has("bun.lock")) return "bun";
4107
+ return "npm";
4108
+ }
4109
+ function installCommand(pm, packages) {
4110
+ const verb = pm === "npm" ? "install" : "add";
4111
+ return { cmd: pm, args: [verb, ...packages] };
4112
+ }
4113
+ var sha = (s) => (0, import_node_crypto.createHash)("sha256").update(s).digest("hex");
4114
+ function appTargeted(block) {
4115
+ const m = /* @__PURE__ */ new Map();
4116
+ for (const f of block.server) if (f.target && !f.target.startsWith("g8/")) m.set(posix(f.file), f.target);
4117
+ return m;
4118
+ }
4119
+ function listFiles(dir, base = dir) {
4120
+ const out = [];
4121
+ for (const e of import_node_fs4.default.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
4122
+ const full = import_node_path5.default.join(dir, e.name);
4123
+ if (e.isDirectory()) out.push(...listFiles(full, base));
4124
+ else out.push(posix(import_node_path5.default.relative(base, full)));
4125
+ }
4126
+ return out;
4127
+ }
4128
+ var IMPORT_RE = /(\bfrom\s*|\bimport\s*\(?\s*|\brequire\s*\(\s*)(["'])([^"'\n]+)\2/g;
4129
+ function relSpec(fromDir, toFile) {
4130
+ const noExt = toFile;
4131
+ let r = posix(import_node_path5.default.relative(fromDir, noExt));
4132
+ if (!r.startsWith(".")) r = `./${r}`;
4133
+ return r;
4134
+ }
4135
+ function g8AliasTarget(spec, ctx) {
4136
+ const [seg, ...rest] = spec.split("/");
4137
+ const reserved = ["components", "hooks", "lib", "server", "recipes"].includes(seg);
4138
+ const owner = rest.length && !reserved ? ctx.reg.blocks.find((b) => b.id === seg || b.dir === seg) : void 0;
4139
+ const block = owner ?? ctx.reg.blocks.find((b) => b.id === "foundation");
4140
+ const sub = owner ? rest.join("/") : spec;
4141
+ return import_node_path5.default.join(ctx.targetAbs, block?.dir ?? "_foundation", installedRel(sub) ?? sub);
4142
+ }
4143
+ function rewriteImports(content, ctx) {
4144
+ return content.replace(IMPORT_RE, (whole, pre, q, spec) => {
4145
+ if (spec.startsWith("@/g8/")) {
4146
+ return `${pre}${q}${relSpec(ctx.destDir, g8AliasTarget(spec.slice("@/g8/".length), ctx))}${q}`;
4147
+ }
4148
+ if (spec.startsWith(".") && ctx.block && ctx.srcFile) {
4149
+ const abs = import_node_path5.default.resolve(import_node_path5.default.dirname(ctx.srcFile), spec);
4150
+ const relInBlock = import_node_path5.default.relative(ctx.block.root, abs);
4151
+ if (relInBlock.startsWith("..")) return whole;
4152
+ const mapped = installedRel(relInBlock);
4153
+ if (mapped === null) return whole;
4154
+ return `${pre}${q}${relSpec(ctx.destDir, import_node_path5.default.join(ctx.targetAbs, ctx.block.dir, mapped))}${q}`;
4155
+ }
4156
+ return whole;
4157
+ });
4158
+ }
4159
+ function diffSummary(before, after) {
4160
+ const count = (s) => {
4161
+ const m = /* @__PURE__ */ new Map();
4162
+ for (const l of s.split("\n")) m.set(l, (m.get(l) ?? 0) + 1);
4163
+ return m;
4164
+ };
4165
+ const a = count(before);
4166
+ const b = count(after);
4167
+ let removed = 0;
4168
+ let added = 0;
4169
+ for (const [l, n] of a) removed += Math.max(0, n - (b.get(l) ?? 0));
4170
+ for (const [l, n] of b) added += Math.max(0, n - (a.get(l) ?? 0));
4171
+ return `+${added} -${removed} lines vs the template`;
4172
+ }
4173
+ function classify(abs, rel, content, force) {
4174
+ if (!import_node_fs4.default.existsSync(abs)) return { rel, content, action: "create" };
4175
+ const current = import_node_fs4.default.readFileSync(abs, "utf8");
4176
+ if (current === content) return { rel, content, action: "unchanged" };
4177
+ if (force) return { rel, content, action: "update", detail: diffSummary(current, content) };
4178
+ return { rel, content, action: "skip", detail: `exists and differs (${diffSummary(current, content)}); use --force to overwrite` };
4179
+ }
4180
+ function readPkg(root) {
4181
+ try {
4182
+ return JSON.parse(import_node_fs4.default.readFileSync(import_node_path5.default.join(root, "package.json"), "utf8"));
4183
+ } catch {
4184
+ return null;
4185
+ }
4186
+ }
4187
+ function missingPeers(blocks, pkg) {
4188
+ const have = /* @__PURE__ */ new Set([
4189
+ ...Object.keys(pkg.dependencies ?? {}),
4190
+ ...Object.keys(pkg.devDependencies ?? {}),
4191
+ ...Object.keys(pkg.peerDependencies ?? {}),
4192
+ ...Object.keys(pkg.optionalDependencies ?? {})
4193
+ ]);
4194
+ const want = /* @__PURE__ */ new Map();
4195
+ for (const b of blocks) for (const [name, range] of Object.entries(b.peerDependencies)) if (!want.has(name)) want.set(name, range);
4196
+ return [...want].filter(([name]) => !have.has(name)).map(([name, range]) => `${name}@${range}`);
4197
+ }
4198
+ function parseSpec(spec) {
4199
+ const [blockId, component, ...extra] = spec.split("/");
4200
+ if (!blockId || extra.length) throw new Error(`invalid block "${spec}": use <block> or <block>/<Component>`);
4201
+ return { blockId, component: component || void 0 };
4202
+ }
4203
+ function planBlock(block, only, reg, targetAbs, root, force) {
4204
+ const skipComponentFiles = /* @__PURE__ */ new Set();
4205
+ if (only) {
4206
+ for (const c of block.components) if (c.name !== only) skipComponentFiles.add(posix(c.file));
4207
+ const byStem = new Map(block.components.map((c) => [posix(c.file).replace(/\.[tj]sx?$/, ""), posix(c.file)]));
4208
+ const start = block.components.find((c) => c.name === only);
4209
+ const queue = start ? [posix(start.file)] : [];
4210
+ while (queue.length) {
4211
+ const file = queue.pop();
4212
+ const text = import_node_fs4.default.readFileSync(import_node_path5.default.join(block.root, file), "utf8");
4213
+ for (const m of text.matchAll(IMPORT_RE)) {
4214
+ if (!m[3].startsWith(".")) continue;
4215
+ const stem = posix(import_node_path5.default.relative(block.root, import_node_path5.default.resolve(block.root, import_node_path5.default.dirname(file), m[3]))).replace(/\.[tj]sx?$/, "");
4216
+ const sibling = byStem.get(stem);
4217
+ if (sibling && skipComponentFiles.delete(sibling)) queue.push(sibling);
4218
+ }
4219
+ }
4220
+ }
4221
+ const files = [];
4222
+ const toApp = appTargeted(block);
4223
+ for (const rel of listFiles(block.root)) {
4224
+ if (skipComponentFiles.has(rel) || toApp.has(rel)) continue;
4225
+ const mapped = installedRel(rel);
4226
+ if (mapped === null) continue;
4227
+ const destAbs = import_node_path5.default.join(targetAbs, block.dir, mapped);
4228
+ const src = import_node_path5.default.join(block.root, rel);
4229
+ const text = import_node_fs4.default.readFileSync(src, "utf8");
4230
+ const content = rewriteImports(text, { reg, targetAbs, block, srcFile: src, destDir: import_node_path5.default.dirname(destAbs) });
4231
+ files.push(classify(destAbs, posix(import_node_path5.default.relative(root, destAbs)), content, force));
4232
+ }
4233
+ return files;
4234
+ }
4235
+ var PROXY_CONTRACT = [
4236
+ "This is not a Next.js project, so the proxy route was not written. Your server must provide the same contract:",
4237
+ " - handle every method on /api/g8/* (the browser calls these paths)",
4238
+ " - forward to ${G8_API_URL:-https://be.graph8.com}/api/v1/<rest of path>, same method, query and body",
4239
+ " - add the header `Authorization: Bearer $G8_API_KEY` on the server; never send the key to the browser",
4240
+ " - return the upstream status and JSON body unchanged"
4241
+ ];
4242
+ function writeFile(root, f) {
4243
+ const abs = import_node_path5.default.join(root, f.rel);
4244
+ import_node_fs4.default.mkdirSync(import_node_path5.default.dirname(abs), { recursive: true });
4245
+ import_node_fs4.default.writeFileSync(abs, f.content, "utf8");
4246
+ }
4247
+ function readLedger(file) {
4248
+ try {
4249
+ const j = JSON.parse(import_node_fs4.default.readFileSync(file, "utf8"));
4250
+ return j && typeof j === "object" && j.blocks ? j : null;
4251
+ } catch {
4252
+ return null;
4253
+ }
4254
+ }
4255
+ function ledgerContent(prev, target, entries, only, wired) {
4256
+ const blocks = { ...prev?.blocks ?? {} };
4257
+ for (const [id, files] of entries) {
4258
+ const old = blocks[id];
4259
+ const recorded = { ...old?.files ?? {} };
4260
+ for (const f of files) {
4261
+ if (f.action === "skip") continue;
4262
+ recorded[f.rel] = sha(f.content);
4263
+ }
4264
+ const comp = only.get(id);
4265
+ let components;
4266
+ if (!comp) components = "all";
4267
+ else if (old?.components === "all") components = "all";
4268
+ else components = [.../* @__PURE__ */ new Set([...old?.components ?? [], comp])].sort();
4269
+ blocks[id] = { files: Object.fromEntries(Object.entries(recorded).sort(([a], [b]) => a.localeCompare(b))), components };
4270
+ }
4271
+ const sorted = Object.fromEntries(Object.entries(blocks).sort(([a], [b]) => a.localeCompare(b)));
4272
+ const stack = prev?.stack ? { stack: prev.stack } : {};
4273
+ const app = appRecord(prev, wired);
4274
+ return `${JSON.stringify({ sdkVersion: SDK_VERSION, target, ...stack, ...app ? { app } : {}, blocks: sorted }, null, 2)}
4275
+ `;
4276
+ }
4277
+ function appRecord(prev, wired) {
4278
+ if (!wired?.screens.length) return prev?.app;
4279
+ const old = prev?.app ?? {};
4280
+ const blocks = [.../* @__PURE__ */ new Set([...old.blocks ?? [], wired.requested])];
4281
+ return { ...old, blocks, screens: [...old.screens ?? [], ...wired.screens] };
4282
+ }
4283
+ function ensureEnvExample(root, dryRun, out) {
4284
+ const file = import_node_path5.default.join(root, ".env.local.example");
4285
+ const current = import_node_fs4.default.existsSync(file) ? import_node_fs4.default.readFileSync(file, "utf8") : null;
4286
+ if (current !== null && /^G8_API_KEY=/m.test(current)) {
4287
+ out("keep .env.local.example (G8_API_KEY already listed)");
4288
+ return;
4289
+ }
4290
+ out(`${dryRun ? "would " : ""}${current === null ? "create" : "update"} .env.local.example (+ G8_API_KEY=)`);
4291
+ if (dryRun) return;
4292
+ const base = current === null ? "" : current.endsWith("\n") || current === "" ? current : `${current}
4293
+ `;
4294
+ import_node_fs4.default.writeFileSync(file, `${base}G8_API_KEY=
4295
+ `, "utf8");
4296
+ }
4297
+ function runAdd(opts, reg, io, cwd) {
4298
+ const root = import_node_path5.default.resolve(cwd, opts.dir);
4299
+ const pkg = readPkg(root);
4300
+ if (!pkg) {
4301
+ io.err(`no package.json in ${root}. Run this from your app's root, or pass --dir.`);
4302
+ return 1;
4303
+ }
4304
+ let parsed;
4305
+ let blocks;
4306
+ try {
4307
+ parsed = parseSpec(opts.spec);
4308
+ blocks = resolveWithDeps(reg, [parsed.blockId]);
4309
+ const main2 = blocks[blocks.length - 1];
4310
+ if (parsed.component && !main2.components.some((c) => c.name === parsed.component)) {
4311
+ const names = main2.components.map((c) => c.name).join(", ") || "none";
4312
+ throw new Error(`block "${main2.id}" has no component "${parsed.component}" (available: ${names})`);
4313
+ }
4314
+ } catch (e) {
4315
+ io.err(e.message);
4316
+ return 1;
4317
+ }
4318
+ const exists = (p) => import_node_fs4.default.existsSync(import_node_path5.default.join(root, p));
4319
+ const target = opts.target ?? (exists("src") && !exists("components") && !exists("app") ? "src/components/g8" : "components/g8");
4320
+ const targetAbs = import_node_path5.default.resolve(root, target);
4321
+ const isNext = Boolean(pkg.dependencies?.next ?? pkg.devDependencies?.next);
4322
+ const dry = opts.dryRun;
4323
+ const verb = (a) => a === "unchanged" ? "keep" : a === "skip" ? "skip" : dry ? `would ${a}` : a;
4324
+ const entries = /* @__PURE__ */ new Map();
4325
+ const only = /* @__PURE__ */ new Map();
4326
+ const main = blocks[blocks.length - 1];
4327
+ for (const b of blocks) {
4328
+ const comp = b === main ? parsed.component : void 0;
4329
+ only.set(b.id, comp);
4330
+ entries.set(b.id, planBlock(b, comp, reg, targetAbs, root, opts.force));
4331
+ }
4332
+ const all = [...entries.values()].flat();
4333
+ const prevLedger = readLedger(import_node_path5.default.join(root, "graph8.json"));
4334
+ const route = planAppFiles(blocks, reg, targetAbs, root, isNext, opts.force, installedBlocks(reg, prevLedger, blocks));
4335
+ const wiring = opts.wireScreens !== false && !parsed.component && isNext ? screenWiring(blocks, prevLedger, opts.routes === true, reg, root, posix(import_node_path5.default.relative(root, targetAbs)), io) : null;
4336
+ io.out(`${dry ? "Dry run: " : ""}adding ${blocks.map((b) => b.id).join(" + ")} to ${target}`);
4337
+ for (const f of [...all, ...route.files]) io.out(`${verb(f.action)} ${f.rel}${f.detail ? ` (${f.detail})` : ""}`);
4338
+ if (route.note) route.note.forEach((l) => io.out(l));
4339
+ for (const f of wiring?.files ?? []) io.out(`${verb(f.action)} ${f.rel}`);
4340
+ wiring?.notes.forEach((l) => io.out(l));
4341
+ if (!dry) {
4342
+ for (const f of [...all, ...route.files, ...wiring?.files ?? []]) if (f.action === "create" || f.action === "update") writeFile(root, f);
4343
+ }
4344
+ ensureEnvExample(root, dry, io.out);
4345
+ const ledgerPath = import_node_path5.default.join(root, "graph8.json");
4346
+ const next = ledgerContent(prevLedger, posix(import_node_path5.default.relative(root, targetAbs)), entries, only, wiring ? { requested: main.id, screens: wiring.screens } : void 0);
4347
+ const prevText = import_node_fs4.default.existsSync(ledgerPath) ? import_node_fs4.default.readFileSync(ledgerPath, "utf8") : null;
4348
+ if (next !== prevText) {
4349
+ io.out(`${dry ? "would " : ""}${prevText === null ? "create" : "update"} graph8.json`);
4350
+ if (!dry) import_node_fs4.default.writeFileSync(ledgerPath, next, "utf8");
4351
+ } else io.out("keep graph8.json");
4352
+ return installPeers(blocks, pkg, root, opts, io, wiring?.usesIcons ? lucidePeer(io) : []);
4353
+ }
4354
+ function screenWiring(blocks, prev, routes, reg, root, target, io) {
4355
+ if (!prev?.app && !(routes && prev?.stack)) return null;
4356
+ if (!io.templatesRoot) return null;
4357
+ const fresh = blocks.filter((b) => !prev.blocks[b.id]);
4358
+ if (!fresh.length) return null;
4359
+ return planScreenWiring({ reg, root, target, stackId: prev.stack ?? "custom", blocks: fresh, templatesRoot: io.templatesRoot, recorded: prev.app?.screens ?? [] });
4360
+ }
4361
+ function lucidePeer(io) {
4362
+ try {
4363
+ const sdk = JSON.parse(import_node_fs4.default.readFileSync(io.sdkPackageJson ?? "", "utf8"));
4364
+ return [`lucide-react@${sdk.peerDependencies?.["lucide-react"] ?? ">=0.400.0"}`];
4365
+ } catch {
4366
+ return ["lucide-react@>=0.400.0"];
4367
+ }
4368
+ }
4369
+ function installPeers(blocks, pkg, root, opts, io, extra = []) {
4370
+ const have = new Set(missingPeers(blocks, pkg).map((m) => m.slice(0, m.lastIndexOf("@"))));
4371
+ const declared = new Set(Object.keys({ ...pkg.dependencies, ...pkg.devDependencies, ...pkg.peerDependencies, ...pkg.optionalDependencies }));
4372
+ const missing = [...missingPeers(blocks, pkg), ...extra.filter((e) => !declared.has(e.slice(0, e.lastIndexOf("@"))) && !have.has(e.slice(0, e.lastIndexOf("@"))))];
4373
+ if (!missing.length) return 0;
4374
+ const pm = detectPackageManager(root);
4375
+ const { cmd, args } = installCommand(pm, missing);
4376
+ const shown = `${cmd} ${args.join(" ")}`;
4377
+ if (!opts.install || opts.dryRun) {
4378
+ io.out(`${opts.dryRun ? "would run" : "not installing (--no-install). Run"}: ${shown}`);
4379
+ return 0;
4380
+ }
4381
+ io.out(`installing peer dependencies: ${shown}`);
4382
+ const code = io.spawn(cmd, args, root);
4383
+ if (code !== 0) {
4384
+ io.err(`peer dependency install failed (exit ${code}). Run it yourself: ${shown}`);
4385
+ return 1;
4386
+ }
4387
+ return 0;
4388
+ }
4389
+ function appTargetPath(root, target) {
4390
+ const useSrc = import_node_fs4.default.existsSync(import_node_path5.default.join(root, "src", "app")) && !import_node_fs4.default.existsSync(import_node_path5.default.join(root, "app"));
4391
+ return useSrc && target.startsWith("app/") ? `src/${target}` : target;
4392
+ }
4393
+ function installedBlocks(reg, prev, adding) {
4394
+ const ids = /* @__PURE__ */ new Set([...Object.keys(prev?.blocks ?? {}), ...adding.map((b) => b.id)]);
4395
+ return reg.blocks.filter((b) => ids.has(b.id));
4396
+ }
4397
+ function planPrefixedRoute(destAbs, rel, template, prefixes, force) {
4398
+ const fresh = applyPrefixes(template, prefixes) ?? template;
4399
+ if (!import_node_fs4.default.existsSync(destAbs)) return { rel, content: fresh, action: "create", detail: `allowedPrefixes: ${prefixes.length} API areas from the installed blocks` };
4400
+ const current = import_node_fs4.default.readFileSync(destAbs, "utf8");
4401
+ const regenerated = hasPrefixRegion(current) ? applyPrefixes(current, prefixes) : null;
4402
+ if (regenerated !== null && !force) {
4403
+ if (regenerated === current) return { rel, content: current, action: "unchanged" };
4404
+ return { rel, content: regenerated, action: "update", detail: `allowedPrefixes regenerated: ${prefixes.length} API areas` };
4405
+ }
4406
+ const planned = classify(destAbs, rel, fresh, force);
4407
+ if (planned.action === "skip") planned.detail = `${planned.detail}; its allowedPrefixes are yours: \`npx @graph8/sdk doctor --fix-prefixes\` prints the list the blocks need`;
4408
+ return planned;
4409
+ }
4410
+ function planAppFiles(blocks, reg, targetAbs, root, isNext, force, installed = blocks) {
4411
+ const files = [];
4412
+ let skipped = false;
4413
+ for (const b of blocks) {
4414
+ for (const [file, target] of appTargeted(b)) {
4415
+ if (!isNext) {
4416
+ skipped = true;
4417
+ continue;
4418
+ }
4419
+ const destAbs = import_node_path5.default.join(root, appTargetPath(root, target));
4420
+ const src = import_node_path5.default.join(b.root, file);
4421
+ const content = rewriteImports(import_node_fs4.default.readFileSync(src, "utf8"), { reg, targetAbs, destDir: import_node_path5.default.dirname(destAbs) });
4422
+ const rel = posix(import_node_path5.default.relative(root, destAbs));
4423
+ if (hasPrefixRegion(content)) files.push(planPrefixedRoute(destAbs, rel, content, derivePrefixes(installed), force));
4424
+ else files.push(classify(destAbs, rel, content, force));
4425
+ }
4426
+ }
4427
+ return { files, note: skipped ? PROXY_CONTRACT : void 0 };
4428
+ }
4429
+
4430
+ // src/blocks/brand.ts
4431
+ var import_node_fs5 = __toESM(require("fs"));
4432
+ var import_node_path6 = __toESM(require("path"));
4433
+ function normalizeHex(input) {
4434
+ const m = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(input.trim());
4435
+ if (!m) return null;
4436
+ const h = m[1].length === 3 ? [...m[1]].map((c) => c + c).join("") : m[1];
4437
+ return `#${h.toLowerCase()}`;
4438
+ }
4439
+ function hexToRgb(hex) {
4440
+ const h = (normalizeHex(hex) ?? "#000000").slice(1);
4441
+ return [0, 2, 4].map((i) => parseInt(h.slice(i, i + 2), 16));
4442
+ }
4443
+ function rgbToHex([r, g, b]) {
4444
+ return `#${[r, g, b].map((v) => Math.round(Math.min(255, Math.max(0, v))).toString(16).padStart(2, "0")).join("")}`;
4445
+ }
4446
+ function luminance(hex) {
4447
+ const [r, g, b] = hexToRgb(hex).map((v) => {
4448
+ const c = v / 255;
4449
+ return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
4450
+ });
4451
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
4452
+ }
4453
+ function contrast(a, b) {
4454
+ const [hi, lo] = [luminance(a), luminance(b)].sort((x, y) => y - x);
4455
+ return (hi + 0.05) / (lo + 0.05);
4456
+ }
4457
+ function mix(hex, toward, amount) {
4458
+ const a = hexToRgb(hex);
4459
+ const b = hexToRgb(toward);
4460
+ return rgbToHex([0, 1, 2].map((i) => a[i] + (b[i] - a[i]) * amount));
4461
+ }
4462
+ var CANVAS = { light: "#ffffff", dark: "#111110" };
4463
+ var INK = { light: "#16140f", dark: "#f2efe9" };
4464
+ var AA_TEXT = 4.5;
4465
+ var AA_UI = 3;
4466
+ var STEPS = [
4467
+ ["50", "#ffffff", 0.94],
4468
+ ["100", "#ffffff", 0.86],
4469
+ ["200", "#ffffff", 0.7],
4470
+ ["300", "#ffffff", 0.5],
4471
+ ["400", "#ffffff", 0.25],
4472
+ ["600", "#000000", 0.14],
4473
+ ["700", "#000000", 0.28],
4474
+ ["800", "#000000", 0.42],
4475
+ ["900", "#000000", 0.56]
4476
+ ];
4477
+ function scaleOf(brand) {
4478
+ const out = {};
4479
+ for (const [k, to, amt] of STEPS) out[k] = mix(brand, to, amt);
4480
+ out["500"] = brand;
4481
+ return Object.fromEntries(Object.entries(out).sort(([a], [b]) => Number(a) - Number(b)));
4482
+ }
4483
+ function bestForeground(bg, ink) {
4484
+ return [ink, "#ffffff", "#000000"].sort((a, b) => contrast(b, bg) - contrast(a, bg))[0];
4485
+ }
4486
+ function swatch(input, mode, explicit, notes) {
4487
+ let brand = input;
4488
+ const canvas = CANVAS[mode];
4489
+ if (mode === "dark" && !explicit) {
4490
+ for (let i = 1; i <= 20 && contrast(brand, canvas) < AA_UI; i++) brand = mix(input, "#ffffff", i * 0.05);
4491
+ if (brand !== input) notes.push(`dark mode: lightened ${input} to ${brand} so it stands out from the dark background (${contrast(brand, canvas).toFixed(2)}:1)`);
4492
+ }
4493
+ let fg = bestForeground(brand, INK[mode]);
4494
+ for (let i = 1; i <= 20 && contrast(fg, brand) < AA_TEXT; i++) {
4495
+ const before = brand;
4496
+ brand = mix(brand, luminance(fg) > 0.5 ? "#000000" : "#ffffff", 0.05);
4497
+ fg = bestForeground(brand, INK[mode]);
4498
+ if (i === 1) notes.push(`${mode} mode: adjusted ${before} for readable button text`);
4499
+ }
4500
+ const canvasContrast = contrast(brand, canvas);
4501
+ if (canvasContrast < AA_UI) {
4502
+ notes.push(
4503
+ `${mode} mode: ${brand} is faint against the ${mode} background (${canvasContrast.toFixed(2)}:1, under ${AA_UI}:1); button text stays readable, but consider a deeper colour${mode === "dark" ? " or --dark-brand-color" : ""}`
4504
+ );
4505
+ }
4506
+ return { brand, foreground: fg, scale: scaleOf(brand), canvasContrast };
4507
+ }
4508
+ function brandTheme(color, darkColor) {
4509
+ const light = normalizeHex(color);
4510
+ if (!light) throw new Error(`--brand-color must be a hex colour like #0f62fe (got "${color}")`);
4511
+ const dark = darkColor === void 0 ? light : normalizeHex(darkColor);
4512
+ if (!dark) throw new Error(`--dark-brand-color must be a hex colour like #78a9ff (got "${darkColor}")`);
4513
+ const notes = [];
4514
+ return { light: swatch(light, "light", true, notes), dark: swatch(dark, "dark", darkColor !== void 0, notes), notes };
4515
+ }
4516
+ var CSS_START = "/* graph8:brand:start */";
4517
+ var CSS_END = "/* graph8:brand:end */";
4518
+ function vars(s, indent) {
4519
+ const lines = [
4520
+ `--brand: ${s.brand};`,
4521
+ `--brand-foreground: ${s.foreground};`,
4522
+ ...Object.entries(s.scale).map(([k, v]) => `--brand-${k}: ${v};`),
4523
+ "--primary: var(--brand);",
4524
+ "--primary-foreground: var(--brand-foreground);",
4525
+ "--ring: color-mix(in srgb, var(--brand) 45%, transparent);"
4526
+ ];
4527
+ return lines.map((l) => `${indent}${l}`).join("\n");
4528
+ }
4529
+ function brandCss(theme, source) {
4530
+ return [
4531
+ CSS_START,
4532
+ `/* Generated by \`npx @graph8/sdk brand --brand-color ${source.color}${source.dark ? ` --dark-brand-color ${source.dark}` : ""}\`. Re-run it to change`,
4533
+ " the colour; edits inside these markers are replaced. Text on --brand meets WCAG AA (4.5:1). */",
4534
+ "@theme inline {",
4535
+ " --color-brand: var(--brand);",
4536
+ " --color-brand-foreground: var(--brand-foreground);",
4537
+ ...Object.keys(theme.light.scale).map((k) => ` --color-brand-${k}: var(--brand-${k});`),
4538
+ "}",
4539
+ ":root {",
4540
+ vars(theme.light, " "),
4541
+ "}",
4542
+ ".dark {",
4543
+ vars(theme.dark, " "),
4544
+ "}",
4545
+ CSS_END
4546
+ ].join("\n");
4547
+ }
4548
+ function applyCssRegion(css, region) {
4549
+ const a = css.indexOf(CSS_START);
4550
+ const b = a >= 0 ? css.indexOf(CSS_END, a) : -1;
4551
+ if (a >= 0 && b > a) return `${css.slice(0, a)}${region}${css.slice(b + CSS_END.length)}`;
4552
+ return `${css.replace(/\s*$/, "")}
4553
+
4554
+ ${region}
4555
+ `;
4556
+ }
4557
+ var DEFAULT_FIELDS = { appUrl: "https://app.graph8.com", linkLabel: "graph8" };
4558
+ var CONFIG_FILE = "g8.config.ts";
4559
+ var CFG_START = "// graph8:brand:start";
4560
+ var CFG_END = "// graph8:brand:end";
4561
+ function renderBrandConfig(f) {
4562
+ return [
4563
+ "/**",
4564
+ " * This app's brand. The app shell shows `name` and `logo`; every graph8 block reads `linkLabel` and `appUrl`",
4565
+ ' * (through <BrandProvider>) for its copy and link-outs ("Connect in graph8", "Open in graph8"), so a white-label',
4566
+ " * app can name its own platform and link to its own URL. Fields left out keep graph8's defaults.",
4567
+ " *",
4568
+ ' * Change it with `npx @graph8/sdk brand --name "Acme" --brand-color "#0f62fe" --logo ./logo.svg`',
4569
+ " * (also --app-url, --link-label, --dark-brand-color), or edit the values below. The CLI rewrites only the",
4570
+ " * lines between the graph8:brand markers.",
4571
+ " */",
4572
+ configRegion(f),
4573
+ ""
4574
+ ].join("\n");
4575
+ }
4576
+ function configRegion(f) {
4577
+ return [
4578
+ CFG_START,
4579
+ "export const brand: { name: string; appUrl: string; linkLabel: string; logo?: string } = {",
4580
+ ` name: ${JSON.stringify(f.name)},`,
4581
+ ` appUrl: ${JSON.stringify(f.appUrl)},`,
4582
+ ` linkLabel: ${JSON.stringify(f.linkLabel)},`,
4583
+ ...f.logo ? [` logo: ${JSON.stringify(f.logo)},`] : [],
4584
+ "};",
4585
+ CFG_END
4586
+ ].join("\n");
4587
+ }
4588
+ function readBrandConfig(source) {
4589
+ const a = source.indexOf(CFG_START);
4590
+ const b = a >= 0 ? source.indexOf(CFG_END, a) : -1;
4591
+ if (a < 0 || b < 0) return null;
4592
+ const region = source.slice(a, b);
4593
+ const out = {};
4594
+ for (const key of ["name", "appUrl", "linkLabel", "logo"]) {
4595
+ const m = new RegExp(`^\\s*${key}:\\s*("(?:[^"\\\\]|\\\\.)*")`, "m").exec(region);
4596
+ if (m) {
4597
+ try {
4598
+ out[key] = JSON.parse(m[1]);
4599
+ } catch {
4600
+ }
4601
+ }
4602
+ }
4603
+ return out;
4604
+ }
4605
+ function applyBrandConfig(source, f) {
4606
+ const a = source.indexOf(CFG_START);
4607
+ const b = a >= 0 ? source.indexOf(CFG_END, a) : -1;
4608
+ if (a < 0 || b < 0) return null;
4609
+ return `${source.slice(0, a)}${configRegion(f)}${source.slice(b + CFG_END.length)}`;
4610
+ }
4611
+ var BRAND_USAGE = `Usage: npx @graph8/sdk brand [options]
4612
+
4613
+ Brand an app made by \`create\` (run it in the app's root). Every option is optional; values you leave out stay.
4614
+ --name <text> the app's name (sidebar, page title)
4615
+ --brand-color <hex> primary colour, e.g. "#0f62fe": writes --brand, a 50-900 scale and --primary into
4616
+ app/globals.css, with button text checked for WCAG AA (adjusted when needed)
4617
+ --dark-brand-color <hex> the colour in dark mode (default: derived from --brand-color, lightened if too dark)
4618
+ --logo <path> an svg, png, jpg, webp or gif (max 512 KB), copied to public/brand/ and shown in the sidebar
4619
+ --app-url <url> white-label: where link-outs go (default https://app.graph8.com)
4620
+ --link-label <text> white-label: what the platform is called in copy and links (default graph8)
4621
+ --dir <path> the app's root (default: current directory)
4622
+ --dry-run print what would change, write nothing
4623
+ Running it again with the same options changes nothing.`;
4624
+ function takeBrandFlag(o, flag, next) {
4625
+ if (flag === "--name") o.name = next();
4626
+ else if (flag === "--brand-color") o.color = next();
4627
+ else if (flag === "--dark-brand-color") o.darkColor = next();
4628
+ else if (flag === "--logo") o.logo = next();
4629
+ else if (flag === "--app-url") o.appUrl = next();
4630
+ else if (flag === "--link-label") o.linkLabel = next();
4631
+ else return false;
4632
+ return true;
4633
+ }
4634
+ function hasBrandInput(o) {
4635
+ return [o.name, o.color, o.darkColor, o.logo, o.appUrl, o.linkLabel].some((v) => v !== void 0);
4636
+ }
4637
+ function parseBrandArgs(argv) {
4638
+ const o = { dir: "." };
4639
+ for (let i = 0; i < argv.length; i++) {
4640
+ const arg = argv[i];
4641
+ const eq = arg.startsWith("--") ? arg.indexOf("=") : -1;
4642
+ const flag = eq > 0 ? arg.slice(0, eq) : arg;
4643
+ const next = () => {
4644
+ const v = eq > 0 ? arg.slice(eq + 1) : argv[++i];
4645
+ if (v === void 0 || v.startsWith("--") && v !== "--") throw new Error(`${flag} needs a value
4646
+
4647
+ ${BRAND_USAGE}`);
4648
+ return v;
4649
+ };
4650
+ if (takeBrandFlag(o, flag, next)) continue;
4651
+ if (flag === "--dir") o.dir = next();
4652
+ else if (arg === "--dry-run") o.dryRun = true;
4653
+ else throw new Error(`unknown option ${arg}
4654
+
4655
+ ${BRAND_USAGE}`);
4656
+ }
4657
+ if (!hasBrandInput(o)) throw new Error(`brand needs at least one of --name, --brand-color, --dark-brand-color, --logo, --app-url, --link-label
4658
+
4659
+ ${BRAND_USAGE}`);
4660
+ return o;
4661
+ }
4662
+ var LOGO_TYPES = ["svg", "png", "jpg", "jpeg", "webp", "gif"];
4663
+ var MAX_LOGO_BYTES = 512 * 1024;
4664
+ function brandProblem(o, cwd) {
4665
+ if (o.name !== void 0 && !o.name.trim()) return "--name cannot be empty";
4666
+ if (o.linkLabel !== void 0 && !o.linkLabel.trim()) return "--link-label cannot be empty";
4667
+ if (o.appUrl !== void 0 && !/^https?:\/\/[^\s/]+[^\s]*$/i.test(o.appUrl)) return `--app-url must be an http(s) URL (got "${o.appUrl}")`;
4668
+ if (o.color !== void 0 && !normalizeHex(o.color)) return `--brand-color must be a hex colour like #0f62fe (got "${o.color}")`;
4669
+ if (o.darkColor !== void 0 && !normalizeHex(o.darkColor)) return `--dark-brand-color must be a hex colour like #78a9ff (got "${o.darkColor}")`;
4670
+ if (o.darkColor !== void 0 && o.color === void 0) return "--dark-brand-color goes with --brand-color (pass both)";
4671
+ if (o.logo !== void 0) {
4672
+ const abs = import_node_path6.default.resolve(cwd, o.logo);
4673
+ const ext = import_node_path6.default.extname(abs).slice(1).toLowerCase();
4674
+ if (!LOGO_TYPES.includes(ext)) return `--logo must be one of ${LOGO_TYPES.join(", ")} (got "${o.logo}")`;
4675
+ if (!import_node_fs5.default.existsSync(abs) || !import_node_fs5.default.statSync(abs).isFile()) return `--logo: no file at ${o.logo}`;
4676
+ if (import_node_fs5.default.statSync(abs).size > MAX_LOGO_BYTES) return `--logo is larger than ${MAX_LOGO_BYTES / 1024} KB: use a smaller (or svg) file`;
4677
+ }
4678
+ return null;
4679
+ }
4680
+ function cssFile(root) {
4681
+ return ["app/globals.css", "src/app/globals.css"].find((f) => import_node_fs5.default.existsSync(import_node_path6.default.join(root, f))) ?? null;
4682
+ }
4683
+ function planBrand(o, root, cwd, fallbackName = "graph8 app") {
4684
+ const files = [];
4685
+ const notes = [];
4686
+ const cfgPath = import_node_path6.default.join(root, CONFIG_FILE);
4687
+ const existing = import_node_fs5.default.existsSync(cfgPath) ? import_node_fs5.default.readFileSync(cfgPath, "utf8") : null;
4688
+ const current = existing === null ? {} : readBrandConfig(existing);
4689
+ if (existing !== null && current === null) {
4690
+ throw new Error(`${CONFIG_FILE} has no graph8:brand markers, so it is yours and was not changed. Set the brand there by hand.`);
4691
+ }
4692
+ const fields = {
4693
+ name: o.name?.trim() ?? current?.name ?? fallbackName,
4694
+ appUrl: (o.appUrl ?? current?.appUrl ?? DEFAULT_FIELDS.appUrl).replace(/\/+$/, ""),
4695
+ linkLabel: o.linkLabel?.trim() ?? current?.linkLabel ?? DEFAULT_FIELDS.linkLabel,
4696
+ logo: current?.logo
4697
+ };
4698
+ if (o.logo !== void 0) {
4699
+ const src = import_node_path6.default.resolve(cwd, o.logo);
4700
+ const ext = import_node_path6.default.extname(src).slice(1).toLowerCase().replace("jpeg", "jpg");
4701
+ const rel = `public/brand/logo.${ext}`;
4702
+ files.push({ rel, content: import_node_fs5.default.readFileSync(src) });
4703
+ for (const other of LOGO_TYPES.map((e) => `public/brand/logo.${e}`)) {
4704
+ if (other !== rel && import_node_fs5.default.existsSync(import_node_path6.default.join(root, other))) files.push({ rel: other, content: null, note: "replaced by the new logo" });
4705
+ }
4706
+ fields.logo = `/brand/logo.${ext}`;
4707
+ }
4708
+ const cfg = existing === null ? renderBrandConfig(fields) : applyBrandConfig(existing, fields);
4709
+ files.push({ rel: CONFIG_FILE, content: cfg });
4710
+ if (o.color !== void 0) {
4711
+ const css = cssFile(root);
4712
+ if (!css) throw new Error("no app/globals.css found: run brand in the root of an app made by `graph8-sdk create`, or pass --dir");
4713
+ const theme = brandTheme(o.color, o.darkColor);
4714
+ notes.push(...theme.notes);
4715
+ notes.push(
4716
+ `colours: light ${theme.light.brand} with ${theme.light.foreground} text (${contrast(theme.light.foreground, theme.light.brand).toFixed(2)}:1), dark ${theme.dark.brand} with ${theme.dark.foreground} text (${contrast(theme.dark.foreground, theme.dark.brand).toFixed(2)}:1)`
4717
+ );
4718
+ const region = brandCss(theme, { color: normalizeHex(o.color), dark: o.darkColor ? normalizeHex(o.darkColor) : void 0 });
4719
+ files.push({ rel: css, content: applyCssRegion(import_node_fs5.default.readFileSync(import_node_path6.default.join(root, css), "utf8"), region) });
4720
+ }
4721
+ const shell = ["components/app-shell.tsx", "src/components/app-shell.tsx"].find((f) => import_node_fs5.default.existsSync(import_node_path6.default.join(root, f)));
4722
+ if (shell && !import_node_fs5.default.readFileSync(import_node_path6.default.join(root, shell), "utf8").includes("g8.config")) {
4723
+ notes.push(
4724
+ `${shell} predates branding, so it does not show the name and logo yet: import { brand } from "@/g8.config" there, and wrap its content in <BrandProvider brand={brand}> (components/g8/_foundation/brand-provider) so the blocks pick up linkLabel and appUrl.`
4725
+ );
4726
+ }
4727
+ return { files, notes };
4728
+ }
4729
+ function same(abs, content) {
4730
+ if (!import_node_fs5.default.existsSync(abs)) return false;
4731
+ const cur = import_node_fs5.default.readFileSync(abs);
4732
+ return Buffer.isBuffer(content) ? cur.equals(content) : cur.toString("utf8") === content;
4733
+ }
4734
+ function runBrand(o, io, cwd, fallbackName) {
4735
+ const root = import_node_path6.default.resolve(cwd, o.dir);
4736
+ if (!import_node_fs5.default.existsSync(import_node_path6.default.join(root, "package.json"))) {
4737
+ io.err(`no package.json in ${root}. Run this from your app's root, or pass --dir.`);
4738
+ return 1;
4739
+ }
4740
+ const problem = brandProblem(o, cwd);
4741
+ if (problem) return io.err(`${problem}
4742
+
4743
+ ${BRAND_USAGE}`), 1;
4744
+ let plan;
4745
+ try {
4746
+ plan = planBrand(o, root, cwd, fallbackName ?? import_node_path6.default.basename(root));
4747
+ } catch (e) {
4748
+ io.err(e.message);
4749
+ return 1;
4750
+ }
4751
+ for (const f of plan.files) {
4752
+ const abs = import_node_path6.default.join(root, f.rel);
4753
+ if (f.content === null) {
4754
+ io.out(`${o.dryRun ? "would remove" : "remove"} ${f.rel}${f.note ? ` (${f.note})` : ""}`);
4755
+ if (!o.dryRun) import_node_fs5.default.rmSync(abs, { force: true });
4756
+ continue;
4757
+ }
4758
+ if (same(abs, f.content)) {
4759
+ io.out(`keep ${f.rel}`);
4760
+ continue;
4761
+ }
4762
+ io.out(`${o.dryRun ? "would " : ""}${import_node_fs5.default.existsSync(abs) ? "update" : "create"} ${f.rel}`);
4763
+ if (!o.dryRun) {
4764
+ import_node_fs5.default.mkdirSync(import_node_path6.default.dirname(abs), { recursive: true });
4765
+ import_node_fs5.default.writeFileSync(abs, f.content);
4766
+ }
4767
+ }
4768
+ plan.notes.forEach((n) => io.out(`note: ${n}`));
4769
+ return 0;
4770
+ }
4771
+
4772
+ // src/blocks/create.ts
4773
+ var import_node_fs6 = __toESM(require("fs"));
4774
+ var import_node_path7 = __toESM(require("path"));
4775
+ var CREATE_USAGE = `Usage: npx @graph8/sdk create <stack> <dir> [options]
4776
+
4777
+ Scaffold a Next.js (App Router, TypeScript, Tailwind CSS v4) app for a stack and
4778
+ install the stack's blocks into it. Run \`graph8-sdk list\` to see the stacks.
4779
+ --package-manager <npm|pnpm|yarn|bun> default: the one running the command, else npm
4780
+ --no-install write the files and blocks, do not install dependencies
4781
+ --dry-run print what would be written, write and install nothing
4782
+ --force allow a non-empty <dir> (template files are overwritten, and graph8.json
4783
+ is reset: blocks you added to that app earlier are no longer recorded)
4784
+ Then: \`npm run dev\` in <dir>, open http://localhost:3000 and paste a graph8 API key (local preview), and run
4785
+ \`npx @graph8/sdk doctor --stack <stack>\` (add --production before going live).
4786
+
4787
+ Or pick the features yourself, for any app (also: \`create custom --blocks ...\`):
4788
+ npx @graph8/sdk create --blocks <id,id,...> [--title "Acme CRM"] <dir> [options]
4789
+ Every requested block's dependencies come with it. The app gets one route and one sidebar entry per finished
4790
+ Screen of the requested blocks, plus the user-facing Screens of their dependencies (asking for deals also gives
4791
+ you crm's screens). \`/\` opens the analytics screen if you asked for one, else the first screen.
4792
+ --title <text> the app's name (default: graph8 app)
4793
+ --with-deps-screens also add the plain fallback page of dependency blocks that have no Screen
4794
+ --no-deps-screens add screens only for the blocks you named
4795
+ Run \`graph8-sdk list\` to see each block and which have Screens.
4796
+
4797
+ Brand it (any stack or --blocks; the same as running \`graph8-sdk brand\` in the new app afterwards):
4798
+ --name <text> the app's name (with --blocks, the same as --title)
4799
+ --brand-color <hex> primary colour, AA-checked; --dark-brand-color <hex> for dark mode
4800
+ --logo <path> svg/png/jpg/webp/gif, shown in the sidebar
4801
+ --app-url <url> white-label: where "Open in graph8" links go; --link-label <text>: what they call it
4802
+ A directory literally named \`custom\` would read as the alias: pass \`--blocks crm ./custom\`.`;
4803
+ var PACKAGE_MANAGERS = ["npm", "pnpm", "yarn", "bun"];
4804
+ function parseCreateArgs(argv) {
4805
+ const o = { stack: "", dir: "", install: true, dryRun: false, force: false };
4806
+ const positional = [];
4807
+ for (let i = 0; i < argv.length; i++) {
4808
+ const arg = argv[i];
4809
+ const eq = arg.startsWith("--") ? arg.indexOf("=") : -1;
4810
+ const [flag, inline] = eq > 0 ? [arg.slice(0, eq), arg.slice(eq + 1)] : [arg, void 0];
4811
+ if (flag === "--package-manager") {
4812
+ const v = inline ?? argv[++i];
4813
+ if (!PACKAGE_MANAGERS.includes(v)) throw new Error(`--package-manager must be one of ${PACKAGE_MANAGERS.join(", ")}
4814
+
4815
+ ${CREATE_USAGE}`);
4816
+ o.packageManager = v;
4817
+ } else if (flag === "--blocks") {
4818
+ const v = inline ?? argv[++i];
4819
+ if (v === void 0 || v.startsWith("--")) throw new Error(`--blocks needs a comma-separated list of block ids
4820
+
4821
+ ${CREATE_USAGE}`);
4822
+ o.blocks = [...o.blocks ?? [], ...v.split(",").map((x) => x.trim()).filter(Boolean)];
4823
+ } else if (flag === "--title") {
4824
+ const v = inline ?? argv[++i];
4825
+ if (v === void 0 || v.startsWith("--")) throw new Error(`--title needs a value
4826
+
4827
+ ${CREATE_USAGE}`);
4828
+ o.title = v;
4829
+ } else if (takeBrandFlag(o.brand ??= {}, flag, () => {
4830
+ const v = inline ?? argv[++i];
4831
+ if (v === void 0 || v.startsWith("--")) throw new Error(`${flag} needs a value
4832
+
4833
+ ${CREATE_USAGE}`);
4834
+ return v;
4835
+ })) {
4836
+ } else if (arg === "--with-deps-screens") o.depsScreens = "with";
4837
+ else if (arg === "--no-deps-screens") o.depsScreens = "none";
4838
+ else if (arg === "--no-install") o.install = false;
4839
+ else if (arg === "--dry-run") o.dryRun = true;
4840
+ else if (arg === "--force") o.force = true;
4841
+ else if (arg.startsWith("-")) throw new Error(`unknown option ${arg}
4842
+
4843
+ ${CREATE_USAGE}`);
4844
+ else positional.push(arg);
4845
+ }
4846
+ if (o.brand && !hasBrandInput(o.brand)) delete o.brand;
4847
+ if (o.brand?.name !== void 0 && o.title !== void 0 && o.brand.name !== o.title) throw new Error(`--name and --title name the same thing: pass one
4848
+
4849
+ ${CREATE_USAGE}`);
4850
+ if (o.blocks || positional[0] === "custom") return parseCustom(o, positional);
4851
+ if (o.title || o.depsScreens) throw new Error(`--title, --with-deps-screens and --no-deps-screens go with --blocks
4852
+
4853
+ ${CREATE_USAGE}`);
4854
+ if (positional.length !== 2) throw new Error(`create needs a stack and a directory
4855
+
4856
+ ${CREATE_USAGE}`);
4857
+ [o.stack, o.dir] = positional;
4858
+ return o;
4859
+ }
4860
+ function parseCustom(o, positional) {
4861
+ const rest = positional[0] === "custom" ? positional.slice(1) : positional;
4862
+ if (!o.blocks?.length) throw new Error(`create custom needs --blocks <id,id,...> (see \`graph8-sdk list\`)
4863
+
4864
+ ${CREATE_USAGE}`);
4865
+ if (rest.length !== 1) {
4866
+ throw new Error(`${rest.length ? "a stack name cannot be combined with --blocks; pass only the directory" : "create --blocks needs a directory"}
4867
+
4868
+ ${CREATE_USAGE}`);
4869
+ }
4870
+ return { ...o, stack: "custom", dir: rest[0] };
4871
+ }
4872
+ function packageManagerFromUserAgent(ua) {
4873
+ const name = /^(npm|pnpm|yarn|bun)\//.exec(ua ?? "")?.[1];
4874
+ return name ?? "npm";
4875
+ }
4876
+ function appNameFor(dir) {
4877
+ const base = import_node_path7.default.basename(import_node_path7.default.resolve(dir)).toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[._-]+|-+$/g, "");
4878
+ return base || "g8-app";
4879
+ }
4880
+ var quoted = (p) => /^[\w@%+=:,./-]+$/.test(p) ? p : JSON.stringify(p);
4881
+ function walk(dir, base = dir) {
4882
+ const out = [];
4883
+ for (const e of import_node_fs6.default.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
4884
+ const full = import_node_path7.default.join(dir, e.name);
4885
+ if (e.isDirectory()) out.push(...walk(full, base));
4886
+ else out.push(posix(import_node_path7.default.relative(base, full)));
4887
+ }
4888
+ return out;
4889
+ }
4890
+ function outputName(rel) {
4891
+ const noExt = rel.replace(/\.tmpl$/, "");
4892
+ const dir = import_node_path7.default.posix.dirname(noExt);
4893
+ const base = import_node_path7.default.posix.basename(noExt).replace(/^dot-/, ".");
4894
+ return dir === "." ? base : `${dir}/${base}`;
4895
+ }
4896
+ function stackProblem(reg, stack) {
4897
+ const { missing } = stackStatus(reg, stack);
4898
+ if (missing.length) {
4899
+ const ready = reg.stacks.filter((s) => !stackStatus(reg, s).missing.length).map((s) => s.id);
4900
+ return [
4901
+ `stack "${stack.id}" is planned: these blocks are not in this version of @graph8/sdk yet: ${missing.join(", ")}.`,
4902
+ `Stacks you can create now: ${ready.join(", ") || "none"}. Or create one of those and add blocks as they ship (\`graph8-sdk add <block>\`).`
4903
+ ].join("\n");
4904
+ }
4905
+ const seen = /* @__PURE__ */ new Set();
4906
+ const homeIssue = homeProblem(stack);
4907
+ if (homeIssue) return homeIssue;
4908
+ for (const n of stack.nav) {
4909
+ const block = findBlock(reg, n.block);
4910
+ if (!stack.blocks.includes(n.block)) return `stack "${stack.id}" nav "${n.label}" uses block "${n.block}" which the stack does not include.`;
4911
+ if (!block?.components.some((c) => c.name === n.component)) return `stack "${stack.id}" nav "${n.label}" uses ${n.component}, which block "${n.block}" does not declare.`;
4912
+ if (/[\[\]]/.test(n.path)) return `stack "${stack.id}" nav "${n.label}" uses ${n.path}: dynamic segments cannot be a sidebar link.`;
4913
+ if (seen.has(n.path)) return `stack "${stack.id}" has two nav entries at ${n.path}.`;
4914
+ seen.add(n.path);
4915
+ if (n.page && (!reg.stacksRoot || !import_node_fs6.default.existsSync(import_node_path7.default.join(reg.stacksRoot, stack.id, n.page)))) {
4916
+ return `stack "${stack.id}" nav "${n.label}" uses page ${n.page}, which does not exist under stacks/${stack.id}/.`;
4917
+ }
4918
+ }
4919
+ return null;
4920
+ }
4921
+ function homeProblem(stack) {
4922
+ if (!stack.home) return null;
4923
+ if (!stack.nav.some((n) => n.path === stack.home)) return `stack "${stack.id}" has home ${stack.home}, which is not one of its nav paths.`;
4924
+ if (stack.home !== "/" && stack.nav.some((n) => n.path === "/")) return `stack "${stack.id}" has home ${stack.home} but also a nav entry at "/": drop one.`;
4925
+ return null;
4926
+ }
4927
+ function stackPageFile(reg, stack, n) {
4928
+ return n.page && reg.stacksRoot ? import_node_path7.default.join(reg.stacksRoot, stack.id, n.page) : null;
4929
+ }
4930
+ function homeRedirect(stack) {
4931
+ if (stack.home) return stack.home === "/" ? null : stack.home;
4932
+ return stack.nav.length && !stack.nav.some((n) => n.path === "/") ? stack.nav[0].path : null;
4933
+ }
4934
+ function renderPages(reg, stack, templates) {
4935
+ const pageTpl = import_node_fs6.default.readFileSync(import_node_path7.default.join(templates, "_pages", "route-page.tsx.tmpl"), "utf8");
4936
+ const files = stack.nav.map((n) => {
4937
+ const block = findBlock(reg, n.block);
4938
+ const comp = block.components.find((c) => c.name === n.component);
4939
+ const custom = stackPageFile(reg, stack, n);
4940
+ const vars2 = {
4941
+ stackId: stack.id,
4942
+ block: block.id,
4943
+ blockDir: `components/g8/${block.dir}`,
4944
+ component: n.component,
4945
+ importPath: componentImportPath(block, comp.file),
4946
+ propsJson: JSON.stringify(n.props ?? {}, null, 2)
4947
+ };
4948
+ return { rel: pageFileFor(n.path), content: render(custom ? import_node_fs6.default.readFileSync(custom, "utf8") : pageTpl, vars2) };
4949
+ });
4950
+ const to = homeRedirect(stack);
4951
+ if (to) {
4952
+ const redirect = import_node_fs6.default.readFileSync(import_node_path7.default.join(templates, "_pages", "redirect-page.tsx.tmpl"), "utf8");
4953
+ files.push({ rel: "app/page.tsx", content: render(redirect, { firstPathJson: JSON.stringify(to) }) });
4954
+ }
4955
+ return files;
4956
+ }
4957
+ function peerDependenciesFor(blocks, io, stack) {
4958
+ const peers = {};
4959
+ for (const b of blocks) for (const [name, range] of Object.entries(b.peerDependencies)) peers[name] ??= range;
4960
+ const usesUi = blocks.some((b) => walk(b.root).some((f) => /\.(tsx?|jsx?)$/.test(f) && /["']@graph8\/sdk\/ui["'/]/.test(import_node_fs6.default.readFileSync(import_node_path7.default.join(b.root, f), "utf8"))));
4961
+ if (usesUi && io.sdkPackageJson && import_node_fs6.default.existsSync(io.sdkPackageJson)) {
4962
+ const sdk = JSON.parse(import_node_fs6.default.readFileSync(io.sdkPackageJson, "utf8"));
4963
+ for (const [name, range] of Object.entries(sdk.peerDependencies ?? {})) peers[name] ??= range;
4964
+ }
4965
+ if (navUsesExtras(stack.nav)) peers["lucide-react"] ??= lucideRange(io);
4966
+ return peers;
4967
+ }
4968
+ function lucideRange(io) {
4969
+ try {
4970
+ const sdk = JSON.parse(import_node_fs6.default.readFileSync(io.sdkPackageJson ?? "", "utf8"));
4971
+ return sdk.peerDependencies?.["lucide-react"] ?? ">=0.400.0";
4972
+ } catch {
4973
+ return ">=0.400.0";
4974
+ }
4975
+ }
4976
+ function mergedPackageJson(rendered, peers) {
4977
+ const pkg = JSON.parse(rendered);
4978
+ for (const [name, range] of Object.entries(peers)) pkg.dependencies[name] ??= range;
4979
+ pkg.dependencies = Object.fromEntries(Object.entries(pkg.dependencies).sort(([a], [b]) => a.localeCompare(b)));
4980
+ return `${JSON.stringify(pkg, null, 2)}
4981
+ `;
4982
+ }
4983
+ function planCreate(reg, stack, appName, pm, io, app) {
4984
+ const templates = import_node_path7.default.join(io.templatesRoot, "next-app");
4985
+ const blocks = resolveWithDeps(reg, stack.blocks);
4986
+ const devCommand = pm === "npm" ? "npm run dev" : `${pm} dev`;
4987
+ const vars2 = {
4988
+ appName,
4989
+ sdkVersion: SDK_VERSION,
4990
+ stackId: stack.id,
4991
+ title: stack.title,
4992
+ titleJson: JSON.stringify(stack.title),
4993
+ ...navParts(stack),
4994
+ devCommand,
4995
+ installCommand: `${pm} install`,
4996
+ doctorCommand: app ? "npx @graph8/sdk doctor" : `npx @graph8/sdk doctor --stack ${stack.id}`,
4997
+ screens: stack.nav.map((n) => `- ${n.label}: \`${n.path}\` (${n.block} block, \`${n.component}\`)`).join("\n") || "- (none yet: add a block with `npx @graph8/sdk add <block>`)"
4998
+ };
4999
+ const files = [];
5000
+ for (const rel of walk(templates).filter((f) => !f.startsWith("_pages/"))) {
5001
+ let content = render(import_node_fs6.default.readFileSync(import_node_path7.default.join(templates, rel), "utf8"), vars2);
5002
+ if (rel === "package.json.tmpl") content = mergedPackageJson(content, peerDependenciesFor(blocks, io, stack));
5003
+ files.push({ rel: outputName(rel), content });
5004
+ }
5005
+ files.push(...renderPages(reg, stack, templates));
5006
+ files.push({ rel: CONFIG_FILE, content: renderBrandConfig({ name: stack.title, ...DEFAULT_FIELDS }) });
5007
+ const ledger = { sdkVersion: SDK_VERSION, target: "components/g8", stack: stack.id, ...app ? { app } : {}, blocks: {} };
5008
+ files.push({ rel: "graph8.json", content: `${JSON.stringify(ledger, null, 2)}
5009
+ ` });
5010
+ return files;
5011
+ }
5012
+ function dirProblem(abs, force) {
5013
+ if (!import_node_fs6.default.existsSync(abs)) return null;
5014
+ if (!import_node_fs6.default.statSync(abs).isDirectory()) return `${abs} exists and is not a directory.`;
5015
+ if (force || import_node_fs6.default.readdirSync(abs).length === 0) return null;
5016
+ return `${abs} is not empty. Pick a new directory, or pass --force to write into it (template files are overwritten).`;
5017
+ }
5018
+ function runCreate(opts, reg, io, cwd) {
5019
+ let app;
5020
+ let stack = opts.blocks ? void 0 : reg.stacks.find((s) => s.id === opts.stack);
5021
+ if (opts.blocks) {
5022
+ let composed;
5023
+ try {
5024
+ composed = composeApp(reg, opts.blocks, { title: opts.title, withDepsScreens: opts.depsScreens === "with", noDepsScreens: opts.depsScreens === "none" });
5025
+ } catch (e) {
5026
+ io.err(e.message);
5027
+ return 1;
5028
+ }
5029
+ stack = composed.stack;
5030
+ app = { title: stack.title, blocks: [...new Set(opts.blocks)], screens: composed.screens.map((x) => ({ block: x.block, component: x.component, path: x.path })) };
5031
+ composed.warnings.forEach((w) => io.err(`warning: ${w}`));
5032
+ } else if (!stack) {
5033
+ io.err(`unknown stack "${opts.stack}". Stacks: ${reg.stacks.map((s) => s.id).join(", ") || "none"} (\`graph8-sdk list\`).`);
5034
+ return 1;
5035
+ }
5036
+ if (!stack) return 1;
5037
+ const name = opts.brand?.name?.trim();
5038
+ if (name) {
5039
+ stack = { ...stack, title: name };
5040
+ if (app) app = { ...app, title: name };
5041
+ }
5042
+ const problem = stackProblem(reg, stack);
5043
+ if (problem) return io.err(problem), 1;
5044
+ const abs = import_node_path7.default.resolve(cwd, opts.dir);
5045
+ const dirIssue = dirProblem(abs, opts.force);
5046
+ if (dirIssue) return io.err(dirIssue), 1;
5047
+ const brandIssue = opts.brand ? brandProblem(opts.brand, cwd) : null;
5048
+ if (brandIssue) return io.err(brandIssue), 1;
5049
+ const pm = opts.packageManager ?? packageManagerFromUserAgent(io.userAgent);
5050
+ let files;
5051
+ try {
5052
+ files = planCreate(reg, stack, appNameFor(opts.dir), pm, io, app);
5053
+ } catch (e) {
5054
+ io.err(e.message);
5055
+ return 1;
5056
+ }
5057
+ const blocks = resolveWithDeps(reg, stack.blocks);
5058
+ io.out(`${opts.dryRun ? "Dry run: " : ""}creating "${stack.title}" (${app ? `custom: ${app.blocks.join(", ")}` : stack.id}) in ${opts.dir}`);
5059
+ for (const f of files) io.out(`${opts.dryRun ? "would write" : "write"} ${f.rel}`);
5060
+ if (opts.dryRun) {
5061
+ io.out(`would add blocks: ${blocks.map((b) => b.id).join(", ")}`);
5062
+ if (opts.brand) io.out(`would brand the app: ${Object.entries(opts.brand).map(([k, v]) => `${k} ${v}`).join(", ")}`);
5063
+ io.out(opts.install ? `would run: ${pm} install` : "would not install dependencies (--no-install)");
5064
+ return 0;
5065
+ }
5066
+ for (const f of files) {
5067
+ const dest = import_node_path7.default.join(abs, f.rel);
5068
+ import_node_fs6.default.mkdirSync(import_node_path7.default.dirname(dest), { recursive: true });
5069
+ import_node_fs6.default.writeFileSync(dest, f.content, "utf8");
5070
+ }
5071
+ return finish(opts, reg, stack, blocks, abs, pm, io, cwd);
5072
+ }
5073
+ function finish(opts, reg, stack, blocks, abs, pm, io, cwd) {
5074
+ for (const b of blocks) {
5075
+ const code = runAdd({ spec: b.id, dir: abs, target: "components/g8", dryRun: false, force: opts.force, install: false, wireScreens: false }, reg, io, cwd);
5076
+ if (code !== 0) {
5077
+ io.err(`adding block "${b.id}" failed; the app in ${opts.dir} is incomplete.`);
5078
+ return code;
5079
+ }
5080
+ }
5081
+ if (opts.brand) {
5082
+ io.out("branding the app (graph8-sdk brand)");
5083
+ const code = runBrand({ ...opts.brand, dir: abs }, io, cwd, stack.title);
5084
+ if (code !== 0) {
5085
+ io.err(`branding failed; the app in ${opts.dir} is complete but unbranded. Fix the option and run \`npx @graph8/sdk brand\` in it.`);
5086
+ return code;
5087
+ }
5088
+ }
5089
+ if (opts.install) {
5090
+ io.out(`installing dependencies: ${pm} install`);
5091
+ const code = io.spawn(pm, ["install"], abs);
5092
+ if (code !== 0) {
5093
+ io.err(`dependency install failed (exit ${code}). Run it yourself: cd ${quoted(opts.dir)} && ${pm} install`);
5094
+ return 1;
5095
+ }
5096
+ }
5097
+ const cd = opts.dir === "." ? "" : `cd ${quoted(opts.dir)} && `;
5098
+ io.out(
5099
+ [
5100
+ "",
5101
+ `Done. Next:`,
5102
+ ` 1. ${cd}${opts.install ? "" : `${pm} install && `}${pm === "npm" ? "npm run dev" : `${pm} dev`}`,
5103
+ ` 2. open http://localhost:3000 and paste a graph8 API key in the Connect graph8 screen (local preview: only`,
5104
+ ` this computer can reach the app's /api/g8 route), or set G8_API_KEY in ${import_node_path7.default.posix.join(opts.dir, ".env.local")}`,
5105
+ ` 3. ${cd}npx @graph8/sdk doctor${opts.blocks ? "" : ` --stack ${stack.id}`} (before going live: add --production)`
5106
+ ].join("\n")
5107
+ );
5108
+ return 0;
5109
+ }
5110
+
5111
+ // src/blocks/doctor.ts
5112
+ var import_node_fs8 = __toESM(require("fs"));
5113
+ var import_node_path9 = __toESM(require("path"));
5114
+
5115
+ // src/generated/billable.ts
5116
+ var BILLABLE_ROUTES = {
5117
+ GET: [
5118
+ "/apps/{app_id}/runtime",
5119
+ "/audience-syncs/{config_id}/runs",
5120
+ "/campaigns/{campaign_id}/dashboard",
5121
+ "/companies/{company_id}/next-best-step",
5122
+ "/contacts/{contact_id}/next-best-step",
5123
+ "/desk/kickoff",
5124
+ "/desk/kickoff/audio",
5125
+ "/radar/competitors/{competitor_id}/discover-pages",
5126
+ "/repos/{repo_id}/scan",
5127
+ "/seo/domains/{domain}/backlinks",
5128
+ "/seo/domains/{domain}/overview",
5129
+ "/voice/campaigns/call-scripts",
5130
+ "/voice/campaigns/email-templates",
5131
+ "/voice/campaigns/templates",
5132
+ "/voice/research/business-data",
5133
+ "/voice/research/competitors",
5134
+ "/voice/research/faq",
5135
+ "/voice/research/keywords",
5136
+ "/voice/research/prospect-company",
5137
+ "/work/routines/{routine_id}/runs"
5138
+ ],
5139
+ PATCH: [
5140
+ "/enrichment/auto-enrichment/configs/{config_id}",
5141
+ "/intent/keywords/{keyword_id}",
5142
+ "/intent/keywords/{keyword_id}/sync-config",
5143
+ "/marketplace/agencies/{slug}",
5144
+ "/marketplace/agencies/{slug}/reps/{sdr_id}",
5145
+ "/marketplace/agencies/{slug}/service-statements/{statement_id}",
5146
+ "/marketplace/applications/{application_id}",
5147
+ "/marketplace/conversations/{conversation_id}/state",
5148
+ "/marketplace/job-posts/{post_id}",
5149
+ "/marketplace/me/meetings/{meeting_id}",
5150
+ "/marketplace/me/messages/{conversation_id}/state",
5151
+ "/marketplace/me/profile",
5152
+ "/marketplace/me/service-statements/{statement_id}",
5153
+ "/marketplace/org/meetings/{meeting_id}",
5154
+ "/radar/competitors/{competitor_id}",
5155
+ "/radar/gaps/{gap_id}",
5156
+ "/research-reports/{report_id}",
5157
+ "/social-listeners/settings",
5158
+ "/social-listeners/{listener_id}"
5159
+ ],
5160
+ POST: [
5161
+ "/accounts/{account_id}/generate-outreach-plan",
5162
+ "/accounts/{account_id}/security-questionnaires",
5163
+ "/accounts/{account_id}/stakeholders/{stakeholder_id}/generate-outreach",
5164
+ "/ad-creatives",
5165
+ "/ad-creatives/batch",
5166
+ "/ad-creatives/concepts",
5167
+ "/ad-creatives/video/clips",
5168
+ "/ad-creatives/video/clips/prompt-enhance",
5169
+ "/ad-creatives/video/clips/{clip_id}/continue",
5170
+ "/ad-creatives/video/clips/{clip_id}/extend",
5171
+ "/ad-creatives/video/clips/{clip_id}/regenerate",
5172
+ "/ad-creatives/{creative_id}/ai-review",
5173
+ "/ad-creatives/{creative_id}/chat-edit",
5174
+ "/ad-creatives/{creative_id}/image-edit-at-point",
5175
+ "/ad-creatives/{creative_id}/persona-lens",
5176
+ "/ad-creatives/{creative_id}/regenerate-copy",
5177
+ "/ad-creatives/{creative_id}/regenerate-image",
5178
+ "/ad-creatives/{creative_id}/retry",
5179
+ "/ad-creatives/{creative_id}/rewrite-field",
5180
+ "/ad-creatives/{creative_id}/size-set",
5181
+ "/agent/chat",
5182
+ "/agent/onboarding/discovery/turn",
5183
+ "/agent/operators/{operator}/approvals/{task_id}/decide",
5184
+ "/agent/sessions/{session_id}/voice",
5185
+ "/agent/work-items",
5186
+ "/app-pages/{page_id}/generate",
5187
+ "/campaigns/generate-ideas",
5188
+ "/campaigns/generate-ideas-async",
5189
+ "/campaigns/recommend-personas",
5190
+ "/campaigns/{campaign_id}/audience-pipeline/accept-filters",
5191
+ "/campaigns/{campaign_id}/audience-pipeline/trigger",
5192
+ "/campaigns/{campaign_id}/documents/{document_id}/regenerate",
5193
+ "/campaigns/{campaign_id}/sequence/steps/{step_id}/generate",
5194
+ "/companies/enrich/bulk",
5195
+ "/companies/enrich/people/bulk",
5196
+ "/companies/enrich/stakeholders/bulk",
5197
+ "/companies/import",
5198
+ "/companies/{company_id}/documents/types/{doc_type}/generate",
5199
+ "/companies/{company_id}/documents/types/{doc_type}/regenerate",
5200
+ "/companies/{company_id}/enrich",
5201
+ "/companies/{company_id}/retry-intelligence",
5202
+ "/companies/{company_id}/retry-synthesis",
5203
+ "/companies/{company_id}/workflows/{workflow_type}/start",
5204
+ "/company-profile/generate",
5205
+ "/company-profile/reindex",
5206
+ "/company-profile/retry",
5207
+ "/contacts/unlock-info",
5208
+ "/contacts/verify-email",
5209
+ "/contacts/verify-email/async",
5210
+ "/contacts/workflow-executions/run",
5211
+ "/contacts/{contact_id}/documents/generate-all",
5212
+ "/contacts/{contact_id}/documents/types/{doc_type}/generate",
5213
+ "/contacts/{contact_id}/documents/types/{doc_type}/regenerate",
5214
+ "/content-workspace/import/crawl",
5215
+ "/content/calendar/ideas/{idea_id}/create-content",
5216
+ "/content/calendar/mine-signals",
5217
+ "/content/calendar/repurpose/ad",
5218
+ "/content/calendar/repurpose/linkedin-post",
5219
+ "/content/grids/{grid_id}/columns/{column_id}/run",
5220
+ "/content/grids/{grid_id}/rows/{row_id}/run/{column_id}",
5221
+ "/content/pages/enrich-seo-bulk",
5222
+ "/content/pages/{page_id}/enrich-seo",
5223
+ "/copilot/chat",
5224
+ "/copilot/landing-page-action",
5225
+ "/copilot/memory/promote",
5226
+ "/customer-success/companies/{company_id}/brief/synthesize",
5227
+ "/deals/{deal_id}/documents/generate-all",
5228
+ "/deals/{deal_id}/documents/types/{doc_type}/generate",
5229
+ "/deals/{deal_id}/documents/types/{doc_type}/regenerate",
5230
+ "/deals/{deal_id}/next-best-step",
5231
+ "/desk/accounts/{account_id}/committee/{contact_id}/find-linkedin",
5232
+ "/desk/accounts/{account_id}/plan/draft",
5233
+ "/desk/ask",
5234
+ "/desk/decisions/{decision_id}/replan",
5235
+ "/desk/email-improve",
5236
+ "/desk/email-score",
5237
+ "/desk/org-settings/plan-stages/run",
5238
+ "/desk/voice-memo",
5239
+ "/dialer/parallel-calls/grading/backfill",
5240
+ "/dialer/sessions/prewarm-voicemails",
5241
+ "/enrichment/ai-formula/generate",
5242
+ "/enrichment/ai-formula/generate-condition",
5243
+ "/enrichment/ai/batch/enrich",
5244
+ "/enrichment/ai/enrich",
5245
+ "/enrichment/ai/qualification/generate-templates",
5246
+ "/enrichment/ai/qualification/run",
5247
+ "/enrichment/auto-enrichment/configs",
5248
+ "/enrichment/enrich",
5249
+ "/enrichment/find-people/configure",
5250
+ "/enrichment/find-people/search-import",
5251
+ "/enrichment/find-people/{config_id}/import",
5252
+ "/enrichment/find-people/{config_id}/preview",
5253
+ "/enrichment/find-people/{config_id}/run",
5254
+ "/enrichment/generate-prompt",
5255
+ "/enrichment/jobs/{job_id}/resume",
5256
+ "/enrichment/list-routing/generate-filter",
5257
+ "/enrichment/lists/{list_id}/pipelines",
5258
+ "/enrichment/lists/{list_id}/pipelines/ai-draft",
5259
+ "/enrichment/lists/{list_id}/pipelines/{pipeline_id}/enabled",
5260
+ "/enrichment/lists/{list_id}/pipelines/{pipeline_id}/runs",
5261
+ "/enrichment/lookup/company",
5262
+ "/enrichment/lookup/person",
5263
+ "/enrichment/person",
5264
+ "/enrichment/record-sync/{entity_type}/run",
5265
+ "/enrichment/verify-email",
5266
+ "/enrichment/waterfall/configs",
5267
+ "/enrichment/waterfall/enrich",
5268
+ "/global-context/generate-all",
5269
+ "/global-context/generate/{doc_type}",
5270
+ "/hiring-signals/aggregators",
5271
+ "/hiring-signals/companies",
5272
+ "/hiring-signals/companies/job-counts",
5273
+ "/hiring-signals/companies/resolve-ids",
5274
+ "/hiring-signals/decision-makers",
5275
+ "/hiring-signals/job-changes/match",
5276
+ "/hiring-signals/jobs",
5277
+ "/hiring-signals/recruiters",
5278
+ "/icps/generate",
5279
+ "/inbox/drafts/generate",
5280
+ "/intelligence/analyze",
5281
+ "/intelligence/retry",
5282
+ "/intent/contacts/metadata",
5283
+ "/intent/keywords/add",
5284
+ "/intent/keywords/auto-seed-proposals",
5285
+ "/intent/keywords/create-from-domain",
5286
+ "/intent/keywords/create-from-search",
5287
+ "/intent/keywords/list",
5288
+ "/intent/keywords/{keyword_id}/companies",
5289
+ "/intent/keywords/{keyword_id}/contacts",
5290
+ "/intent/keywords/{keyword_id}/filters",
5291
+ "/intent/keywords/{keyword_id}/job-resolution",
5292
+ "/intent/keywords/{keyword_id}/sync",
5293
+ "/intent/keywords/{keyword_id}/urls",
5294
+ "/intent/pages-by-domain",
5295
+ "/intent/pages/contacts",
5296
+ "/intent/pages/search",
5297
+ "/intent/pages/visitor-counts",
5298
+ "/intent/pages/visitors",
5299
+ "/landing-pages",
5300
+ "/landing-pages/clone-homepage",
5301
+ "/landing-pages/clone-html",
5302
+ "/landing-pages/clone-url",
5303
+ "/landing-pages/components/extract",
5304
+ "/landing-pages/ensure-base-template",
5305
+ "/landing-pages/{landing_page_id}/ai-review",
5306
+ "/landing-pages/{landing_page_id}/ai-review/fix-with-ai",
5307
+ "/landing-pages/{landing_page_id}/edit",
5308
+ "/landing-pages/{landing_page_id}/edit-element",
5309
+ "/landing-pages/{landing_page_id}/edit-page",
5310
+ "/landing-pages/{landing_page_id}/generate-seo-metadata",
5311
+ "/landing-pages/{landing_page_id}/rewrite-text",
5312
+ "/leads/generate-content/batches",
5313
+ "/leads/{lead_id}/generate-content",
5314
+ "/library/case-studies/backfill",
5315
+ "/lifecycle-contacts/unlock",
5316
+ "/mailboxes/schedule-buy/plans",
5317
+ "/mailboxes/schedule-buy/plans/{plan_id}/resume",
5318
+ "/mailboxes/uncancel",
5319
+ "/mailboxes/{mailbox_id}/health/placement-test",
5320
+ "/marketplace/agencies/{slug}/intake/upload",
5321
+ "/marketplace/agencies/{slug}/reps/{sdr_id}/unlist",
5322
+ "/marketplace/agencies/{slug}/reps/{sdr_id}/video-intro",
5323
+ "/marketplace/agencies/{slug}/service-statements",
5324
+ "/marketplace/agencies/{slug}/service-statements/{statement_id}/line-items",
5325
+ "/marketplace/agencies/{slug}/service-statements/{statement_id}/submit",
5326
+ "/marketplace/commissions/entries",
5327
+ "/marketplace/commissions/entries/{entry_id}/quality",
5328
+ "/marketplace/commissions/entries/{entry_id}/reversals",
5329
+ "/marketplace/commissions/entries/{entry_id}/statement",
5330
+ "/marketplace/commissions/plans",
5331
+ "/marketplace/commissions/plans/{version_id}/activate",
5332
+ "/marketplace/commissions/preview",
5333
+ "/marketplace/conversations/{conversation_id}/read",
5334
+ "/marketplace/job-posts",
5335
+ "/marketplace/job-posts/{post_id}/jd",
5336
+ "/marketplace/me/headshot",
5337
+ "/marketplace/me/invoices",
5338
+ "/marketplace/me/invoices/{invoice_id}/approve",
5339
+ "/marketplace/me/messages/{conversation_id}/read",
5340
+ "/marketplace/me/offers/{hiring_id}/accept",
5341
+ "/marketplace/me/offers/{hiring_id}/reject",
5342
+ "/marketplace/me/payout-account/w8-certify",
5343
+ "/marketplace/me/resume",
5344
+ "/marketplace/me/service-statements",
5345
+ "/marketplace/me/service-statements/{statement_id}/line-items",
5346
+ "/marketplace/me/service-statements/{statement_id}/revise",
5347
+ "/marketplace/me/service-statements/{statement_id}/submit",
5348
+ "/marketplace/me/video-intro",
5349
+ "/marketplace/me/work-certificates",
5350
+ "/marketplace/opportunities/{post_id}/apply",
5351
+ "/marketplace/org/hirings/{hiring_id}/meetings",
5352
+ "/marketplace/org/hirings/{hiring_id}/terminate",
5353
+ "/marketplace/org/hirings/{hiring_id}/withdraw",
5354
+ "/marketplace/org/invoices/{invoice_id}/approve",
5355
+ "/marketplace/org/invoices/{invoice_id}/dispute",
5356
+ "/marketplace/org/service-statements/{statement_id}/approve",
5357
+ "/marketplace/org/service-statements/{statement_id}/line-items",
5358
+ "/marketplace/org/service-statements/{statement_id}/reject",
5359
+ "/marketplace/org/service-statements/{statement_id}/request-changes",
5360
+ "/marketplace/shortlist/export",
5361
+ "/marketplace/talent/{sdr_id}/hire",
5362
+ "/marketplace/talent/{sdr_id}/match/analyze",
5363
+ "/marketplace/talent/{sdr_id}/match/refresh",
5364
+ "/marketplace/terms-acknowledgements/ack-all",
5365
+ "/meeting-transcripts/{transcript_id}/follow-up-draft",
5366
+ "/meeting-transcripts/{transcript_id}/review/regenerate",
5367
+ "/newsletters/templates/clone",
5368
+ "/newsletters/templates/custom",
5369
+ "/newsletters/templates/layout",
5370
+ "/newsletters/{newsletter_id}/issues/{issue_id}/generate",
5371
+ "/newsletters/{newsletter_id}/issues/{issue_id}/rewrite-text",
5372
+ "/newsletters/{newsletter_id}/subscribers/import-audience",
5373
+ "/newsletters/{newsletter_id}/subscribers/verify",
5374
+ "/org-videos/generate",
5375
+ "/org/brand-snapshot/refresh",
5376
+ "/personas/generate",
5377
+ "/phone-numbers/purchase",
5378
+ "/pipelines/suggest",
5379
+ "/placement/tests",
5380
+ "/provider-domains/add-external",
5381
+ "/provider-domains/check-availability",
5382
+ "/provider-domains/checkout-purchase",
5383
+ "/provider-domains/checkout-transfer",
5384
+ "/provider-domains/{domain_id}/auto-warmup",
5385
+ "/provider-domains/{domain_id}/check-propagation",
5386
+ "/provider-domains/{domain_id}/checkout-mailboxes",
5387
+ "/provider-domains/{domain_id}/connect-provider",
5388
+ "/provider-domains/{domain_id}/dns",
5389
+ "/public/enrich/lookup",
5390
+ "/radar/aeo/run",
5391
+ "/radar/competitors",
5392
+ "/radar/competitors/bulk",
5393
+ "/radar/competitors/{competitor_id}/scan",
5394
+ "/radar/gaps/analyze",
5395
+ "/radar/initiatives/measure",
5396
+ "/radar/opportunities/approve-all",
5397
+ "/radar/opportunities/{gap_id}/approve",
5398
+ "/radar/reports",
5399
+ "/radar/scan-all",
5400
+ "/repos/{repo_id}/scan",
5401
+ "/research-reports/generate",
5402
+ "/research-reports/generate-all",
5403
+ "/research-reports/retry",
5404
+ "/research-reports/workflow/resume",
5405
+ "/revenue/stage-docs/{lifecycle_entry_id}/{entity}/backfill",
5406
+ "/scrape/requests",
5407
+ "/search/companies",
5408
+ "/search/companies/save",
5409
+ "/search/contacts",
5410
+ "/search/contacts/save",
5411
+ "/seo/audits",
5412
+ "/seo/keywords/research",
5413
+ "/sequencer/content/email/generate",
5414
+ "/sequencer/content/instructions/improve",
5415
+ "/sequencer/content/linkedin/generate",
5416
+ "/sequencer/templates/steps/generate",
5417
+ "/social-listeners",
5418
+ "/social-listeners/auto-seed/all",
5419
+ "/social-listeners/auto-seed/brand",
5420
+ "/social-listeners/auto-seed/competitors",
5421
+ "/social-listeners/auto-seed/engagement",
5422
+ "/social-listeners/auto-seed/keywords",
5423
+ "/social-listeners/bulk-from-list",
5424
+ "/social-listeners/integrations/x",
5425
+ "/social-listeners/integrations/x/test",
5426
+ "/social-listeners/posts/{post_id}/engagements/add-to-list",
5427
+ "/social-listeners/posts/{post_id}/mark",
5428
+ "/social-listeners/posts/{post_id}/save-contact",
5429
+ "/social-listeners/queries/generate",
5430
+ "/social/posts/generate",
5431
+ "/tam/generate",
5432
+ "/tam/regenerate",
5433
+ "/tasks/{task_id}/start",
5434
+ "/team-members/me/enrich-linkedin",
5435
+ "/team-members/{team_member_id}/enrich-linkedin",
5436
+ "/team-members/{team_member_id}/generate-post",
5437
+ "/teams/auto-compose/suggest",
5438
+ "/voice/agents/{agent_id}/documents/upload",
5439
+ "/voice/call-flow-examples/transcribe",
5440
+ "/voice/call-scripts/generate",
5441
+ "/voice/campaigns/call-scripts",
5442
+ "/voice/campaigns/email-enhancements",
5443
+ "/voice/campaigns/email-spintax",
5444
+ "/voice/campaigns/emails",
5445
+ "/voice/campaigns/linkedin-messages",
5446
+ "/voice/campaigns/onboarding/messages",
5447
+ "/voice/campaigns/onboarding/threads/actions",
5448
+ "/voice/campaigns/voicemails",
5449
+ "/voice/chat-agent/messages",
5450
+ "/voice/collections/{collection_id}/files/paste-text",
5451
+ "/voice/collections/{collection_id}/files/upload",
5452
+ "/voice/collections/{collection_id}/files/upload-multiple",
5453
+ "/voice/collections/{collection_id}/reindex",
5454
+ "/voice/collections/{collection_id}/scrape-sources",
5455
+ "/voice/collections/{collection_id}/scrape-sources/{source_id}/run",
5456
+ "/voice/email-agent/batch",
5457
+ "/voice/email-agent/compose",
5458
+ "/voice/email-agent/draft-reply",
5459
+ "/voice/icp/queries",
5460
+ "/voice/knowledge-base/files/upload",
5461
+ "/voice/receptionist/messages",
5462
+ "/voice/research/company",
5463
+ "/voice/research/org-chart",
5464
+ "/voice/research/prospect-data",
5465
+ "/voice/research/prospect-profile",
5466
+ "/voice/topics/classify",
5467
+ "/voice/twins",
5468
+ "/voice/twins/extract-linkedin",
5469
+ "/voice/video-recordings/upload",
5470
+ "/voice/voice-library/onboarding-voice",
5471
+ "/voice/voice-library/voices",
5472
+ "/voice/worksheets/calculate",
5473
+ "/work/signals/promote",
5474
+ "/workbench/tables/{table_id}/ai/generate-prompt",
5475
+ "/workbench/tables/{table_id}/ai/groups/{group_id}/run",
5476
+ "/workbench/tables/{table_id}/ai/preview",
5477
+ "/workbench/tables/{table_id}/ai/run",
5478
+ "/workbench/tables/{table_id}/ai/run-all",
5479
+ "/workbench/tables/{table_id}/columns/{col_id}/ai/run",
5480
+ "/workbench/tables/{table_id}/find-people/configure",
5481
+ "/workbench/tables/{table_id}/find-people/{config_id}/run",
5482
+ "/workbench/tables/{table_id}/import/search",
5483
+ "/workbench/tables/{table_id}/import/search/research/preview",
5484
+ "/workbench/tables/{table_id}/pipelines",
5485
+ "/workbench/tables/{table_id}/scraper/providers/{provider}/runs",
5486
+ "/workbench/tables/{table_id}/scraper/runs",
5487
+ "/workbench/tables/{table_id}/suggest-mappings",
5488
+ "/workbench/tables/{table_id}/triggers",
5489
+ "/workbench/tables/{table_id}/unlock-rows",
5490
+ "/workbench/tables/{table_id}/waterfall/configs/{column_id}/run"
5491
+ ],
5492
+ PUT: [
5493
+ "/agent/operators/{operator}/enabled",
5494
+ "/agent/operators/{operator}/skills/{skill_key}",
5495
+ "/deliverability/autopilot/preferences",
5496
+ "/enrichment/auto-enrichment/settings/{entity_type}",
5497
+ "/enrichment/lists/{list_id}/pipelines/{pipeline_id}",
5498
+ "/enrichment/record-sync/{entity_type}/policy",
5499
+ "/enrichment/waterfall/configs/{column_id}",
5500
+ "/marketplace/me/invoices/{invoice_id}",
5501
+ "/marketplace/me/rates",
5502
+ "/radar/settings",
5503
+ "/workbench/tables/{table_id}/auto-enrich",
5504
+ "/workbench/tables/{table_id}/pipelines/{pipeline_id}",
5505
+ "/workbench/tables/{table_id}/schedule",
5506
+ "/workbench/tables/{table_id}/triggers/{trigger_id}"
5507
+ ]
5508
+ };
5509
+
5510
+ // src/generated/external.ts
5511
+ var EXTERNAL_ROUTES = {
5512
+ GET: [
5513
+ "/dialer/sessions/token",
5514
+ "/marketplace/me/connect/dashboard-link",
5515
+ "/marketplace/me/connect/onboarding-link",
5516
+ "/marketplace/me/payout-account/login-link",
5517
+ "/marketplace/me/payout-account/onboarding-link",
5518
+ "/voice/mcp-servers/{mcp_server_id}/tools"
5519
+ ],
5520
+ PATCH: [
5521
+ "/ad-campaigns/{campaign_id}/ads/{ad_id}",
5522
+ "/appointments/bookings/{booking_uid}/location",
5523
+ "/appointments/bookings/{booking_uid}/sequence",
5524
+ "/appointments/workflows/{workflow_id}",
5525
+ "/audience-syncs/{config_id}",
5526
+ "/campaigns/{campaign_id}",
5527
+ "/campaigns/{campaign_id}/full",
5528
+ "/content/calendar/{content_type}/{content_id}/schedule",
5529
+ "/inbox/mailboxes/bulk",
5530
+ "/inbox/mailboxes/{mailbox}/settings",
5531
+ "/integrations/crm/meetings/{event_id}",
5532
+ "/newsletters/{newsletter_id}/subscribers/{subscriber_id}/unsubscribe",
5533
+ "/quotes/{quote_id}",
5534
+ "/sequences/{sequence_id}",
5535
+ "/sequences/{sequence_id}/steps/{step_id}",
5536
+ "/voice/dialer/sessions/{session_id}/status",
5537
+ "/voice/dispositions/{disposition_id}",
5538
+ "/webhooks/{webhook_id}"
5539
+ ],
5540
+ POST: [
5541
+ "/ad-campaigns/images",
5542
+ "/ad-campaigns/status-refresh",
5543
+ "/ad-campaigns/{campaign_id}/ads",
5544
+ "/ad-campaigns/{campaign_id}/ads/batch",
5545
+ "/ad-campaigns/{campaign_id}/ads/{ad_id}/status",
5546
+ "/ad-campaigns/{campaign_id}/edit-preview",
5547
+ "/ad-campaigns/{campaign_id}/retry",
5548
+ "/ad-campaigns/{campaign_id}/status-refresh",
5549
+ "/appointments/bookings",
5550
+ "/appointments/bookings/import",
5551
+ "/appointments/bookings/{booking_uid}/confirm",
5552
+ "/appointments/bookings/{booking_uid}/guests",
5553
+ "/appointments/bookings/{booking_uid}/mark-absent",
5554
+ "/appointments/bookings/{booking_uid}/notes",
5555
+ "/appointments/bookings/{booking_uid}/request-reschedule",
5556
+ "/appointments/bookings/{booking_uid}/reschedule",
5557
+ "/appointments/calendars/selected",
5558
+ "/appointments/conferencing/connect/{provider}",
5559
+ "/appointments/conferencing/{provider}/default",
5560
+ "/appointments/workflows",
5561
+ "/appointments/workflows/{workflow_id}/toggle",
5562
+ "/audience-syncs",
5563
+ "/audience-syncs/google/conversion-actions",
5564
+ "/audience-syncs/google/conversions",
5565
+ "/audience-syncs/google/video-assets/library",
5566
+ "/audience-syncs/google/video-assets/links",
5567
+ "/audience-syncs/google/video-assets/upload-urls",
5568
+ "/audience-syncs/google/video-assets/uploads",
5569
+ "/audience-syncs/{config_id}/launch",
5570
+ "/audience-syncs/{config_id}/trigger",
5571
+ "/campaigns",
5572
+ "/campaigns/{campaign_id}/ad-images/{image_id}/hide",
5573
+ "/campaigns/{campaign_id}/documents",
5574
+ "/campaigns/{campaign_id}/launch",
5575
+ "/campaigns/{campaign_id}/sequence/steps",
5576
+ "/content/calendar/{content_type}/{content_id}/retry-publish",
5577
+ "/crm-syncs/{provider}/companies/push",
5578
+ "/crm-syncs/{provider}/contacts/push",
5579
+ "/crm-syncs/{provider}/lists/push",
5580
+ "/customer-success/companies/{company_id}/sync",
5581
+ "/desk/accounts/{account_id}/committee/plays/{action_id}/run",
5582
+ "/desk/accounts/{account_id}/committee/{contact_id}/linkedin-invite",
5583
+ "/desk/decisions/{decision_id}/plays/{action_id}/run",
5584
+ "/desk/decisions/{decision_id}/send-reply",
5585
+ "/desk/decisions/{decision_id}/send-sms",
5586
+ "/desk/plays/approvals/{execution_id}/decide",
5587
+ "/dialer/parallel-calls/calls/{livekit_room_name}/drop-voicemail",
5588
+ "/dialer/parallel-calls/manual-dial",
5589
+ "/dialer/parallel-calls/redial-contact",
5590
+ "/dialer/parallel-calls/sessions/start",
5591
+ "/dialer/sessions/calls/drop-voicemail",
5592
+ "/dialer/sessions/{session_id}/next-batch",
5593
+ "/envelopes/{envelope_id}/resend",
5594
+ "/envelopes/{envelope_id}/send",
5595
+ "/feedback/sending-domains/verify",
5596
+ "/feedback/surveys/{survey_id}/send",
5597
+ "/forms/deliveries/{receipt_id}/retry",
5598
+ "/forms/{form_key}/backfills",
5599
+ "/forms/{form_key}/delivery/pause",
5600
+ "/forms/{form_key}/delivery/resume",
5601
+ "/inbox/emails/compose",
5602
+ "/inbox/emails/{email_id}/send",
5603
+ "/inbox/linkedin/accounts/sync",
5604
+ "/inbox/linkedin/accounts/{account_id}/enable",
5605
+ "/inbox/linkedin/compose",
5606
+ "/inbox/mailboxes/activate",
5607
+ "/inbox/mailboxes/{mailbox}/sync",
5608
+ "/inbox/sms/compose",
5609
+ "/inbox/{reply_id}/assign",
5610
+ "/inbox/{reply_id}/send",
5611
+ "/inbox/{reply_id}/tag",
5612
+ "/integrations/connections",
5613
+ "/integrations/connections/session-token",
5614
+ "/integrations/crm-sync/audiences",
5615
+ "/integrations/crm-sync/contacts/pull",
5616
+ "/integrations/crm-sync/contacts/push",
5617
+ "/integrations/crm-sync/deals/pull",
5618
+ "/integrations/crm-sync/deals/push",
5619
+ "/integrations/crm-sync/leads/previews",
5620
+ "/integrations/crm-sync/leads/pull",
5621
+ "/integrations/crm-sync/leads/push",
5622
+ "/integrations/crm-sync/pull",
5623
+ "/integrations/crm-sync/push",
5624
+ "/integrations/crm/connections/{connection_id}/batch/contacts",
5625
+ "/integrations/crm/connections/{connection_id}/companies",
5626
+ "/integrations/crm/connections/{connection_id}/contacts",
5627
+ "/integrations/crm/connections/{connection_id}/deals",
5628
+ "/integrations/crm/connections/{connection_id}/leads",
5629
+ "/integrations/crm/lists",
5630
+ "/integrations/crm/lists/memberships",
5631
+ "/integrations/crm/meetings",
5632
+ "/integrations/linkedin-outreach/campaigns/{campaign_id}/pause",
5633
+ "/integrations/linkedin-outreach/campaigns/{campaign_id}/resume",
5634
+ "/integrations/linkedin-outreach/connection/reconnect",
5635
+ "/integrations/linkedin-outreach/lists",
5636
+ "/integrations/linkedin-outreach/lists/{list_id}/leads",
5637
+ "/integrations/linkedin-outreach/workspaces/sync",
5638
+ "/integrations/migrations/delta-sync",
5639
+ "/integrations/migrations/discovery",
5640
+ "/integrations/migrations/{migration_id}/confirm",
5641
+ "/integrations/sugarcrm/connect",
5642
+ "/integrations/sugarcrm/reconnect",
5643
+ "/landing-pages/{landing_page_id}/publish",
5644
+ "/landing-pages/{landing_page_id}/unpublish",
5645
+ "/linkedin/accounts/sync",
5646
+ "/marketplace/agencies/{slug}/reps/{sdr_id}/publish",
5647
+ "/marketplace/me/messages/{conversation_id}/reply",
5648
+ "/marketplace/me/payout-account/bank-details",
5649
+ "/marketplace/me/payout-account/methods",
5650
+ "/marketplace/me/payout-account/setup",
5651
+ "/marketplace/me/sync-workbench",
5652
+ "/marketplace/talent/{sdr_id}/messages",
5653
+ "/marketplace/talent/{sdr_id}/request",
5654
+ "/newsletters",
5655
+ "/newsletters/{newsletter_id}/activate",
5656
+ "/newsletters/{newsletter_id}/issues",
5657
+ "/newsletters/{newsletter_id}/issues/{issue_id}/duplicate",
5658
+ "/newsletters/{newsletter_id}/issues/{issue_id}/send",
5659
+ "/newsletters/{newsletter_id}/issues/{issue_id}/send-test",
5660
+ "/newsletters/{newsletter_id}/issues/{issue_id}/send/resume",
5661
+ "/newsletters/{newsletter_id}/resume",
5662
+ "/newsletters/{newsletter_id}/settings/verify-domain",
5663
+ "/newsletters/{newsletter_id}/subscribers",
5664
+ "/nurtures/{nurture_id}/launch",
5665
+ "/org/sso/sync",
5666
+ "/phone-numbers/a2p/register",
5667
+ "/profile/deal-automations/sync-now",
5668
+ "/programmatic-campaigns/{campaign_id}/pages/actions",
5669
+ "/programmatic-campaigns/{campaign_id}/pages/{page_id}/actions",
5670
+ "/provider-domains/{domain_id}/forwarding",
5671
+ "/provider-domains/{domain_id}/mailbox-profile-picture",
5672
+ "/provider-domains/{domain_id}/regenerate-nameservers",
5673
+ "/provider-domains/{domain_id}/resend-transfer-password",
5674
+ "/provider-domains/{domain_id}/resend-verification",
5675
+ "/provider-domains/{domain_id}/set-nameservers",
5676
+ "/provider-usage/refresh",
5677
+ "/provider-usage/sync",
5678
+ "/quote-billing-contact",
5679
+ "/quote-contacts",
5680
+ "/quote-settings/documents",
5681
+ "/quote-templates",
5682
+ "/quotes",
5683
+ "/quotes/batch",
5684
+ "/quotes/{quote_id}/duplicate",
5685
+ "/quotes/{quote_id}/edit-as-draft",
5686
+ "/quotes/{quote_id}/mark-accepted",
5687
+ "/quotes/{quote_id}/send",
5688
+ "/quotes/{quote_id}/unarchive",
5689
+ "/radar/alerts/dispatch",
5690
+ "/repos/{repo_id}/campaigns/{campaign_id}/launch",
5691
+ "/roles/invite",
5692
+ "/sales-coach/interviews/{interview_id}/members",
5693
+ "/sales-coach/interviews/{interview_id}/members/{member_id}/send-invite",
5694
+ "/sequencer/content/voice/test-call",
5695
+ "/sequences",
5696
+ "/sequences/generate-spintax",
5697
+ "/sequences/{sequence_id}/contacts",
5698
+ "/sequences/{sequence_id}/contacts/{contact_id}/resume",
5699
+ "/sequences/{sequence_id}/pause",
5700
+ "/sequences/{sequence_id}/resume",
5701
+ "/sequences/{sequence_id}/run",
5702
+ "/sequences/{sequence_id}/sync",
5703
+ "/settings/scrape-config/crawl",
5704
+ "/skills/{action_id}/execute",
5705
+ "/skills/{action_id}/test-runs",
5706
+ "/social-listeners/posts/{post_id}/add-to-sequence",
5707
+ "/social/posts/{post_id}/engagers/add-to-audience",
5708
+ "/social/posts/{post_id}/engagers/enroll",
5709
+ "/social/posts/{post_id}/publish",
5710
+ "/social/posts/{post_id}/schedule",
5711
+ "/stripe/api-key",
5712
+ "/stripe/sync",
5713
+ "/team-members/sync",
5714
+ "/voice/calls",
5715
+ "/voice/dialer/sessions",
5716
+ "/voice/dialer/sessions/{session_id}/resume",
5717
+ "/voice/dispositions",
5718
+ "/voice/inbound-calls/decline",
5719
+ "/voice/inbound-calls/dialer/{call_id}/decline",
5720
+ "/voice/inbound-calls/{room_name}/claim",
5721
+ "/voice/mcp-servers/{mcp_server_id}/test",
5722
+ "/webhooks",
5723
+ "/webhooks/{webhook_id}/rotate-secret",
5724
+ "/website-editor/publish",
5725
+ "/website-editor/sync",
5726
+ "/workbench/scraper/providers/{provider}/connection/test",
5727
+ "/workbench/tables/{table_id}/import/crm",
5728
+ "/workbench/tables/{table_id}/send-to-sequence",
5729
+ "/workflows",
5730
+ "/workflows/executions/approve",
5731
+ "/workflows/executions/bulk-restart",
5732
+ "/workflows/executions/request-changes",
5733
+ "/workflows/executions/{execution_id}/pause",
5734
+ "/workflows/executions/{execution_id}/restart",
5735
+ "/workflows/executions/{execution_id}/resume",
5736
+ "/workflows/executions/{execution_id}/stop",
5737
+ "/workflows/integrations/{provider_name}/test",
5738
+ "/workflows/validate",
5739
+ "/workflows/workbenches",
5740
+ "/workflows/{action_id}/execute",
5741
+ "/workflows/{action_id}/trigger-reset"
5742
+ ],
5743
+ PUT: [
5744
+ "/ad-campaigns/{campaign_id}",
5745
+ "/appointments/calendars/destination",
5746
+ "/appointments/conferencing/{provider}/link",
5747
+ "/campaigns/{campaign_id}/audience",
5748
+ "/campaigns/{campaign_id}/documents/{document_id}",
5749
+ "/campaigns/{campaign_id}/sequence",
5750
+ "/campaigns/{campaign_id}/sequence/steps/{step_id}",
5751
+ "/desk/autonomy",
5752
+ "/desk/org-settings",
5753
+ "/inbox/linkedin/accounts/{account_id}",
5754
+ "/integrations/crm-sync/pull-enabled",
5755
+ "/integrations/crm-sync/push-enabled",
5756
+ "/integrations/crm/connections/{connection_id}/batch/contacts",
5757
+ "/integrations/crm/connections/{connection_id}/companies/{company_id}",
5758
+ "/integrations/crm/connections/{connection_id}/contacts/{contact_id}",
5759
+ "/integrations/crm/connections/{connection_id}/deals/{deal_id}",
5760
+ "/integrations/crm/connections/{connection_id}/leads/{lead_id}",
5761
+ "/integrations/linkedin-outreach/workspaces/active",
5762
+ "/linkedin/accounts/{account_id}/schedule",
5763
+ "/newsletters/{newsletter_id}/issues/{issue_id}",
5764
+ "/quote-settings",
5765
+ "/quote-templates/{template_id}",
5766
+ "/quotes/{quote_id}",
5767
+ "/sms/phone-numbers/{phone_number}",
5768
+ "/social/autopilot/{account_id}",
5769
+ "/workflows/{action_id}"
5770
+ ]
5771
+ };
5772
+
5773
+ // src/errors.ts
5774
+ var G8ErrorClass = class _G8ErrorClass extends Error {
5775
+ constructor(args) {
5776
+ super(args.message);
5777
+ this.name = "G8Error";
5778
+ this.status = args.status;
5779
+ this.type = args.type;
5780
+ this.code = args.code;
5781
+ this.requestId = args.requestId;
5782
+ this.detail = args.detail;
5783
+ this.retryable = args.retryable ?? false;
5784
+ Object.setPrototypeOf(this, _G8ErrorClass.prototype);
5785
+ }
5786
+ };
5787
+ var REGISTRY = /* @__PURE__ */ Symbol.for("@graph8/sdk/G8Error");
5788
+ var registry = globalThis;
5789
+ var G8Error = registry[REGISTRY] ?? (registry[REGISTRY] = G8ErrorClass);
5790
+
5791
+ // src/http.ts
5792
+ function isRetryableStatus(status) {
5793
+ return status === 429 || status >= 500 && status <= 599;
5794
+ }
5795
+ function isWriteMethod(method) {
5796
+ const m = method.toUpperCase();
5797
+ return m === "POST" || m === "PUT" || m === "PATCH" || m === "DELETE";
5798
+ }
5799
+ var API_BASE_SEGMENT = "/api/v1";
5800
+ var billableMatchers = null;
5801
+ var externalMatchers = null;
5802
+ function templateToRegexSource(template) {
5803
+ return template.split(/(\{[^}/]+\})/).map((part) => /^\{[^}/]+\}$/.test(part) ? "[^/]+" : part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("");
5804
+ }
5805
+ function buildMatchers(routes) {
5806
+ const out = /* @__PURE__ */ new Map();
5807
+ for (const [m, templates] of Object.entries(routes)) {
5808
+ if (!templates.length) continue;
5809
+ out.set(m, new RegExp(`^(?:${templates.map(templateToRegexSource).join("|")})/?$`));
5810
+ }
5811
+ return out;
5812
+ }
5813
+ function matcherFor(method) {
5814
+ if (!billableMatchers) billableMatchers = buildMatchers(BILLABLE_ROUTES);
5815
+ return billableMatchers.get(method.toUpperCase());
5816
+ }
5817
+ function externalMatcherFor(method) {
5818
+ if (!externalMatchers) externalMatchers = buildMatchers(EXTERNAL_ROUTES);
5819
+ return externalMatchers.get(method.toUpperCase());
5820
+ }
5821
+ function apiRelativePath(urlOrPath) {
5822
+ let p = urlOrPath.replace(/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^/]*/, "");
5823
+ p = p.split(/[?#]/, 1)[0];
5824
+ const at = p.indexOf(API_BASE_SEGMENT + "/");
5825
+ if (at < 0) return null;
5826
+ return p.slice(at + API_BASE_SEGMENT.length);
5827
+ }
5828
+ function isBillableOperation(method, urlOrPath) {
5829
+ const matcher = matcherFor(method);
5830
+ if (!matcher) return false;
5831
+ const p = apiRelativePath(urlOrPath);
5832
+ return p != null && matcher.test(p);
5833
+ }
5834
+ function isExternalOperation(method, urlOrPath) {
5835
+ const matcher = externalMatcherFor(method);
5836
+ if (!matcher) return false;
5837
+ const p = apiRelativePath(urlOrPath);
5838
+ return p != null && matcher.test(p);
5839
+ }
5840
+ function isNoBlindRetryOperation(method, urlOrPath) {
5841
+ return isBillableOperation(method, urlOrPath) || isExternalOperation(method, urlOrPath);
5842
+ }
5843
+ var PRE_SEND_ERROR_CODES = /* @__PURE__ */ new Set([
5844
+ "ECONNREFUSED",
5845
+ "ENOTFOUND",
5846
+ "EAI_AGAIN",
5847
+ "ENETUNREACH",
5848
+ "EHOSTUNREACH",
5849
+ "UND_ERR_CONNECT_TIMEOUT"
5850
+ ]);
5851
+ function isPreSendNetworkError(err) {
5852
+ let cur = err;
5853
+ for (let depth = 0; cur && depth < 4; depth++) {
5854
+ if (typeof cur.code === "string" && PRE_SEND_ERROR_CODES.has(cur.code)) return true;
5855
+ if (Array.isArray(cur.errors) && cur.errors.some((e) => isPreSendNetworkError(e))) return true;
5856
+ cur = cur.cause;
5857
+ }
5858
+ return false;
5859
+ }
5860
+ function newIdempotencyKey() {
5861
+ const c = globalThis.crypto;
5862
+ if (c?.randomUUID) return c.randomUUID();
5863
+ if (c?.getRandomValues) {
5864
+ const bytes = c.getRandomValues(new Uint8Array(16));
5865
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
5866
+ }
5867
+ return `g8-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;
5868
+ }
5869
+ function parseRetryAfter(header, nowMs = Date.now()) {
5870
+ if (!header) return null;
5871
+ const secs = Number(header);
5872
+ if (Number.isFinite(secs) && secs >= 0) return Math.round(secs * 1e3);
5873
+ const dateMs = Date.parse(header);
5874
+ if (!Number.isNaN(dateMs)) {
5875
+ const delta = dateMs - nowMs;
5876
+ return delta > 0 ? delta : 0;
5877
+ }
5878
+ return null;
5879
+ }
5880
+ function backoffDelayMs(attempt, baseMs = 200, rand = Math.random) {
5881
+ const expo = Math.min(baseMs * 2 ** attempt, 1e4);
5882
+ return Math.floor(expo / 2 + rand() * (expo / 2));
5883
+ }
5884
+ function defaultSleep(ms) {
5885
+ return new Promise((resolve) => setTimeout(resolve, ms));
5886
+ }
5887
+ function buildQuery(query) {
5888
+ if (!query) return "";
5889
+ const qs = new URLSearchParams();
5890
+ for (const [k, v] of Object.entries(query)) {
5891
+ if (v != null) qs.set(k, String(v));
5892
+ }
5893
+ const s = qs.toString();
5894
+ return s ? `?${s}` : "";
5895
+ }
5896
+ async function toG8Error(resp, retryable = isRetryableStatus(resp.status)) {
5897
+ let body = void 0;
5898
+ try {
5899
+ const text = await resp.text();
5900
+ body = text ? JSON.parse(text) : void 0;
5901
+ } catch {
5902
+ body = void 0;
5903
+ }
5904
+ const env = body ?? {};
5905
+ const message = typeof env.message === "string" && env.message || typeof env.error === "string" && env.error || `Request failed with status ${resp.status}`;
5906
+ return new G8Error({
5907
+ message,
5908
+ status: resp.status,
5909
+ type: typeof env.type === "string" && env.type || typeof env.error === "string" && env.error || "api_error",
5910
+ code: typeof env.code === "string" ? env.code : String(resp.status),
5911
+ requestId: typeof env.request_id === "string" && env.request_id || resp.headers.get("x-request-id") || void 0,
5912
+ detail: env.detail,
5913
+ retryable
5914
+ });
5915
+ }
5916
+ var TARGET_ORG_HEADER = "x-target-org-id";
5917
+ var nonAgencyKeys = /* @__PURE__ */ new Set();
5918
+ var warnedTargetOrgKeys = /* @__PURE__ */ new Set();
5919
+ function keyFingerprint(apiKey) {
5920
+ let h = 2166136261;
5921
+ for (let i = 0; i < apiKey.length; i++) {
5922
+ h ^= apiKey.charCodeAt(i);
5923
+ h = Math.imul(h, 16777619) >>> 0;
5924
+ }
5925
+ return `${apiKey.length}:${h.toString(36)}`;
5926
+ }
5927
+ function warnIfTargetOrgIgnored(apiKey, headers) {
5928
+ if (!Object.keys(headers).some((h) => h.toLowerCase() === TARGET_ORG_HEADER)) return;
5929
+ const fp = keyFingerprint(apiKey);
5930
+ if (!nonAgencyKeys.has(fp) || warnedTargetOrgKeys.has(fp)) return;
5931
+ warnedTargetOrgKeys.add(fp);
5932
+ if (typeof console !== "undefined" && typeof console.warn === "function") {
5933
+ console.warn(
5934
+ "[@graph8/sdk] X-Target-Org-Id is set, but the server reported this API key is not an agency key. The header is ignored for non-agency keys: this call runs against the key's own org. Use an agency key (minted with is_agency=true) to target a client org."
5935
+ );
5936
+ }
5937
+ }
5938
+ async function request(baseUrl, path11, apiKey, opts = {}) {
5939
+ const {
5940
+ method = "GET",
5941
+ body,
5942
+ headers = {},
5943
+ query,
5944
+ idempotencyKey,
5945
+ maxRetries = 2,
5946
+ retryBaseMs = 200,
5947
+ signal,
5948
+ fetchImpl = fetch,
5949
+ sleepImpl = defaultSleep
5950
+ } = opts;
5951
+ const url = baseUrl.replace(/\/+$/, "") + path11 + buildQuery(query);
5952
+ const finalHeaders = {
5953
+ "Content-Type": "application/json",
5954
+ Authorization: `Bearer ${apiKey}`,
5955
+ ...clientIdentityHeaders(headers),
5956
+ ...headers
5957
+ };
5958
+ warnIfTargetOrgIgnored(apiKey, headers);
5959
+ const effectiveIdempotencyKey = idempotencyKey ?? (maxRetries > 0 && isWriteMethod(method) ? newIdempotencyKey() : void 0);
5960
+ if (effectiveIdempotencyKey) finalHeaders["Idempotency-Key"] = effectiveIdempotencyKey;
5961
+ const billable = isNoBlindRetryOperation(method, url);
5962
+ let attempt = 0;
5963
+ for (; ; ) {
5964
+ let resp;
5965
+ try {
5966
+ resp = await fetchImpl(url, {
5967
+ method,
5968
+ headers: finalHeaders,
5969
+ body: body == null ? void 0 : JSON.stringify(body),
5970
+ signal
5971
+ });
5972
+ } catch (err) {
5973
+ const safeToRetry = !billable || isPreSendNetworkError(err);
5974
+ if (attempt < maxRetries && safeToRetry) {
5975
+ await sleepImpl(backoffDelayMs(attempt, retryBaseMs));
5976
+ attempt++;
5977
+ continue;
5978
+ }
5979
+ throw new G8Error({
5980
+ message: `Network error: ${err?.message ?? "request failed"}`,
5981
+ status: 0,
5982
+ type: "network_error",
5983
+ retryable: safeToRetry
5984
+ });
5985
+ }
5986
+ if (resp.ok) {
5987
+ const text = await resp.text();
5988
+ return text ? JSON.parse(text) : void 0;
5989
+ }
5990
+ const statusRetryable = isRetryableStatus(resp.status) && (!billable || resp.status === 429);
5991
+ if (statusRetryable && attempt < maxRetries) {
5992
+ const retryAfter = parseRetryAfter(resp.headers.get("retry-after"));
5993
+ const delay = retryAfter != null ? retryAfter : backoffDelayMs(attempt, retryBaseMs);
5994
+ await sleepImpl(delay);
5995
+ attempt++;
5996
+ continue;
5997
+ }
5998
+ throw await toG8Error(resp, billable ? statusRetryable : isRetryableStatus(resp.status));
5999
+ }
6000
+ }
6001
+ function isServerRuntime() {
6002
+ const g = globalThis;
6003
+ if (typeof g.window !== "undefined") return false;
6004
+ return typeof g.process?.versions?.node === "string";
6005
+ }
6006
+ function runtimeUserAgent() {
6007
+ const g = globalThis;
6008
+ const node = typeof g.process?.version === "string" ? ` node/${g.process.version.replace(/^v/, "")}` : "";
6009
+ return `graph8-sdk-js/${SDK_VERSION}${node}`;
6010
+ }
6011
+ function clientIdentityHeaders(callerHeaders = {}) {
6012
+ const taken = new Set(Object.keys(callerHeaders).map((h) => h.toLowerCase()));
6013
+ const out = {};
6014
+ if (!taken.has("x-g8-client")) out["X-G8-Client"] = SDK_CLIENT_ID;
6015
+ if (!taken.has("user-agent") && isServerRuntime()) out["User-Agent"] = runtimeUserAgent();
6016
+ return out;
6017
+ }
6018
+ async function* paginate(fetchPage) {
6019
+ let cursor = void 0;
6020
+ for (; ; ) {
6021
+ const page = await fetchPage(cursor);
6022
+ for (const item of page.data ?? []) yield item;
6023
+ const next = page.pagination?.next_cursor;
6024
+ if (!next) break;
6025
+ cursor = next;
6026
+ }
6027
+ }
5093
6028
 
5094
6029
  // src/api.ts
5095
6030
  var DEFAULT_API = "https://be.graph8.com";
@@ -5173,15 +6108,95 @@ function createApiClient(apiKey, apiUrl) {
5173
6108
  return client;
5174
6109
  }
5175
6110
 
6111
+ // src/blocks/flows.ts
6112
+ var import_node_fs7 = __toESM(require("fs"));
6113
+ var import_node_path8 = __toESM(require("path"));
6114
+ var IMPORT_RE2 = /(?:^|\n)\s*(?:import|export)\s[^'"`;]*?from\s*["']([^"']+)["']|(?:^|\n)\s*import\s*["']([^"']+)["']|import\(\s*["']([^"']+)["']\s*\)/g;
6115
+ var EXTENSIONS = ["", ".tsx", ".ts", "/index.tsx", "/index.ts"];
6116
+ function specifiers(source) {
6117
+ return [...source.matchAll(IMPORT_RE2)].map((m) => m[1] ?? m[2] ?? m[3]);
6118
+ }
6119
+ function resolveFile(base) {
6120
+ for (const ext of EXTENSIONS) {
6121
+ const candidate = base + ext;
6122
+ if (import_node_fs7.default.existsSync(candidate) && import_node_fs7.default.statSync(candidate).isFile()) return candidate;
6123
+ }
6124
+ return null;
6125
+ }
6126
+ function resolveImport(spec, from, reg) {
6127
+ if (spec.startsWith(".")) return resolveFile(import_node_path8.default.resolve(import_node_path8.default.dirname(from), spec));
6128
+ const g8 = /^@\/g8\/([^/]+)\/(.+)$/.exec(spec);
6129
+ if (!g8) return null;
6130
+ const owner = findBlock(reg, g8[1]);
6131
+ if (owner) return resolveFile(import_node_path8.default.join(owner.root, "components", g8[2])) ?? resolveFile(import_node_path8.default.join(owner.root, g8[2]));
6132
+ const foundation = findBlock(reg, "foundation");
6133
+ return foundation ? resolveFile(import_node_path8.default.join(foundation.root, g8[1], g8[2])) : null;
6134
+ }
6135
+ function resolveInstalled(spec, reg) {
6136
+ const m = /^@\/components\/g8\/([^/]+)\/(.+)$/.exec(spec);
6137
+ const block = m && reg.blocks.find((b) => b.dir === m[1]);
6138
+ if (!m || !block) return null;
6139
+ const files = [...block.components, ...block.hooks, ...block.lib].map((x) => x.file);
6140
+ const hit = files.find((f) => (installedRel(f) ?? f).replace(/\.(tsx|ts|jsx|js)$/, "") === m[2]);
6141
+ return hit ? import_node_path8.default.join(block.root, hit) : null;
6142
+ }
6143
+ function seeds(reg, stack) {
6144
+ const out = [];
6145
+ for (const n of stack.nav) {
6146
+ const comp = findBlock(reg, n.block)?.components.find((c) => c.name === n.component);
6147
+ const block = findBlock(reg, n.block);
6148
+ if (!block || !comp) continue;
6149
+ const pageFile = n.page && reg.stacksRoot ? import_node_path8.default.join(reg.stacksRoot, stack.id, n.page) : null;
6150
+ if (!pageFile || !import_node_fs7.default.existsSync(pageFile)) {
6151
+ out.push(import_node_path8.default.join(block.root, comp.file));
6152
+ continue;
6153
+ }
6154
+ const source = import_node_fs7.default.readFileSync(pageFile, "utf8");
6155
+ if (source.includes("{{importPath}}")) out.push(import_node_path8.default.join(block.root, comp.file));
6156
+ for (const spec of specifiers(source)) {
6157
+ const hit = resolveInstalled(spec, reg);
6158
+ if (hit) out.push(hit);
6159
+ }
6160
+ }
6161
+ return out;
6162
+ }
6163
+ function reachedFiles(reg, stack) {
6164
+ const seen = /* @__PURE__ */ new Set();
6165
+ const queue = seeds(reg, stack);
6166
+ while (queue.length) {
6167
+ const file = queue.pop();
6168
+ if (seen.has(file)) continue;
6169
+ seen.add(file);
6170
+ if (!/\.(tsx?|jsx?)$/.test(file)) continue;
6171
+ for (const spec of specifiers(import_node_fs7.default.readFileSync(file, "utf8"))) {
6172
+ const next = resolveImport(spec, file, reg);
6173
+ if (next && !seen.has(next)) queue.push(next);
6174
+ }
6175
+ }
6176
+ return seen;
6177
+ }
6178
+ function unreachedComponents(reg, stack) {
6179
+ const reached = reachedFiles(reg, stack);
6180
+ const blocks = resolveWithDeps(reg, stack.blocks).filter((b) => b.id !== "foundation" && stack.blocks.includes(b.id));
6181
+ return blocks.flatMap((b) => b.components.filter((c) => !reached.has(import_node_path8.default.join(b.root, c.file))).map((c) => ({ block: b.id, component: c.name })));
6182
+ }
6183
+
5176
6184
  // src/blocks/doctor.ts
5177
- var DOCTOR_USAGE = `Usage: npx @graph8/sdk doctor [--stack <id>] [--blocks a,b] [--dir <path>]
6185
+ var DOCTOR_USAGE = `Usage: npx @graph8/sdk doctor [--stack <id>] [--blocks a,b] [--dir <path>] [--strict] [--production] [--fix-prefixes]
5178
6186
 
5179
6187
  Check that this project is ready for graph8 blocks:
5180
6188
  - G8_API_KEY (env or .env.local) is valid
5181
6189
  - the key's scopes cover the blocks you chose
5182
6190
  - the organization's subscription is active
5183
6191
  - a checklist of in-app setup each block needs
6192
+ With --stack it also lists "Flows": block components that no page of the stack reaches
6193
+ (a warning; --strict makes them fail the run).
5184
6194
  With no --stack/--blocks, the blocks recorded in graph8.json are checked.
6195
+ It also reads the proxy route (app/api/g8/[...path]/route.ts) and warns about what is left before going live:
6196
+ --production fail instead: no \`authorize\` (local preview would be the only gate), no allowedPrefixes,
6197
+ or a dangerouslyAllow* switch on; and the key must come from the environment
6198
+ --fix-prefixes rewrite the route's generated allowedPrefixes from the installed blocks (graph8.json), then exit
6199
+ (no network). A route written before 0.367.0 is upgraded only if its list is still the default.
5185
6200
  Optional G8_API_URL points at a non-default API. Exit code 0 when all checks
5186
6201
  pass, 1 otherwise.`;
5187
6202
  function parseDoctorArgs(argv) {
@@ -5200,6 +6215,9 @@ ${DOCTOR_USAGE}`);
5200
6215
  if (flag === "--stack") o.stack = value();
5201
6216
  else if (flag === "--blocks") o.blocks.push(...value().split(",").map((s) => s.trim()).filter(Boolean));
5202
6217
  else if (flag === "--dir") o.dir = value();
6218
+ else if (arg === "--strict") o.strict = true;
6219
+ else if (arg === "--production") o.production = true;
6220
+ else if (arg === "--fix-prefixes") o.fixPrefixes = true;
5203
6221
  else throw new Error(`unknown option ${arg}
5204
6222
 
5205
6223
  ${DOCTOR_USAGE}`);
@@ -5218,9 +6236,9 @@ function parseEnvFile(text) {
5218
6236
  function readSetting(name, root, env) {
5219
6237
  if (env[name]) return env[name];
5220
6238
  for (const f of [".env.local"]) {
5221
- const file = import_node_path4.default.join(root, f);
5222
- if (import_node_fs4.default.existsSync(file)) {
5223
- const v = parseEnvFile(import_node_fs4.default.readFileSync(file, "utf8"))[name];
6239
+ const file = import_node_path9.default.join(root, f);
6240
+ if (import_node_fs8.default.existsSync(file)) {
6241
+ const v = parseEnvFile(import_node_fs8.default.readFileSync(file, "utf8"))[name];
5224
6242
  if (v) return v;
5225
6243
  }
5226
6244
  }
@@ -5241,7 +6259,7 @@ function selectBlocks(opts, reg, root) {
5241
6259
  }
5242
6260
  if (!ids.length) {
5243
6261
  try {
5244
- const ledger = JSON.parse(import_node_fs4.default.readFileSync(import_node_path4.default.join(root, "graph8.json"), "utf8"));
6262
+ const ledger = JSON.parse(import_node_fs8.default.readFileSync(import_node_path9.default.join(root, "graph8.json"), "utf8"));
5245
6263
  ids = Object.keys(ledger.blocks ?? {});
5246
6264
  } catch {
5247
6265
  ids = [];
@@ -5255,7 +6273,7 @@ function errorText(e, key) {
5255
6273
  return `${status ? `HTTP ${status}: ` : ""}${msg}`.split(key).join("[redacted]");
5256
6274
  }
5257
6275
  async function runDoctor(opts, reg, io, cwd) {
5258
- const root = import_node_path4.default.resolve(cwd, opts.dir);
6276
+ const root = import_node_path9.default.resolve(cwd, opts.dir);
5259
6277
  let blocks;
5260
6278
  try {
5261
6279
  blocks = selectBlocks(opts, reg, root);
@@ -5263,15 +6281,20 @@ async function runDoctor(opts, reg, io, cwd) {
5263
6281
  io.err(e.message);
5264
6282
  return 1;
5265
6283
  }
6284
+ if (opts.fixPrefixes) return fixPrefixes(root, blocks, io);
5266
6285
  let failed = false;
5267
6286
  const line = (ok, text) => {
5268
6287
  if (ok === false) failed = true;
5269
6288
  io.out(`${ok === null ? "info" : ok ? "ok " : "FAIL"} ${text}`);
5270
6289
  };
5271
6290
  io.out(blocks.length ? `Checking blocks: ${blocks.map((b) => b.id).join(", ")}` : "No blocks selected (use --stack or --blocks); checking the key and subscription only.");
5272
- const key = readSetting("G8_API_KEY", root, io.env);
6291
+ let key = readSetting("G8_API_KEY", root, io.env);
6292
+ if (!key && !opts.production) {
6293
+ key = readLocalKey(root);
6294
+ if (key) line(null, "using the key pasted in the Connect graph8 screen (.g8/local-key, local preview only)");
6295
+ }
5273
6296
  if (!key) {
5274
- line(false, "G8_API_KEY is not set (checked the environment and .env.local)");
6297
+ line(false, opts.production ? "G8_API_KEY is not set (production reads it from the environment only; checked the environment and .env.local)" : "G8_API_KEY is not set (checked the environment, .env.local and .g8/local-key)");
5275
6298
  } else {
5276
6299
  const api = createApiClient(key, readSetting("G8_API_URL", root, io.env));
5277
6300
  const call = (op) => api.call(op, void 0, {
@@ -5281,6 +6304,9 @@ async function runDoctor(opts, reg, io, cwd) {
5281
6304
  await checkKey(call, blocks, key, line);
5282
6305
  await checkSubscription(call, key, line);
5283
6306
  }
6307
+ checkRoute(root, blocks, opts.production === true, io.out, line);
6308
+ if (opts.stack) printFlows(opts, reg, io.out, line);
6309
+ else if (!opts.blocks.length) printAppScreens(root, reg, io.out);
5284
6310
  printChecklist(blocks, io.out);
5285
6311
  io.out(failed ? "\nSome checks failed." : "\nAll checks passed.");
5286
6312
  return failed ? 1 : 0;
@@ -5318,6 +6344,33 @@ async function checkSubscription(call, key, line) {
5318
6344
  else line(false, `could not read subscription status (${errorText(e, key)})`);
5319
6345
  }
5320
6346
  }
6347
+ function printFlows(opts, reg, out, line) {
6348
+ const stack = reg.stacks.find((s) => s.id === opts.stack);
6349
+ if (!stack) return;
6350
+ const missing = unreachedComponents(reg, stack);
6351
+ out("\nFlows (block components a page of this stack reaches):");
6352
+ if (!missing.length) return line(true, "every component of the stack's blocks is reachable from a page");
6353
+ for (const m of missing) {
6354
+ const text = `(${m.block}) ${m.component} is not reached by any page of "${stack.id}": wire it into a page or a Screen slot`;
6355
+ if (opts.strict) line(false, text);
6356
+ else out(`warn ${text}`);
6357
+ }
6358
+ }
6359
+ function printAppScreens(root, reg, out) {
6360
+ let app;
6361
+ try {
6362
+ app = JSON.parse(import_node_fs8.default.readFileSync(import_node_path9.default.join(root, "graph8.json"), "utf8")).app;
6363
+ } catch {
6364
+ return;
6365
+ }
6366
+ if (!app?.blocks?.length) return;
6367
+ const ids = app.blocks.filter((id) => reg.blocks.some((b) => b.id === id));
6368
+ out("\nScreens (what each block of this app gives its users):");
6369
+ for (const id of ids) {
6370
+ const block = reg.blocks.find((b) => b.id === id);
6371
+ if (!hasRealScreen(block)) out(`warn (${id}) has no finished Screen in this version: the app shows its plain ${composeApp(reg, [id], { noDepsScreens: true }).screens[0]?.component ?? "components"} page. A newer @graph8/sdk ships the finished screen.`);
6372
+ }
6373
+ }
5321
6374
  function printChecklist(blocks, out) {
5322
6375
  const items = blocks.flatMap((b) => b.prerequisites.filter((p) => p.where === "app").map((p) => ({ block: b.id, p })));
5323
6376
  if (!items.length) return;
@@ -5327,6 +6380,73 @@ function printChecklist(blocks, out) {
5327
6380
  if (p.verify) out(` verify: ${p.verify}`);
5328
6381
  }
5329
6382
  }
6383
+ function readLocalKey(root) {
6384
+ try {
6385
+ return import_node_fs8.default.readFileSync(import_node_path9.default.join(root, ".g8", "local-key"), "utf8").trim() || void 0;
6386
+ } catch {
6387
+ return void 0;
6388
+ }
6389
+ }
6390
+ var ROUTE_FILES = ["app/api/g8/[...path]/route.ts", "src/app/api/g8/[...path]/route.ts"];
6391
+ function findRoute(root) {
6392
+ return ROUTE_FILES.find((f) => import_node_fs8.default.existsSync(import_node_path9.default.join(root, f))) ?? null;
6393
+ }
6394
+ function checkRoute(root, blocks, production, out, line) {
6395
+ const rel = findRoute(root);
6396
+ if (!rel) {
6397
+ if (production) line(false, `no proxy route found (${ROUTE_FILES[0]}): run doctor from the app's root or pass --dir`);
6398
+ return;
6399
+ }
6400
+ const facts = inspectRoute(import_node_fs8.default.readFileSync(import_node_path9.default.join(root, rel), "utf8"));
6401
+ out(`
6402
+ Proxy route (${rel})${production ? ", production gate" : ""}:`);
6403
+ const problem = (text) => production ? line(false, text) : out(`warn ${text}`);
6404
+ if (!facts.authorize) {
6405
+ problem(
6406
+ facts.localPreview ? "`authorize` is not set: local preview (this computer, `next dev` only) is the only gate. Set `authorize` before going live; a production build refuses every call until you do" : "`authorize` is not set: the route refuses every call until you set it (check your app's session)"
6407
+ );
6408
+ } else line(true, "`authorize` is set");
6409
+ if (facts.dangerouslyAllowUnauthenticated) problem("dangerouslyAllowUnauthenticated is on: anyone who can reach the route can use the key");
6410
+ if (facts.dangerouslyAllowAllPaths) problem("dangerouslyAllowAllPaths is on: every /api/v1 area is reachable from the browser");
6411
+ const need = derivePrefixes(blocks);
6412
+ if (facts.prefixes === void 0) {
6413
+ problem(`allowedPrefixes is not set${need.length ? `: \`npx @graph8/sdk doctor --fix-prefixes\` writes the ${need.length} areas the blocks use` : ""}`);
6414
+ } else if (facts.prefixes === "custom") {
6415
+ out("info allowedPrefixes is not a literal list; it was not compared with the blocks");
6416
+ } else {
6417
+ const missing = need.filter((p) => !facts.prefixes.includes(p));
6418
+ const extra = facts.prefixes.filter((p) => !need.includes(p));
6419
+ if (!missing.length && !extra.length) line(true, `allowedPrefixes match the installed blocks (${need.length} areas)`);
6420
+ if (missing.length) out(`warn allowedPrefixes lacks areas the installed blocks call: ${missing.join(", ")} (\`npx @graph8/sdk doctor --fix-prefixes\`)`);
6421
+ if (extra.length && blocks.length) out(`warn allowedPrefixes allows areas no installed block declares: ${extra.join(", ")}`);
6422
+ }
6423
+ }
6424
+ function fixPrefixes(root, blocks, io) {
6425
+ const rel = findRoute(root);
6426
+ if (!rel) {
6427
+ io.err(`no proxy route found (${ROUTE_FILES.join(" or ")}). Run this in the app's root or pass --dir.`);
6428
+ return 1;
6429
+ }
6430
+ if (!blocks.length) {
6431
+ io.err("no blocks found: graph8.json lists none (or pass --blocks / --stack).");
6432
+ return 1;
6433
+ }
6434
+ const file = import_node_path9.default.join(root, rel);
6435
+ const source = import_node_fs8.default.readFileSync(file, "utf8");
6436
+ const prefixes = derivePrefixes(blocks);
6437
+ const next = applyPrefixes(source, prefixes) ?? upgradeLegacyRoute(source, prefixes);
6438
+ if (next === null) {
6439
+ io.err(`${rel} has no generated allowedPrefixes region and its list is not the default, so it is yours. The installed blocks need:`);
6440
+ io.err(`const allowedPrefixes = ${formatPrefixes(prefixes)};`);
6441
+ return 1;
6442
+ }
6443
+ if (next === source) io.out(`keep ${rel} (allowedPrefixes already match the ${prefixes.length} areas of the installed blocks)`);
6444
+ else {
6445
+ import_node_fs8.default.writeFileSync(file, next, "utf8");
6446
+ io.out(`update ${rel} (allowedPrefixes: ${prefixes.join(", ")})`);
6447
+ }
6448
+ return 0;
6449
+ }
5330
6450
 
5331
6451
  // src/cli.ts
5332
6452
  var LIST_USAGE = `Usage: npx @graph8/sdk list [--json]
@@ -5342,14 +6462,16 @@ Commands:
5342
6462
  list list the blocks and stacks this package ships
5343
6463
  search find a block by what it does
5344
6464
  add copy a block (components, hooks, recipes) into your app
5345
- create scaffold a Next.js app for a stack (create apollo my-app)
6465
+ create scaffold a Next.js app: from a stack (create apollo my-app) or from the blocks you pick
6466
+ (create --blocks crm,deals,inbox my-app)
5346
6467
  doctor check your key, scopes, subscription and setup for the blocks you use
6468
+ brand give an app its name, colour and logo, and white-label its graph8 links
5347
6469
 
5348
6470
  Run a command with --help for its options.
5349
6471
 
5350
6472
  ${INIT_AGENT_USAGE}`;
5351
6473
  function registryFor(io) {
5352
- const root = io.blocksRoot ?? process.env.G8_BLOCKS_DIR ?? import_node_path5.default.resolve(__dirname, "..", "blocks");
6474
+ const root = io.blocksRoot ?? process.env.G8_BLOCKS_DIR ?? import_node_path10.default.resolve(__dirname, "..", "blocks");
5353
6475
  return loadRegistry(root);
5354
6476
  }
5355
6477
  function defaultSpawn(cmd, args, cwd) {
@@ -5362,6 +6484,7 @@ function printCatalog(blocks, stacks, io, empty) {
5362
6484
  if (blocks.length) io.out("Blocks");
5363
6485
  for (const b of blocks) {
5364
6486
  io.out(` ${b.id} ${b.summary}`);
6487
+ if (b.screens?.length) io.out(` screens (start here): ${b.screens.join(", ")}`);
5365
6488
  if (b.dependsOn.length) io.out(` depends on: ${b.dependsOn.join(", ")}`);
5366
6489
  }
5367
6490
  if (stacks.length) io.out(`${blocks.length ? "\n" : ""}Stacks`);
@@ -5371,6 +6494,7 @@ function printCatalog(blocks, stacks, io, empty) {
5371
6494
  if (s.missingBlocks?.length) io.out(` planned (cannot be created yet), missing blocks: ${s.missingBlocks.join(", ")}`);
5372
6495
  }
5373
6496
  }
6497
+ var BUILD_HINT = "To build an app: npx @graph8/sdk create --blocks <ids> <dir> (every block's finished Screens become routes and sidebar entries)";
5374
6498
  function wantsHelp(rest) {
5375
6499
  return rest.includes("--help") || rest.includes("-h");
5376
6500
  }
@@ -5388,38 +6512,46 @@ ${LIST_USAGE}`);
5388
6512
  const blocks = reg.blocks.map(blockSummary);
5389
6513
  const stacks = reg.stacks.map((s) => stackSummary(s, reg));
5390
6514
  if (rest.includes("--json")) io.out(JSON.stringify({ blocks, stacks }, null, 2));
5391
- else printCatalog(blocks, stacks, io, "No blocks or stacks are included in this package version.");
6515
+ else {
6516
+ if (blocks.length) io.out(`${BUILD_HINT}
6517
+ `);
6518
+ printCatalog(blocks, stacks, io, "No blocks or stacks are included in this package version.");
6519
+ }
5392
6520
  return 0;
5393
6521
  }
5394
6522
  function runSearch(rest, io) {
5395
6523
  if (wantsHelp(rest)) return io.out(SEARCH_USAGE), 0;
5396
- const words = rest.filter((a) => a !== "--json");
5397
- const bad = words.find((a) => a.startsWith("-"));
6524
+ const words2 = rest.filter((a) => a !== "--json");
6525
+ const bad = words2.find((a) => a.startsWith("-"));
5398
6526
  if (bad) return fail(io, `unknown option ${bad}
5399
6527
 
5400
6528
  ${SEARCH_USAGE}`);
5401
- if (!words.length) return fail(io, `search needs some text
6529
+ if (!words2.length) return fail(io, `search needs some text
5402
6530
 
5403
6531
  ${SEARCH_USAGE}`);
5404
6532
  const reg = registryFor(io);
5405
- const text = words.join(" ");
6533
+ const text = words2.join(" ");
5406
6534
  const blocks = searchBlocks(reg, text).map(blockSummary);
5407
6535
  const stacks = searchStacks(reg, text).map((s) => stackSummary(s, reg));
5408
6536
  if (rest.includes("--json")) io.out(JSON.stringify({ query: text, blocks, stacks }, null, 2));
5409
- else printCatalog(blocks, stacks, io, `Nothing matches "${text}". Run \`graph8-sdk list\` to see everything.`);
6537
+ else {
6538
+ if (blocks.length) io.out(`${BUILD_HINT}
6539
+ `);
6540
+ printCatalog(blocks, stacks, io, `Nothing matches "${text}". Run \`graph8-sdk list\` to see everything.`);
6541
+ }
5410
6542
  return 0;
5411
6543
  }
5412
6544
  function runCreateCommand(rest, io) {
5413
6545
  if (wantsHelp(rest)) return io.out(CREATE_USAGE), 0;
5414
6546
  try {
5415
6547
  const opts = parseCreateArgs(rest);
5416
- const packageRoot = import_node_path5.default.resolve(__dirname, "..");
6548
+ const packageRoot = import_node_path10.default.resolve(__dirname, "..");
5417
6549
  const cio = {
5418
6550
  out: io.out,
5419
6551
  err: io.err,
5420
6552
  spawn: io.spawn ?? defaultSpawn,
5421
- templatesRoot: io.templatesRoot ?? process.env.G8_TEMPLATES_DIR ?? import_node_path5.default.join(packageRoot, "templates"),
5422
- sdkPackageJson: import_node_path5.default.join(packageRoot, "package.json"),
6553
+ templatesRoot: io.templatesRoot ?? process.env.G8_TEMPLATES_DIR ?? import_node_path10.default.join(packageRoot, "templates"),
6554
+ sdkPackageJson: import_node_path10.default.join(packageRoot, "package.json"),
5423
6555
  userAgent: io.userAgent ?? process.env.npm_config_user_agent
5424
6556
  };
5425
6557
  return runCreate(opts, registryFor(io), cio, io.cwd);
@@ -5431,7 +6563,15 @@ function runAddCommand(rest, io) {
5431
6563
  if (wantsHelp(rest)) return io.out(ADD_USAGE), 0;
5432
6564
  try {
5433
6565
  const opts = parseAddArgs(rest);
5434
- return runAdd(opts, registryFor(io), { out: io.out, err: io.err, spawn: io.spawn ?? defaultSpawn }, io.cwd);
6566
+ const packageRoot = import_node_path10.default.resolve(__dirname, "..");
6567
+ const aio = {
6568
+ out: io.out,
6569
+ err: io.err,
6570
+ spawn: io.spawn ?? defaultSpawn,
6571
+ templatesRoot: io.templatesRoot ?? process.env.G8_TEMPLATES_DIR ?? import_node_path10.default.join(packageRoot, "templates"),
6572
+ sdkPackageJson: import_node_path10.default.join(packageRoot, "package.json")
6573
+ };
6574
+ return runAdd(opts, registryFor(io), aio, io.cwd);
5435
6575
  } catch (e) {
5436
6576
  return fail(io, e.message);
5437
6577
  }
@@ -5446,6 +6586,14 @@ function runCli(argv, io) {
5446
6586
  if (command === "search") return runSearch(rest, io);
5447
6587
  if (command === "add") return runAddCommand(rest, io);
5448
6588
  if (command === "create") return runCreateCommand(rest, io);
6589
+ if (command === "brand") {
6590
+ if (wantsHelp(rest)) return io.out(BRAND_USAGE), 0;
6591
+ try {
6592
+ return runBrand(parseBrandArgs(rest), { out: io.out, err: io.err }, io.cwd);
6593
+ } catch (e) {
6594
+ return fail(io, e.message);
6595
+ }
6596
+ }
5449
6597
  if (command === "doctor") return fail(io, "doctor makes network calls; run it through runCliAsync (the graph8-sdk bin does).");
5450
6598
  if (command !== "init-agent") {
5451
6599
  io.err(`unknown command ${command}
@@ -5465,8 +6613,8 @@ ${USAGE}`);
5465
6613
  return 1;
5466
6614
  }
5467
6615
  const read = (rel) => {
5468
- const full = import_node_path5.default.join(io.cwd, rel);
5469
- return import_node_fs5.default.existsSync(full) ? import_node_fs5.default.readFileSync(full, "utf8") : null;
6616
+ const full = import_node_path10.default.join(io.cwd, rel);
6617
+ return import_node_fs9.default.existsSync(full) ? import_node_fs9.default.readFileSync(full, "utf8") : null;
5470
6618
  };
5471
6619
  let plans;
5472
6620
  try {
@@ -5477,9 +6625,9 @@ ${USAGE}`);
5477
6625
  }
5478
6626
  for (const plan of plans) {
5479
6627
  if (!options.dryRun && plan.action !== "unchanged") {
5480
- const full = import_node_path5.default.join(io.cwd, plan.path);
5481
- import_node_fs5.default.mkdirSync(import_node_path5.default.dirname(full), { recursive: true });
5482
- import_node_fs5.default.writeFileSync(full, plan.content, "utf8");
6628
+ const full = import_node_path10.default.join(io.cwd, plan.path);
6629
+ import_node_fs9.default.mkdirSync(import_node_path10.default.dirname(full), { recursive: true });
6630
+ import_node_fs9.default.writeFileSync(full, plan.content, "utf8");
5483
6631
  }
5484
6632
  io.out(`${options.dryRun ? "would " : ""}${plan.action === "unchanged" ? "keep" : plan.action} ${plan.path} (${plan.target})`);
5485
6633
  }