@graph8/sdk 0.373.0 → 0.374.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 (279) hide show
  1. package/README.md +3 -3
  2. package/blocks/_foundation/README.md +47 -0
  3. package/blocks/_foundation/block.json +13 -0
  4. package/blocks/_foundation/components/auth-gate.tsx +363 -0
  5. package/blocks/_foundation/server/auth.ts +815 -0
  6. package/blocks/campaigns/README.md +1 -1
  7. package/blocks/campaigns/block.json +34 -1
  8. package/blocks/campaigns/components/campaign-links.tsx +180 -0
  9. package/blocks/company-intelligence/components/research-run.tsx +6 -2
  10. package/blocks/crm/README.md +19 -0
  11. package/blocks/crm/block.json +205 -6
  12. package/blocks/crm/components/bulk-update.tsx +119 -0
  13. package/blocks/crm/components/contact-documents-tab.tsx +144 -0
  14. package/blocks/crm/components/contact-insights-tab.tsx +118 -0
  15. package/blocks/crm/components/contact-lookup-link.tsx +47 -6
  16. package/blocks/crm/components/contact-row-actions.tsx +43 -0
  17. package/blocks/crm/components/contact-tag-chips.tsx +113 -0
  18. package/blocks/crm/components/contacts-screen.tsx +46 -5
  19. package/blocks/crm/components/owner-assign.tsx +132 -0
  20. package/blocks/crm/components/record-open-links.tsx +79 -0
  21. package/blocks/crm/components/tag-assign.tsx +112 -0
  22. package/blocks/crm/components/tag-manager.tsx +178 -0
  23. package/blocks/crm/components/unlock-info-button.tsx +82 -0
  24. package/blocks/crm/components/verify-email-button.tsx +93 -0
  25. package/blocks/crm/hooks/use-contact-tags.ts +29 -0
  26. package/blocks/crm/lib/record-actions.ts +102 -0
  27. package/blocks/deals/README.md +1 -0
  28. package/blocks/deals/block.json +17 -0
  29. package/blocks/deals/components/create-deal-button.tsx +109 -0
  30. package/blocks/deals/components/deal-detail.tsx +5 -3
  31. package/blocks/dialer/components/call-log-screen.tsx +6 -1
  32. package/blocks/documents/README.md +86 -0
  33. package/blocks/documents/block.json +378 -0
  34. package/blocks/documents/components/account-bar.tsx +48 -0
  35. package/blocks/documents/components/correspondence-form.tsx +95 -0
  36. package/blocks/documents/components/correspondence-import.tsx +106 -0
  37. package/blocks/documents/components/correspondence-panel.tsx +191 -0
  38. package/blocks/documents/components/documents-screen.tsx +76 -0
  39. package/blocks/documents/components/library-document-detail.tsx +175 -0
  40. package/blocks/documents/components/library-document-form.tsx +105 -0
  41. package/blocks/documents/components/library-folder-form.tsx +74 -0
  42. package/blocks/documents/components/library-panel.tsx +235 -0
  43. package/blocks/documents/components/library-upload.tsx +160 -0
  44. package/blocks/documents/components/question-row.tsx +130 -0
  45. package/blocks/documents/components/questionnaire-detail.tsx +197 -0
  46. package/blocks/documents/components/questionnaires-panel.tsx +173 -0
  47. package/blocks/documents/components/record-documents-tab.tsx +109 -0
  48. package/blocks/documents/lib/documents-types.ts +252 -0
  49. package/blocks/documents/lib/download.ts +64 -0
  50. package/blocks/documents/recipes/follow-a-questionnaire.ts +26 -0
  51. package/blocks/documents/recipes/log-account-correspondence.ts +27 -0
  52. package/blocks/documents/recipes/organize-the-library.ts +25 -0
  53. package/blocks/email-signatures/README.md +123 -0
  54. package/blocks/email-signatures/block.json +341 -0
  55. package/blocks/email-signatures/components/email-signatures-screen.tsx +166 -0
  56. package/blocks/email-signatures/components/signature-assign-template.tsx +119 -0
  57. package/blocks/email-signatures/components/signature-block-editor.tsx +87 -0
  58. package/blocks/email-signatures/components/signature-campaign-form.tsx +165 -0
  59. package/blocks/email-signatures/components/signature-campaigns.tsx +75 -0
  60. package/blocks/email-signatures/components/signature-identity-form.tsx +131 -0
  61. package/blocks/email-signatures/components/signature-mailboxes.tsx +195 -0
  62. package/blocks/email-signatures/components/signature-policy.tsx +130 -0
  63. package/blocks/email-signatures/components/signature-preview.tsx +119 -0
  64. package/blocks/email-signatures/components/signature-report.tsx +77 -0
  65. package/blocks/email-signatures/components/signature-template-details.tsx +112 -0
  66. package/blocks/email-signatures/components/signature-template-form.tsx +109 -0
  67. package/blocks/email-signatures/hooks/use-signatures-data.ts +93 -0
  68. package/blocks/email-signatures/lib/signatures-types.ts +392 -0
  69. package/blocks/email-signatures/recipes/assign-a-signature-to-a-new-hire.ts +32 -0
  70. package/blocks/email-signatures/recipes/create-a-template-and-make-it-default.ts +35 -0
  71. package/blocks/email-signatures/recipes/signature-engagement-report.ts +16 -0
  72. package/blocks/enrichment/README.md +22 -4
  73. package/blocks/enrichment/block.json +145 -5
  74. package/blocks/enrichment/components/auto-enrichment-configs.tsx +134 -0
  75. package/blocks/enrichment/components/auto-enrichment-settings.tsx +82 -0
  76. package/blocks/enrichment/components/bulk-enrich-panel.tsx +7 -7
  77. package/blocks/enrichment/components/enrichment-activity.tsx +93 -0
  78. package/blocks/enrichment/components/enrichment-job-detail.tsx +18 -0
  79. package/blocks/enrichment/components/enrichment-screen.tsx +34 -3
  80. package/blocks/enrichment/components/list-pipelines.tsx +97 -0
  81. package/blocks/enrichment/components/pipeline-detail.tsx +214 -0
  82. package/blocks/enrichment/components/pipeline-editor.tsx +144 -0
  83. package/blocks/enrichment/components/pipeline-templates.tsx +90 -0
  84. package/blocks/enrichment/lib/enrichment-types.ts +12 -0
  85. package/blocks/enrichment/lib/pipeline-types.ts +234 -0
  86. package/blocks/forms/README.md +2 -0
  87. package/blocks/forms/components/forms-screen.tsx +6 -3
  88. package/blocks/inbox/README.md +40 -4
  89. package/blocks/inbox/block.json +130 -8
  90. package/blocks/inbox/components/blocked-senders.tsx +103 -0
  91. package/blocks/inbox/components/compose-message.tsx +193 -0
  92. package/blocks/inbox/components/inbox-screen.tsx +54 -6
  93. package/blocks/inbox/components/inbox-search.tsx +106 -0
  94. package/blocks/inbox/components/tag-manager.tsx +136 -0
  95. package/blocks/inbox/components/thread-actions.tsx +133 -0
  96. package/blocks/inbox/components/thread-view.tsx +2 -0
  97. package/blocks/inbox/hooks/use-inbox-mailboxes.ts +31 -0
  98. package/blocks/inbox/lib/inbox-manage.ts +227 -0
  99. package/blocks/newsletter/components/newsletter-screen.tsx +3 -1
  100. package/blocks/pages/block.json +10 -1
  101. package/blocks/pages/hooks/use-landing-page-paths.ts +60 -0
  102. package/blocks/programmatic-pages/README.md +82 -0
  103. package/blocks/programmatic-pages/block.json +292 -0
  104. package/blocks/programmatic-pages/components/programmatic-campaign-form.tsx +136 -0
  105. package/blocks/programmatic-pages/components/programmatic-generate.tsx +112 -0
  106. package/blocks/programmatic-pages/components/programmatic-generated-pages.tsx +210 -0
  107. package/blocks/programmatic-pages/components/programmatic-overview.tsx +84 -0
  108. package/blocks/programmatic-pages/components/programmatic-pages-screen.tsx +221 -0
  109. package/blocks/programmatic-pages/components/programmatic-preview.tsx +66 -0
  110. package/blocks/programmatic-pages/components/programmatic-publish.tsx +102 -0
  111. package/blocks/programmatic-pages/components/programmatic-settings.tsx +96 -0
  112. package/blocks/programmatic-pages/components/programmatic-source-fields.tsx +75 -0
  113. package/blocks/programmatic-pages/components/programmatic-source.tsx +106 -0
  114. package/blocks/programmatic-pages/components/programmatic-variables.tsx +84 -0
  115. package/blocks/programmatic-pages/hooks/use-programmatic-workspace.ts +19 -0
  116. package/blocks/programmatic-pages/lib/programmatic-types.ts +330 -0
  117. package/blocks/programmatic-pages/recipes/create-a-programmatic-campaign.ts +23 -0
  118. package/blocks/programmatic-pages/recipes/render-and-follow.ts +21 -0
  119. package/blocks/programmatic-pages/recipes/review-then-publish.ts +17 -0
  120. package/blocks/quotes/README.md +25 -1
  121. package/blocks/quotes/block.json +123 -6
  122. package/blocks/quotes/components/manual-products.tsx +144 -0
  123. package/blocks/quotes/components/quote-batch-actions.tsx +86 -0
  124. package/blocks/quotes/components/quote-documents.tsx +94 -0
  125. package/blocks/quotes/components/quote-evidence.tsx +95 -0
  126. package/blocks/quotes/components/quote-list.tsx +23 -5
  127. package/blocks/quotes/components/quote-settings.tsx +184 -0
  128. package/blocks/quotes/components/quote-templates.tsx +189 -0
  129. package/blocks/quotes/components/quote-viewer.tsx +16 -3
  130. package/blocks/quotes/components/quotes-screen.tsx +15 -5
  131. package/blocks/quotes/lib/quote-admin.ts +264 -0
  132. package/blocks/sequencer/README.md +55 -0
  133. package/blocks/sequencer/block.json +172 -5
  134. package/blocks/sequencer/components/enrollment-withdraw.tsx +71 -0
  135. package/blocks/sequencer/components/sequence-builder.tsx +13 -3
  136. package/blocks/sequencer/components/sequence-campaign-link.tsx +78 -0
  137. package/blocks/sequencer/components/sequence-detail.tsx +6 -0
  138. package/blocks/sequencer/components/sequence-edit-form.tsx +111 -0
  139. package/blocks/sequencer/components/sequence-enrollments.tsx +2 -1
  140. package/blocks/sequencer/components/sequence-manage-actions.tsx +119 -0
  141. package/blocks/sequencer/components/sequence-senders-panel.tsx +84 -0
  142. package/blocks/sequencer/components/sequence-templates.tsx +160 -0
  143. package/blocks/sequencer/components/sequences-screen.tsx +36 -6
  144. package/blocks/sequencer/components/step-ai-assist.tsx +196 -0
  145. package/blocks/sequencer/components/step-editor.tsx +10 -1
  146. package/blocks/sequencer/components/step-templates.tsx +224 -0
  147. package/blocks/sequencer/lib/sequencer-manage.ts +235 -0
  148. package/blocks/sequencer/lib/sequencer-utils.ts +4 -0
  149. package/blocks/signals/README.md +39 -0
  150. package/blocks/signals/block.json +287 -0
  151. package/blocks/signals/components/auto-seed-panel.tsx +111 -0
  152. package/blocks/signals/components/listener-form.tsx +184 -0
  153. package/blocks/signals/components/matched-posts.tsx +195 -0
  154. package/blocks/signals/components/post-engagements.tsx +114 -0
  155. package/blocks/signals/components/record-social-tab.tsx +49 -0
  156. package/blocks/signals/components/social-listeners-screen.tsx +250 -0
  157. package/blocks/signals/components/social-settings.tsx +128 -0
  158. package/blocks/signals/components/source-lists.tsx +83 -0
  159. package/blocks/signals/components/top-engagers.tsx +68 -0
  160. package/blocks/signals/components/x-integration.tsx +129 -0
  161. package/blocks/signals/lib/signals-types.ts +228 -0
  162. package/blocks/signals/recipes/preview-and-create-a-listener.ts +16 -0
  163. package/blocks/signals/recipes/spend-against-caps.ts +14 -0
  164. package/blocks/signals/recipes/triage-matched-posts.ts +18 -0
  165. package/blocks/visitors/components/traffic-chart.tsx +3 -2
  166. package/blocks/web-analytics/README.md +102 -0
  167. package/blocks/web-analytics/block.json +292 -0
  168. package/blocks/web-analytics/components/acquisition-tab.tsx +154 -0
  169. package/blocks/web-analytics/components/analytics-filters.tsx +161 -0
  170. package/blocks/web-analytics/components/audience-tab.tsx +135 -0
  171. package/blocks/web-analytics/components/conversions-tab.tsx +128 -0
  172. package/blocks/web-analytics/components/engagement-tab.tsx +123 -0
  173. package/blocks/web-analytics/components/overview-tab.tsx +102 -0
  174. package/blocks/web-analytics/components/pages-tab.tsx +148 -0
  175. package/blocks/web-analytics/components/ranked-table.tsx +103 -0
  176. package/blocks/web-analytics/components/realtime-tab.tsx +103 -0
  177. package/blocks/web-analytics/components/report-card.tsx +71 -0
  178. package/blocks/web-analytics/components/web-analytics-screen.tsx +90 -0
  179. package/blocks/web-analytics/hooks/use-report.ts +48 -0
  180. package/blocks/web-analytics/lib/analytics-scope.ts +155 -0
  181. package/blocks/web-analytics/lib/page-path.ts +14 -0
  182. package/blocks/web-analytics/lib/web-analytics-types.ts +187 -0
  183. package/blocks/web-analytics/recipes/best-landing-pages.ts +26 -0
  184. package/blocks/web-analytics/recipes/form-conversion-check.ts +19 -0
  185. package/blocks/web-analytics/recipes/weekly-traffic-report.ts +37 -0
  186. package/blocks/workbench/README.md +54 -2
  187. package/blocks/workbench/block.json +234 -5
  188. package/blocks/workbench/components/workbench-auto-enrich.tsx +88 -0
  189. package/blocks/workbench/components/workbench-automation.tsx +36 -0
  190. package/blocks/workbench/components/workbench-connections.tsx +136 -0
  191. package/blocks/workbench/components/workbench-crm-import.tsx +91 -0
  192. package/blocks/workbench/components/workbench-datafile-import.tsx +154 -0
  193. package/blocks/workbench/components/workbench-grid.tsx +70 -7
  194. package/blocks/workbench/components/workbench-pipelines.tsx +173 -0
  195. package/blocks/workbench/components/workbench-row-actions.tsx +8 -1
  196. package/blocks/workbench/components/workbench-schedule.tsx +191 -0
  197. package/blocks/workbench/components/workbench-screen.tsx +50 -17
  198. package/blocks/workbench/components/workbench-send-to-sequence.tsx +116 -0
  199. package/blocks/workbench/components/workbench-table-settings.tsx +53 -0
  200. package/blocks/workbench/components/workbench-triggers.tsx +178 -0
  201. package/blocks/workbench/hooks/use-connected-crms.ts +29 -0
  202. package/blocks/workbench/lib/api-status.ts +8 -0
  203. package/blocks/workbench/lib/automation-types.ts +257 -0
  204. package/dist/cli.js +257 -51
  205. package/dist/index.browser.js +1 -1
  206. package/dist/index.browser.js.map +1 -1
  207. package/dist/index.browser.mjs +1 -1
  208. package/dist/index.browser.mjs.map +1 -1
  209. package/dist/index.js +1 -1
  210. package/dist/index.js.map +1 -1
  211. package/dist/index.mjs +1 -1
  212. package/dist/index.mjs.map +1 -1
  213. package/dist/react.browser.js +1 -1
  214. package/dist/react.browser.js.map +1 -1
  215. package/dist/react.browser.mjs +1 -1
  216. package/dist/react.browser.mjs.map +1 -1
  217. package/dist/react.js +1 -1
  218. package/dist/react.js.map +1 -1
  219. package/dist/react.mjs +1 -1
  220. package/dist/react.mjs.map +1 -1
  221. package/dist/types/blocks/auth.d.ts +45 -0
  222. package/dist/types/blocks/create.d.ts +5 -2
  223. package/dist/types/version.d.ts +2 -2
  224. package/docs/api/forms.md +1 -1
  225. package/package.json +1 -1
  226. package/stacks/6sense/pages/contacts.tsx.tmpl +2 -0
  227. package/stacks/6sense.json +20 -0
  228. package/stacks/apollo/pages/calls.tsx.tmpl +9 -9
  229. package/stacks/apollo/pages/campaigns.tsx.tmpl +27 -0
  230. package/stacks/apollo/pages/contacts.tsx.tmpl +6 -0
  231. package/stacks/apollo/pages/inbox-contact.tsx.tmpl +3 -3
  232. package/stacks/apollo/pages/lists.tsx.tmpl +14 -2
  233. package/stacks/apollo/pages/sequences.tsx.tmpl +8 -2
  234. package/stacks/apollo.json +33 -2
  235. package/stacks/clay/pages/contacts.tsx.tmpl +2 -0
  236. package/stacks/clay/pages/lists.tsx.tmpl +14 -2
  237. package/stacks/gong/pages/calls.tsx.tmpl +9 -9
  238. package/stacks/gong/pages/contacts.tsx.tmpl +2 -0
  239. package/stacks/hootsuite/pages/contacts.tsx.tmpl +2 -0
  240. package/stacks/hootsuite.json +10 -0
  241. package/stacks/hubspot/pages/campaigns.tsx.tmpl +30 -0
  242. package/stacks/hubspot/pages/companies.tsx.tmpl +2 -0
  243. package/stacks/hubspot/pages/contacts.tsx.tmpl +6 -0
  244. package/stacks/hubspot/pages/deals.tsx.tmpl +4 -0
  245. package/stacks/hubspot/pages/forms.tsx.tmpl +36 -0
  246. package/stacks/hubspot/pages/inbox.tsx.tmpl +2 -2
  247. package/stacks/hubspot/pages/sequences.tsx.tmpl +8 -2
  248. package/stacks/hubspot/pages/web-analytics.tsx.tmpl +14 -0
  249. package/stacks/hubspot.json +45 -2
  250. package/stacks/intercom/pages/inbox-contact.tsx.tmpl +3 -3
  251. package/stacks/intercom.json +10 -0
  252. package/stacks/mailchimp/pages/contacts.tsx.tmpl +2 -0
  253. package/stacks/mailchimp/pages/forms.tsx.tmpl +36 -0
  254. package/stacks/mailchimp.json +2 -1
  255. package/stacks/outreach/pages/calls.tsx.tmpl +9 -9
  256. package/stacks/outreach/pages/campaigns.tsx.tmpl +27 -0
  257. package/stacks/outreach/pages/contacts.tsx.tmpl +2 -0
  258. package/stacks/outreach/pages/inbox-contact.tsx.tmpl +3 -3
  259. package/stacks/outreach/pages/sequences.tsx.tmpl +8 -2
  260. package/stacks/outreach.json +13 -2
  261. package/stacks/pipedrive/pages/contacts.tsx.tmpl +2 -0
  262. package/stacks/pipedrive/pages/inbox.tsx.tmpl +2 -2
  263. package/stacks/pipedrive.json +10 -0
  264. package/stacks/salesforce/pages/companies.tsx.tmpl +2 -0
  265. package/stacks/salesforce/pages/contacts.tsx.tmpl +6 -0
  266. package/stacks/salesforce/pages/deals.tsx.tmpl +4 -0
  267. package/stacks/salesforce.json +10 -0
  268. package/stacks/zoominfo/pages/contacts.tsx.tmpl +4 -0
  269. package/stacks/zoominfo/pages/lists.tsx.tmpl +14 -2
  270. package/stacks/zoominfo.json +20 -0
  271. package/styles/styles.css +1 -1
  272. package/templates/auth/auth-route.ts.tmpl +16 -0
  273. package/templates/auth/g8.auth.anonymous.ts.tmpl +15 -0
  274. package/templates/auth/g8.auth.basic.ts.tmpl +17 -0
  275. package/templates/auth/g8.auth.jwt.ts.tmpl +23 -0
  276. package/templates/auth/g8.auth.password.ts.tmpl +18 -0
  277. package/templates/auth/g8.auth.session.ts.tmpl +23 -0
  278. package/templates/auth/middleware.ts.tmpl +14 -0
  279. package/templates/next-app/README.md.tmpl +5 -0
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_fs10 = __toESM(require("fs"));
40
- var import_node_path11 = __toESM(require("path"));
39
+ var import_node_fs11 = __toESM(require("fs"));
40
+ var import_node_path12 = __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, path12, next) => {
123
- const current = read(path12);
124
- plans.push({ target, path: path12, content: next, action: current === null ? "create" : current === next ? "unchanged" : "update" });
122
+ const add = (target, path13, next) => {
123
+ const current = read(path13);
124
+ plans.push({ target, path: path13, 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));
@@ -158,7 +158,7 @@ var import_node_fs4 = __toESM(require("fs"));
158
158
  var import_node_path5 = __toESM(require("path"));
159
159
 
160
160
  // src/version.ts
161
- var SDK_VERSION = "0.373.0";
161
+ var SDK_VERSION = "0.374.0";
162
162
  var SDK_CLIENT_ID = `js-sdk/${SDK_VERSION}`;
163
163
 
164
164
  // src/blocks/paths.ts
@@ -5618,7 +5618,7 @@ function warnIfTargetOrgIgnored(apiKey, headers) {
5618
5618
  );
5619
5619
  }
5620
5620
  }
5621
- async function request(baseUrl, path12, apiKey, opts = {}) {
5621
+ async function request(baseUrl, path13, apiKey, opts = {}) {
5622
5622
  const {
5623
5623
  method = "GET",
5624
5624
  body,
@@ -5631,7 +5631,7 @@ async function request(baseUrl, path12, apiKey, opts = {}) {
5631
5631
  fetchImpl = fetch,
5632
5632
  sleepImpl = defaultSleep
5633
5633
  } = opts;
5634
- const url = baseUrl.replace(/\/+$/, "") + path12 + buildQuery(query);
5634
+ const url = baseUrl.replace(/\/+$/, "") + path13 + buildQuery(query);
5635
5635
  const finalHeaders = {
5636
5636
  "Content-Type": "application/json",
5637
5637
  Authorization: `Bearer ${apiKey}`,
@@ -5992,7 +5992,7 @@ async function runDoctor(opts, reg, io, cwd) {
5992
5992
  await checkKey(call, blocks, key, line);
5993
5993
  await checkSubscription(call, key, line);
5994
5994
  }
5995
- checkRoute(root, blocks, opts.production === true, io.out, line);
5995
+ checkRoute(root, blocks, opts.production === true, io.out, line, io.env);
5996
5996
  if (opts.stack) printFlows(opts, reg, io.out, line);
5997
5997
  else if (!opts.blocks.length) printAppScreens(root, reg, io.out);
5998
5998
  printChecklist(blocks, io.out);
@@ -6079,7 +6079,7 @@ var ROUTE_FILES = ["app/api/g8/[...path]/route.ts", "src/app/api/g8/[...path]/ro
6079
6079
  function findRoute(root) {
6080
6080
  return ROUTE_FILES.find((f) => import_node_fs7.default.existsSync(import_node_path8.default.join(root, f))) ?? null;
6081
6081
  }
6082
- function checkRoute(root, blocks, production, out, line) {
6082
+ function checkRoute(root, blocks, production, out, line, env = {}) {
6083
6083
  const rel = findRoute(root);
6084
6084
  if (!rel) {
6085
6085
  if (production) line(false, `no proxy route found (${ROUTE_FILES[0]}): run doctor from the app's root or pass --dir`);
@@ -6094,6 +6094,7 @@ Proxy route (${rel})${production ? ", production gate" : ""}:`);
6094
6094
  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)"
6095
6095
  );
6096
6096
  } else line(true, "`authorize` is set");
6097
+ checkAuthConfig(root, problem, out, env);
6097
6098
  if (facts.dangerouslyAllowUnauthenticated) problem("dangerouslyAllowUnauthenticated is on: anyone who can reach the route can use the key");
6098
6099
  if (facts.dangerouslyAllowAllPaths) problem("dangerouslyAllowAllPaths is on: every /api/v1 area is reachable from the browser");
6099
6100
  const need = derivePrefixes(blocks);
@@ -6109,6 +6110,21 @@ Proxy route (${rel})${production ? ", production gate" : ""}:`);
6109
6110
  if (extra.length && blocks.length) out(`warn allowedPrefixes allows areas no installed block declares: ${extra.join(", ")}`);
6110
6111
  }
6111
6112
  }
6113
+ function checkAuthConfig(root, problem, out, env = {}) {
6114
+ const rel = ["g8.auth.ts", "src/g8.auth.ts"].find((f) => import_node_fs7.default.existsSync(import_node_path8.default.join(root, f)));
6115
+ if (!rel) return;
6116
+ const source = import_node_fs7.default.readFileSync(import_node_path8.default.join(root, rel), "utf8");
6117
+ const code = source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:"'`])\/\/[^\n]*/g, "$1");
6118
+ if (/\ballowAnonymous\s*\(/.test(code)) {
6119
+ out(`warn ${rel} uses allowAnonymous(): NO sign-in. Anyone who can reach this app can use its graph8 API key through /api/g8. Use a narrowly scoped key with a spend cap, or switch to a sign-in preset`);
6120
+ }
6121
+ if (/\bjwtAuth\s*\(/.test(code) && !/\bclaims\s*:/.test(code) && !/\b(audience|secret)\s*:/.test(code)) {
6122
+ if (!readSetting("G8_JWT_AUDIENCE", root, env) && !readSetting("G8_JWT_SECRET", root, env)) {
6123
+ out(`warn ${rel} uses jwtAuth() with no audience: set G8_JWT_AUDIENCE (or a claims check), otherwise tokens your identity provider minted for other apps on the same tenant are accepted`);
6124
+ }
6125
+ }
6126
+ if (source.includes("graph8:auth-todo")) problem(`${rel} still has the sessionAuth stub (graph8:auth-todo): it refuses everyone until you wire your session library`);
6127
+ }
6112
6128
  function fixPrefixes(root, blocks, io) {
6113
6129
  const rel = findRoute(root);
6114
6130
  if (!rel) {
@@ -6225,8 +6241,142 @@ async function pullOrgBrand(io, root) {
6225
6241
  }
6226
6242
 
6227
6243
  // src/blocks/create.ts
6244
+ var import_node_fs10 = __toESM(require("fs"));
6245
+ var import_node_path11 = __toESM(require("path"));
6246
+
6247
+ // src/blocks/auth.ts
6248
+ var import_node_crypto2 = require("crypto");
6228
6249
  var import_node_fs9 = __toESM(require("fs"));
6229
6250
  var import_node_path10 = __toESM(require("path"));
6251
+ var AUTH_PRESETS = ["password", "basic", "jwt", "session", "anonymous"];
6252
+ var AUTH_CONFIG_FILE = "g8.auth.ts";
6253
+ var AUTH_ROUTE_FILE = "app/api/g8-auth/route.ts";
6254
+ function isAuthPreset(v) {
6255
+ return AUTH_PRESETS.includes(v);
6256
+ }
6257
+ function usesSignInScreen(p) {
6258
+ return p === "password" || p === "jwt" || p === "session";
6259
+ }
6260
+ var SUMMARY = {
6261
+ password: "one shared team password, a signed HttpOnly session cookie and a sign-in screen",
6262
+ basic: "HTTP Basic auth: the browser asks for a user and password on every page",
6263
+ jwt: "your identity provider's JWT (OIDC: Auth0, Okta, Cognito, Keycloak, Clerk...), checked against its keys",
6264
+ session: "your own session library (Clerk, NextAuth, Supabase...): wire one function in g8.auth.ts",
6265
+ anonymous: "NO sign-in: anyone who can reach the app can use its key (public demos only)"
6266
+ };
6267
+ function authSummary(p) {
6268
+ return SUMMARY[p];
6269
+ }
6270
+ var ENV = {
6271
+ password: [
6272
+ "# Sign-in (create --auth password): the team password (12+ characters) and the cookie signing secret",
6273
+ "# (32+ random characters: openssl rand -base64 48). create wrote fresh values to .env.local.",
6274
+ "G8_APP_PASSWORD=",
6275
+ "G8_SESSION_SECRET="
6276
+ ],
6277
+ basic: ["# Sign-in (create --auth basic): user:password (12+ character passwords, no commas), comma-separated for several users.", "G8_BASIC_AUTH="],
6278
+ jwt: [
6279
+ "# Sign-in (create --auth jwt): your identity provider. See g8.auth.ts for examples.",
6280
+ "G8_JWT_ISSUER=",
6281
+ "G8_JWT_JWKS_URL=",
6282
+ "G8_JWT_AUDIENCE=",
6283
+ "G8_JWT_COOKIE=",
6284
+ "# Or an HS256 secret instead of a JWKS (32+ characters):",
6285
+ "G8_JWT_SECRET=",
6286
+ "G8_SIGN_IN_URL=",
6287
+ "G8_SIGN_OUT_URL="
6288
+ ],
6289
+ session: ["# Sign-in (create --auth session): where the sign-in screen sends people, and the Sign out link.", "G8_SIGN_IN_URL=", "G8_SIGN_OUT_URL="],
6290
+ anonymous: []
6291
+ };
6292
+ function authEnvLines(p) {
6293
+ return ENV[p];
6294
+ }
6295
+ function planAuthFiles(p, templatesRoot) {
6296
+ const dir = import_node_path10.default.join(templatesRoot, "auth");
6297
+ const read = (f) => render(import_node_fs9.default.readFileSync(import_node_path10.default.join(dir, f), "utf8"), { preset: p });
6298
+ const files = [{ rel: AUTH_CONFIG_FILE, content: read(`g8.auth.${p}.ts.tmpl`) }];
6299
+ if (usesSignInScreen(p)) files.push({ rel: AUTH_ROUTE_FILE, content: read("auth-route.ts.tmpl") });
6300
+ if (p === "basic") files.push({ rel: "middleware.ts", content: read("middleware.ts.tmpl") });
6301
+ return files;
6302
+ }
6303
+ var AUTHORIZE_HINT = /^[ \t]*\/\/ authorize: async \(request\) => \{[^\n]*\n/m;
6304
+ var LAST_IMPORT = /^import [^\n]+;\n(?![\s\S]*^import )/m;
6305
+ function applyAuthToRoute(source) {
6306
+ if (!AUTHORIZE_HINT.test(source) || !LAST_IMPORT.test(source) || source.includes('from "@/g8.auth"')) return null;
6307
+ return source.replace(LAST_IMPORT, (m) => `${m}import { appAuth } from "@/g8.auth";
6308
+ `).replace(AUTHORIZE_HINT, " authorize: appAuth.authorize, // g8.auth.ts: who may use this route (npx @graph8/sdk create --auth)\n");
6309
+ }
6310
+ var LAYOUT_IMPORT = 'import { G8ConnectGate } from "@/components/g8/_foundation/connect-graph8";\n';
6311
+ var LAYOUT_BODY = /^([ \t]*)<AppShell>\n([\s\S]*?)^\1<\/AppShell>\n/m;
6312
+ function applyAuthToLayout(source) {
6313
+ const m = LAYOUT_BODY.exec(source);
6314
+ if (!m || !source.includes(LAYOUT_IMPORT)) return null;
6315
+ const pad = m[1];
6316
+ const inner = m[2].split("\n").map((l) => l ? ` ${l}` : l).join("\n");
6317
+ const body = [
6318
+ `${pad}{/* Sign-in (g8.auth.ts): the sign-in screen until the visitor is signed in. The proxy enforces it on the server. */}`,
6319
+ `${pad}<BrandProvider brand={brand}>`,
6320
+ `${pad} <G8AuthGate>`,
6321
+ `${pad} <AppShell headerSlot={<G8SignOutButton />}>`,
6322
+ `${inner}${pad} </AppShell>`,
6323
+ `${pad} </G8AuthGate>`,
6324
+ `${pad}</BrandProvider>`,
6325
+ ""
6326
+ ].join("\n");
6327
+ const imports = `${LAYOUT_IMPORT}import { G8AuthGate, G8SignOutButton } from "@/components/g8/_foundation/auth-gate";
6328
+ import { BrandProvider } from "@/components/g8/_foundation/brand-provider";
6329
+ `;
6330
+ return source.replace(LAYOUT_IMPORT, imports).replace(LAYOUT_BODY, body);
6331
+ }
6332
+ var GO_LIVE_ITEM = /^- \[ \] Set `authorize` in `app\/api\/g8\/\[\.\.\.path\]\/route\.ts`[^\n]*\n(?: {6}[^\n]*\n)*/m;
6333
+ function readmeSection(p) {
6334
+ const vars2 = authEnvLines(p).filter((l) => !l.startsWith("#")).map((l) => `\`${l.replace(/=$/, "")}\``);
6335
+ const lines = [
6336
+ "## Sign-in",
6337
+ "",
6338
+ `This app was created with \`--auth ${p}\`: ${authSummary(p)}. \`g8.auth.ts\` holds the preset, and`,
6339
+ "`app/api/g8/[...path]/route.ts` runs its `authorize` before it forwards any call with the server's graph8 API key.",
6340
+ ""
6341
+ ];
6342
+ if (vars2.length) lines.push(`Server environment (\`.env.local\` while developing, your host's settings in production): ${vars2.join(", ")}.`, "");
6343
+ if (p === "password") lines.push("`create` wrote a fresh team password and session secret to `.env.local` (git-ignored): read the password there, and set your own values on the host before deploying.", "");
6344
+ if (p === "basic") lines.push("`create` wrote a fresh `admin:<random>` login to `.env.local` (git-ignored). `middleware.ts` makes the browser ask for it on every page. Serve the app over HTTPS only.", "");
6345
+ if (p === "jwt") lines.push("Set the issuer (or JWKS URL), the audience and the cookie your provider uses; examples are in `g8.auth.ts`.", "");
6346
+ if (p === "session") lines.push("Wire your session library in `g8.auth.ts` (Clerk, NextAuth and Supabase examples inside). Until then it refuses everyone and `doctor --production` fails.", "");
6347
+ if (p === "anonymous") lines.push("**Warning: no sign-in.** Anyone who can reach the app can use its key through `/api/g8`. Use a narrowly scoped key with a spend cap, or switch preset in `g8.auth.ts`.", "");
6348
+ lines.push('Switch preset or tune it (session length, extra JWT claim checks): `components/g8/_foundation/README.md`, "Sign-in presets".', "", "");
6349
+ return lines.join("\n");
6350
+ }
6351
+ function applyAuthToReadme(source, p) {
6352
+ if (!source.includes("\n## Go live\n") || !GO_LIVE_ITEM.test(source)) return null;
6353
+ const item = p === "anonymous" ? "- [ ] `authorize` is set by `--auth anonymous`: NO sign-in. Make sure that is what you want (see Sign-in above).\n" : `- [x] \`authorize\` is set by the \`--auth ${p}\` preset (\`g8.auth.ts\`). Set its variables on your host (see Sign-in above).
6354
+ `;
6355
+ return source.replace("\n## Go live\n", `
6356
+ ${readmeSection(p)}## Go live
6357
+ `).replace(GO_LIVE_ITEM, item);
6358
+ }
6359
+ function applyAuthToEnvExample(source, p) {
6360
+ const lines = authEnvLines(p);
6361
+ return lines.length ? `${source.replace(/\n*$/, "\n")}${lines.join("\n")}
6362
+ ` : source;
6363
+ }
6364
+ var secret = (bytes) => (0, import_node_crypto2.randomBytes)(bytes).toString("base64url");
6365
+ function writeAuthSecrets(root, p) {
6366
+ const wanted = p === "password" ? { G8_APP_PASSWORD: secret(18), G8_SESSION_SECRET: secret(48) } : p === "basic" ? { G8_BASIC_AUTH: `admin:${secret(18)}` } : {};
6367
+ const file = import_node_path10.default.join(root, ".env.local");
6368
+ const current = import_node_fs9.default.existsSync(file) ? import_node_fs9.default.readFileSync(file, "utf8") : "";
6369
+ const has = (name) => new RegExp(`^\\s*(?:export\\s+)?${name}\\s*=\\s*\\S`, "m").test(current);
6370
+ const add = Object.entries(wanted).filter(([name]) => !has(name));
6371
+ if (!add.length) return [];
6372
+ const block = [`# Written by \`npx @graph8/sdk create --auth ${p}\` for local development. Set your own values on your host.`, ...add.map(([k, v]) => `${k}=${v}`)].join("\n");
6373
+ import_node_fs9.default.writeFileSync(file, `${current ? `${current.replace(/\n*$/, "\n")}
6374
+ ` : ""}${block}
6375
+ `, { encoding: "utf8", mode: 384 });
6376
+ return add.map(([k]) => k);
6377
+ }
6378
+
6379
+ // src/blocks/create.ts
6230
6380
  var CREATE_USAGE = `Usage: npx @graph8/sdk create <stack> <dir> [options]
6231
6381
 
6232
6382
  Scaffold a Next.js (App Router, TypeScript, Tailwind CSS v4) app for a stack and
@@ -6236,6 +6386,14 @@ install the stack's blocks into it. Run \`graph8-sdk list\` to see the stacks.
6236
6386
  --dry-run print what would be written, write and install nothing
6237
6387
  --force allow a non-empty <dir> (template files are overwritten, and graph8.json
6238
6388
  is reset: blocks you added to that app earlier are no longer recorded)
6389
+ --auth <preset> scaffold sign-in so the app can go live (sets \`authorize\` on the proxy route):
6390
+ password one shared team password, session cookie, sign-in screen
6391
+ basic HTTP Basic auth on every page (the browser asks)
6392
+ jwt your identity provider's JWT (OIDC JWKS or an HS256 secret)
6393
+ session your own session library (Clerk, NextAuth, Supabase): wire g8.auth.ts
6394
+ anonymous NO sign-in: anyone who can reach the app uses its key (public demos)
6395
+ Without --auth: local preview only, and a production build refuses /api/g8 until you
6396
+ set \`authorize\` yourself (unchanged).
6239
6397
  Then: \`npm run dev\` in <dir>, open http://localhost:3000 and paste a graph8 API key (local preview), and run
6240
6398
  \`npx @graph8/sdk doctor --stack <stack>\` (add --production before going live).
6241
6399
 
@@ -6278,6 +6436,12 @@ ${CREATE_USAGE}`);
6278
6436
 
6279
6437
  ${CREATE_USAGE}`);
6280
6438
  o.blocks = [...o.blocks ?? [], ...v.split(",").map((x) => x.trim()).filter(Boolean)];
6439
+ } else if (flag === "--auth") {
6440
+ const v = inline ?? argv[++i];
6441
+ if (v === void 0 || !isAuthPreset(v)) throw new Error(`--auth must be one of ${AUTH_PRESETS.join(", ")}
6442
+
6443
+ ${CREATE_USAGE}`);
6444
+ o.auth = v;
6281
6445
  } else if (flag === "--title") {
6282
6446
  const v = inline ?? argv[++i];
6283
6447
  if (v === void 0 || v.startsWith("--")) throw new Error(`--title needs a value
@@ -6333,23 +6497,23 @@ function packageManagerFromUserAgent(ua) {
6333
6497
  return name ?? "npm";
6334
6498
  }
6335
6499
  function appNameFor(dir) {
6336
- const base = import_node_path10.default.basename(import_node_path10.default.resolve(dir)).toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[._-]+|-+$/g, "");
6500
+ const base = import_node_path11.default.basename(import_node_path11.default.resolve(dir)).toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[._-]+|-+$/g, "");
6337
6501
  return base || "g8-app";
6338
6502
  }
6339
6503
  var quoted = (p) => /^[\w@%+=:,./-]+$/.test(p) ? p : JSON.stringify(p);
6340
6504
  function walk(dir, base = dir) {
6341
6505
  const out = [];
6342
- for (const e of import_node_fs9.default.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
6343
- const full = import_node_path10.default.join(dir, e.name);
6506
+ for (const e of import_node_fs10.default.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
6507
+ const full = import_node_path11.default.join(dir, e.name);
6344
6508
  if (e.isDirectory()) out.push(...walk(full, base));
6345
- else out.push(posix(import_node_path10.default.relative(base, full)));
6509
+ else out.push(posix(import_node_path11.default.relative(base, full)));
6346
6510
  }
6347
6511
  return out;
6348
6512
  }
6349
6513
  function outputName(rel) {
6350
6514
  const noExt = rel.replace(/\.tmpl$/, "");
6351
- const dir = import_node_path10.default.posix.dirname(noExt);
6352
- const base = import_node_path10.default.posix.basename(noExt).replace(/^dot-/, ".");
6515
+ const dir = import_node_path11.default.posix.dirname(noExt);
6516
+ const base = import_node_path11.default.posix.basename(noExt).replace(/^dot-/, ".");
6353
6517
  return dir === "." ? base : `${dir}/${base}`;
6354
6518
  }
6355
6519
  function stackProblem(reg, stack) {
@@ -6371,7 +6535,7 @@ function stackProblem(reg, stack) {
6371
6535
  if (/[\[\]]/.test(n.path)) return `stack "${stack.id}" nav "${n.label}" uses ${n.path}: dynamic segments cannot be a sidebar link.`;
6372
6536
  if (seen.has(n.path)) return `stack "${stack.id}" has two nav entries at ${n.path}.`;
6373
6537
  seen.add(n.path);
6374
- if (n.page && (!reg.stacksRoot || !import_node_fs9.default.existsSync(import_node_path10.default.join(reg.stacksRoot, stack.id, n.page)))) {
6538
+ if (n.page && (!reg.stacksRoot || !import_node_fs10.default.existsSync(import_node_path11.default.join(reg.stacksRoot, stack.id, n.page)))) {
6375
6539
  return `stack "${stack.id}" nav "${n.label}" uses page ${n.page}, which does not exist under stacks/${stack.id}/.`;
6376
6540
  }
6377
6541
  }
@@ -6384,14 +6548,14 @@ function homeProblem(stack) {
6384
6548
  return null;
6385
6549
  }
6386
6550
  function stackPageFile(reg, stack, n) {
6387
- return n.page && reg.stacksRoot ? import_node_path10.default.join(reg.stacksRoot, stack.id, n.page) : null;
6551
+ return n.page && reg.stacksRoot ? import_node_path11.default.join(reg.stacksRoot, stack.id, n.page) : null;
6388
6552
  }
6389
6553
  function homeRedirect(stack) {
6390
6554
  if (stack.home) return stack.home === "/" ? null : stack.home;
6391
6555
  return stack.nav.length && !stack.nav.some((n) => n.path === "/") ? stack.nav[0].path : null;
6392
6556
  }
6393
6557
  function renderPages(reg, stack, templates) {
6394
- const pageTpl = import_node_fs9.default.readFileSync(import_node_path10.default.join(templates, "_pages", "route-page.tsx.tmpl"), "utf8");
6558
+ const pageTpl = import_node_fs10.default.readFileSync(import_node_path11.default.join(templates, "_pages", "route-page.tsx.tmpl"), "utf8");
6395
6559
  const files = stack.nav.map((n) => {
6396
6560
  const block = findBlock(reg, n.block);
6397
6561
  const comp = block.components.find((c) => c.name === n.component);
@@ -6404,11 +6568,11 @@ function renderPages(reg, stack, templates) {
6404
6568
  importPath: componentImportPath(block, comp.file),
6405
6569
  propsJson: JSON.stringify(n.props ?? {}, null, 2)
6406
6570
  };
6407
- return { rel: pageFileFor(n.path), content: render(custom ? import_node_fs9.default.readFileSync(custom, "utf8") : pageTpl, vars2) };
6571
+ return { rel: pageFileFor(n.path), content: render(custom ? import_node_fs10.default.readFileSync(custom, "utf8") : pageTpl, vars2) };
6408
6572
  });
6409
6573
  const to = homeRedirect(stack);
6410
6574
  if (to) {
6411
- const redirect = import_node_fs9.default.readFileSync(import_node_path10.default.join(templates, "_pages", "redirect-page.tsx.tmpl"), "utf8");
6575
+ const redirect = import_node_fs10.default.readFileSync(import_node_path11.default.join(templates, "_pages", "redirect-page.tsx.tmpl"), "utf8");
6412
6576
  files.push({ rel: "app/page.tsx", content: render(redirect, { firstPathJson: JSON.stringify(to) }) });
6413
6577
  }
6414
6578
  return files;
@@ -6416,9 +6580,9 @@ function renderPages(reg, stack, templates) {
6416
6580
  function peerDependenciesFor(blocks, io, stack) {
6417
6581
  const peers = {};
6418
6582
  for (const b of blocks) for (const [name, range] of Object.entries(b.peerDependencies)) peers[name] ??= range;
6419
- const usesUi = blocks.some((b) => walk(b.root).some((f) => /\.(tsx?|jsx?)$/.test(f) && /["']@graph8\/sdk\/ui["'/]/.test(import_node_fs9.default.readFileSync(import_node_path10.default.join(b.root, f), "utf8"))));
6420
- if (usesUi && io.sdkPackageJson && import_node_fs9.default.existsSync(io.sdkPackageJson)) {
6421
- const sdk = JSON.parse(import_node_fs9.default.readFileSync(io.sdkPackageJson, "utf8"));
6583
+ const usesUi = blocks.some((b) => walk(b.root).some((f) => /\.(tsx?|jsx?)$/.test(f) && /["']@graph8\/sdk\/ui["'/]/.test(import_node_fs10.default.readFileSync(import_node_path11.default.join(b.root, f), "utf8"))));
6584
+ if (usesUi && io.sdkPackageJson && import_node_fs10.default.existsSync(io.sdkPackageJson)) {
6585
+ const sdk = JSON.parse(import_node_fs10.default.readFileSync(io.sdkPackageJson, "utf8"));
6422
6586
  for (const [name, range] of Object.entries(sdk.peerDependencies ?? {})) peers[name] ??= range;
6423
6587
  }
6424
6588
  if (navUsesExtras(stack.nav)) peers["lucide-react"] ??= lucideRange(io);
@@ -6426,7 +6590,7 @@ function peerDependenciesFor(blocks, io, stack) {
6426
6590
  }
6427
6591
  function lucideRange(io) {
6428
6592
  try {
6429
- const sdk = JSON.parse(import_node_fs9.default.readFileSync(io.sdkPackageJson ?? "", "utf8"));
6593
+ const sdk = JSON.parse(import_node_fs10.default.readFileSync(io.sdkPackageJson ?? "", "utf8"));
6430
6594
  return sdk.peerDependencies?.["lucide-react"] ?? ">=0.400.0";
6431
6595
  } catch {
6432
6596
  return ">=0.400.0";
@@ -6439,8 +6603,21 @@ function mergedPackageJson(rendered, peers) {
6439
6603
  return `${JSON.stringify(pkg, null, 2)}
6440
6604
  `;
6441
6605
  }
6442
- function planCreate(reg, stack, appName, pm, io, app) {
6443
- const templates = import_node_path10.default.join(io.templatesRoot, "next-app");
6606
+ function withAuth(files, auth, templatesRoot) {
6607
+ const patch = (rel, fn) => {
6608
+ const f = files.find((x) => x.rel === rel);
6609
+ if (!f) throw new Error(`--auth ${auth}: the template has no ${rel}`);
6610
+ const next = fn(f.content);
6611
+ if (next === null) throw new Error(`--auth ${auth}: ${rel} does not have the generated shape`);
6612
+ f.content = next;
6613
+ };
6614
+ patch("README.md", (s) => applyAuthToReadme(s, auth));
6615
+ patch(".env.local.example", (s) => applyAuthToEnvExample(s, auth));
6616
+ if (usesSignInScreen(auth)) patch("app/layout.tsx", applyAuthToLayout);
6617
+ return [...files, ...planAuthFiles(auth, templatesRoot)];
6618
+ }
6619
+ function planCreate(reg, stack, appName, pm, io, app, auth) {
6620
+ const templates = import_node_path11.default.join(io.templatesRoot, "next-app");
6444
6621
  const blocks = resolveWithDeps(reg, stack.blocks);
6445
6622
  const devCommand = pm === "npm" ? "npm run dev" : `${pm} dev`;
6446
6623
  const vars2 = {
@@ -6457,7 +6634,7 @@ function planCreate(reg, stack, appName, pm, io, app) {
6457
6634
  };
6458
6635
  const files = [];
6459
6636
  for (const rel of walk(templates).filter((f) => !f.startsWith("_pages/"))) {
6460
- let content = render(import_node_fs9.default.readFileSync(import_node_path10.default.join(templates, rel), "utf8"), vars2);
6637
+ let content = render(import_node_fs10.default.readFileSync(import_node_path11.default.join(templates, rel), "utf8"), vars2);
6461
6638
  if (rel === "package.json.tmpl") content = mergedPackageJson(content, peerDependenciesFor(blocks, io, stack));
6462
6639
  files.push({ rel: outputName(rel), content });
6463
6640
  }
@@ -6466,12 +6643,12 @@ function planCreate(reg, stack, appName, pm, io, app) {
6466
6643
  const ledger = { sdkVersion: SDK_VERSION, target: "components/g8", stack: stack.id, ...app ? { app } : {}, blocks: {} };
6467
6644
  files.push({ rel: "graph8.json", content: `${JSON.stringify(ledger, null, 2)}
6468
6645
  ` });
6469
- return files;
6646
+ return auth ? withAuth(files, auth, io.templatesRoot) : files;
6470
6647
  }
6471
6648
  function dirProblem(abs, force) {
6472
- if (!import_node_fs9.default.existsSync(abs)) return null;
6473
- if (!import_node_fs9.default.statSync(abs).isDirectory()) return `${abs} exists and is not a directory.`;
6474
- if (force || import_node_fs9.default.readdirSync(abs).length === 0) return null;
6649
+ if (!import_node_fs10.default.existsSync(abs)) return null;
6650
+ if (!import_node_fs10.default.statSync(abs).isDirectory()) return `${abs} exists and is not a directory.`;
6651
+ if (force || import_node_fs10.default.readdirSync(abs).length === 0) return null;
6475
6652
  return `${abs} is not empty. Pick a new directory, or pass --force to write into it (template files are overwritten).`;
6476
6653
  }
6477
6654
  function runCreate(given, reg, io, cwd) {
@@ -6505,7 +6682,7 @@ function runCreate(given, reg, io, cwd) {
6505
6682
  }
6506
6683
  const problem = stackProblem(reg, stack);
6507
6684
  if (problem) return io.err(problem), 1;
6508
- const abs = import_node_path10.default.resolve(cwd, opts.dir);
6685
+ const abs = import_node_path11.default.resolve(cwd, opts.dir);
6509
6686
  const dirIssue = dirProblem(abs, opts.force);
6510
6687
  if (dirIssue) return io.err(dirIssue), 1;
6511
6688
  const brandIssue = opts.brand ? brandProblem(opts.brand, cwd) : null;
@@ -6513,7 +6690,7 @@ function runCreate(given, reg, io, cwd) {
6513
6690
  const pm = opts.packageManager ?? packageManagerFromUserAgent(io.userAgent);
6514
6691
  let files;
6515
6692
  try {
6516
- files = planCreate(reg, stack, appNameFor(opts.dir), pm, io, app);
6693
+ files = planCreate(reg, stack, appNameFor(opts.dir), pm, io, app, opts.auth);
6517
6694
  } catch (e) {
6518
6695
  io.err(e.message);
6519
6696
  return 1;
@@ -6524,13 +6701,14 @@ function runCreate(given, reg, io, cwd) {
6524
6701
  if (opts.dryRun) {
6525
6702
  io.out(`would add blocks: ${blocks.map((b) => b.id).join(", ")}`);
6526
6703
  if (opts.brand) io.out(`would brand the app: ${Object.entries(opts.brand).map(([k, v]) => `${k} ${v}`).join(", ")}`);
6704
+ if (opts.auth) io.out(`would set up sign-in (--auth ${opts.auth}): ${authSummary(opts.auth)}; authorize: appAuth.authorize in app/api/g8/[...path]/route.ts`);
6527
6705
  io.out(opts.install ? `would run: ${pm} install` : "would not install dependencies (--no-install)");
6528
6706
  return 0;
6529
6707
  }
6530
6708
  for (const f of files) {
6531
- const dest = import_node_path10.default.join(abs, f.rel);
6532
- import_node_fs9.default.mkdirSync(import_node_path10.default.dirname(dest), { recursive: true });
6533
- import_node_fs9.default.writeFileSync(dest, f.content, "utf8");
6709
+ const dest = import_node_path11.default.join(abs, f.rel);
6710
+ import_node_fs10.default.mkdirSync(import_node_path11.default.dirname(dest), { recursive: true });
6711
+ import_node_fs10.default.writeFileSync(dest, f.content, "utf8");
6534
6712
  }
6535
6713
  return finish(opts, reg, stack, blocks, abs, pm, io, cwd);
6536
6714
  }
@@ -6542,6 +6720,10 @@ function finish(opts, reg, stack, blocks, abs, pm, io, cwd) {
6542
6720
  return code;
6543
6721
  }
6544
6722
  }
6723
+ if (opts.auth) {
6724
+ const code = finishAuth(opts.auth, abs, io);
6725
+ if (code !== 0) return code;
6726
+ }
6545
6727
  if (opts.brand) {
6546
6728
  io.out("branding the app (graph8-sdk brand)");
6547
6729
  const code = runBrand({ ...opts.brand, dir: abs }, io, cwd, stack.title);
@@ -6564,13 +6746,37 @@ function finish(opts, reg, stack, blocks, abs, pm, io, cwd) {
6564
6746
  "",
6565
6747
  `Done. Next:`,
6566
6748
  ` 1. ${cd}${opts.install ? "" : `${pm} install && `}${pm === "npm" ? "npm run dev" : `${pm} dev`}`,
6749
+ ...opts.auth ? authOutro(opts.auth, opts.dir) : [],
6567
6750
  ` 2. open http://localhost:3000 and paste a graph8 API key in the Connect graph8 screen (local preview: only`,
6568
- ` this computer can reach the app's /api/g8 route), or set G8_API_KEY in ${import_node_path10.default.posix.join(opts.dir, ".env.local")}`,
6751
+ ` this computer can reach the app's /api/g8 route), or set G8_API_KEY in ${import_node_path11.default.posix.join(opts.dir, ".env.local")}`,
6569
6752
  ` 3. ${cd}npx @graph8/sdk doctor${opts.blocks ? "" : ` --stack ${stack.id}`} (before going live: add --production)`
6570
6753
  ].join("\n")
6571
6754
  );
6572
6755
  return 0;
6573
6756
  }
6757
+ function finishAuth(auth, abs, io) {
6758
+ const rel = "app/api/g8/[...path]/route.ts";
6759
+ const file = import_node_path11.default.join(abs, rel);
6760
+ const next = import_node_fs10.default.existsSync(file) ? applyAuthToRoute(import_node_fs10.default.readFileSync(file, "utf8")) : null;
6761
+ if (next === null) {
6762
+ io.err(`--auth ${auth}: ${rel} does not have the generated shape; set \`authorize: appAuth.authorize\` (import { appAuth } from "@/g8.auth") in it yourself.`);
6763
+ return 1;
6764
+ }
6765
+ import_node_fs10.default.writeFileSync(file, next, "utf8");
6766
+ io.out(`sign-in (--auth ${auth}): ${authSummary(auth)}`);
6767
+ io.out(`update ${rel} (authorize: appAuth.authorize, from g8.auth.ts)`);
6768
+ const written = writeAuthSecrets(abs, auth);
6769
+ if (written.length) io.out(`write .env.local (fresh ${written.join(" and ")} for local development; git-ignored, not printed)`);
6770
+ return 0;
6771
+ }
6772
+ function authOutro(auth, dir) {
6773
+ const env = import_node_path11.default.posix.join(dir, ".env.local");
6774
+ if (auth === "password") return [` sign in with G8_APP_PASSWORD from ${env} (set your own G8_APP_PASSWORD and G8_SESSION_SECRET on your host)`];
6775
+ if (auth === "basic") return [` the browser asks for the G8_BASIC_AUTH user and password in ${env}`];
6776
+ if (auth === "jwt") return [` set your identity provider in ${env} (G8_JWT_ISSUER, G8_JWT_AUDIENCE, G8_JWT_COOKIE, G8_SIGN_IN_URL; see g8.auth.ts)`];
6777
+ if (auth === "session") return [" wire your session library in g8.auth.ts (Clerk, NextAuth, Supabase examples inside); until then it refuses everyone"];
6778
+ return [" WARNING: --auth anonymous: anyone who can reach this app can use its graph8 API key. Use a narrow key with a spend cap."];
6779
+ }
6574
6780
 
6575
6781
  // src/cli.ts
6576
6782
  var LIST_USAGE = `Usage: npx @graph8/sdk list [--json]
@@ -6595,7 +6801,7 @@ Run a command with --help for its options.
6595
6801
 
6596
6802
  ${INIT_AGENT_USAGE}`;
6597
6803
  function registryFor(io) {
6598
- const root = io.blocksRoot ?? process.env.G8_BLOCKS_DIR ?? import_node_path11.default.resolve(__dirname, "..", "blocks");
6804
+ const root = io.blocksRoot ?? process.env.G8_BLOCKS_DIR ?? import_node_path12.default.resolve(__dirname, "..", "blocks");
6599
6805
  return loadRegistry(root);
6600
6806
  }
6601
6807
  function defaultSpawn(cmd, args, cwd) {
@@ -6669,13 +6875,13 @@ function runCreateCommand(rest, io) {
6669
6875
  if (wantsHelp(rest)) return io.out(CREATE_USAGE), 0;
6670
6876
  try {
6671
6877
  const opts = parseCreateArgs(rest);
6672
- const packageRoot = import_node_path11.default.resolve(__dirname, "..");
6878
+ const packageRoot = import_node_path12.default.resolve(__dirname, "..");
6673
6879
  const cio = {
6674
6880
  out: io.out,
6675
6881
  err: io.err,
6676
6882
  spawn: io.spawn ?? defaultSpawn,
6677
- templatesRoot: io.templatesRoot ?? process.env.G8_TEMPLATES_DIR ?? import_node_path11.default.join(packageRoot, "templates"),
6678
- sdkPackageJson: import_node_path11.default.join(packageRoot, "package.json"),
6883
+ templatesRoot: io.templatesRoot ?? process.env.G8_TEMPLATES_DIR ?? import_node_path12.default.join(packageRoot, "templates"),
6884
+ sdkPackageJson: import_node_path12.default.join(packageRoot, "package.json"),
6679
6885
  userAgent: io.userAgent ?? process.env.npm_config_user_agent,
6680
6886
  orgBrand: io.orgBrand
6681
6887
  };
@@ -6688,13 +6894,13 @@ function runAddCommand(rest, io) {
6688
6894
  if (wantsHelp(rest)) return io.out(ADD_USAGE), 0;
6689
6895
  try {
6690
6896
  const opts = parseAddArgs(rest);
6691
- const packageRoot = import_node_path11.default.resolve(__dirname, "..");
6897
+ const packageRoot = import_node_path12.default.resolve(__dirname, "..");
6692
6898
  const aio = {
6693
6899
  out: io.out,
6694
6900
  err: io.err,
6695
6901
  spawn: io.spawn ?? defaultSpawn,
6696
- templatesRoot: io.templatesRoot ?? process.env.G8_TEMPLATES_DIR ?? import_node_path11.default.join(packageRoot, "templates"),
6697
- sdkPackageJson: import_node_path11.default.join(packageRoot, "package.json")
6902
+ templatesRoot: io.templatesRoot ?? process.env.G8_TEMPLATES_DIR ?? import_node_path12.default.join(packageRoot, "templates"),
6903
+ sdkPackageJson: import_node_path12.default.join(packageRoot, "package.json")
6698
6904
  };
6699
6905
  return runAdd(opts, registryFor(io), aio, io.cwd);
6700
6906
  } catch (e) {
@@ -6738,8 +6944,8 @@ ${USAGE}`);
6738
6944
  return 1;
6739
6945
  }
6740
6946
  const read = (rel) => {
6741
- const full = import_node_path11.default.join(io.cwd, rel);
6742
- return import_node_fs10.default.existsSync(full) ? import_node_fs10.default.readFileSync(full, "utf8") : null;
6947
+ const full = import_node_path12.default.join(io.cwd, rel);
6948
+ return import_node_fs11.default.existsSync(full) ? import_node_fs11.default.readFileSync(full, "utf8") : null;
6743
6949
  };
6744
6950
  let plans;
6745
6951
  try {
@@ -6750,9 +6956,9 @@ ${USAGE}`);
6750
6956
  }
6751
6957
  for (const plan of plans) {
6752
6958
  if (!options.dryRun && plan.action !== "unchanged") {
6753
- const full = import_node_path11.default.join(io.cwd, plan.path);
6754
- import_node_fs10.default.mkdirSync(import_node_path11.default.dirname(full), { recursive: true });
6755
- import_node_fs10.default.writeFileSync(full, plan.content, "utf8");
6959
+ const full = import_node_path12.default.join(io.cwd, plan.path);
6960
+ import_node_fs11.default.mkdirSync(import_node_path12.default.dirname(full), { recursive: true });
6961
+ import_node_fs11.default.writeFileSync(full, plan.content, "utf8");
6756
6962
  }
6757
6963
  io.out(`${options.dryRun ? "would " : ""}${plan.action === "unchanged" ? "keep" : plan.action} ${plan.path} (${plan.target})`);
6758
6964
  }
@@ -6762,7 +6968,7 @@ ${USAGE}`);
6762
6968
  function brandRoot(command, rest, io) {
6763
6969
  const at = rest.findIndex((a) => a === "--dir" || a.startsWith("--dir="));
6764
6970
  const dir = at < 0 ? void 0 : rest[at].includes("=") ? rest[at].slice(6) : rest[at + 1];
6765
- return command === "brand" && dir ? import_node_path11.default.resolve(io.cwd, dir) : io.cwd;
6971
+ return command === "brand" && dir ? import_node_path12.default.resolve(io.cwd, dir) : io.cwd;
6766
6972
  }
6767
6973
  async function runWithOrgBrand(command, rest, argv, io) {
6768
6974
  const wants = !wantsHelp(rest) && !rest.includes("--no-org-brand") && (command === "brand" || !rest.includes("--dry-run")) && (command === "create" || rest.includes("--from-org") || !rest.some((a) => a.startsWith("--") && a !== "--dir" && !a.startsWith("--dir=")));
@@ -991,7 +991,7 @@ var EXTERNAL_ROUTES = {
991
991
  };
992
992
 
993
993
  // src/version.ts
994
- var SDK_VERSION = "0.373.0";
994
+ var SDK_VERSION = "0.374.0";
995
995
  var SDK_CLIENT_ID = `js-sdk/${SDK_VERSION}`;
996
996
 
997
997
  // src/errors.ts