handoff-app 2.0.0-1 → 2.0.0-3

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 (219) hide show
  1. package/README.md +170 -91
  2. package/config/docs/foundations/colors/guidelines.md +9 -0
  3. package/config/docs/foundations/effects/guidelines.md +9 -0
  4. package/config/docs/foundations/effects/resources.md +9 -0
  5. package/config/docs/foundations/grid.md +10 -0
  6. package/config/docs/foundations/icons/guidelines.md +9 -0
  7. package/config/docs/foundations/images/guidelines.md +9 -0
  8. package/config/docs/foundations/images/resources.md +9 -0
  9. package/config/docs/foundations/images.md +10 -0
  10. package/config/docs/foundations/logo/resources.md +9 -0
  11. package/config/docs/foundations/typography/guidelines.md +9 -0
  12. package/config/docs/foundations/typography/resources.md +9 -0
  13. package/config/docs/foundations.md +5 -3
  14. package/dist/app-builder/build.js +109 -49
  15. package/dist/app-builder/client-config.js +13 -12
  16. package/dist/app-builder/paths.js +7 -9
  17. package/dist/app-builder/static-export.js +6 -6
  18. package/dist/app-builder/vercel-output.js +2 -2
  19. package/dist/artifacts/url.d.ts +7 -7
  20. package/dist/artifacts/url.js +7 -7
  21. package/dist/cli/auth/browser.d.ts +2 -0
  22. package/dist/cli/auth/browser.js +33 -0
  23. package/dist/cli/auth/device.d.ts +11 -0
  24. package/dist/cli/auth/device.js +155 -0
  25. package/dist/cli/auth/store.d.ts +29 -0
  26. package/dist/cli/auth/store.js +146 -0
  27. package/dist/cli/create.js +48 -3
  28. package/dist/cli/scaffold.js +20 -154
  29. package/dist/commands/checkout/index.d.ts +6 -6
  30. package/dist/commands/checkout/index.js +8 -12
  31. package/dist/commands/index.js +4 -0
  32. package/dist/commands/login/index.d.ts +8 -0
  33. package/dist/commands/login/index.js +53 -0
  34. package/dist/commands/logout/index.d.ts +7 -0
  35. package/dist/commands/logout/index.js +54 -0
  36. package/dist/commands/publish/index.d.ts +6 -5
  37. package/dist/commands/publish/index.js +8 -11
  38. package/dist/commands/utils.d.ts +6 -0
  39. package/dist/commands/utils.js +9 -1
  40. package/dist/config/entries.d.ts +47 -0
  41. package/dist/config/entries.js +162 -0
  42. package/dist/config/index.d.ts +2 -0
  43. package/dist/config/index.js +5 -1
  44. package/dist/config/runtime.d.ts +9 -9
  45. package/dist/config/runtime.js +15 -15
  46. package/dist/index.d.ts +12 -8
  47. package/dist/index.js +26 -10
  48. package/dist/registry/asset-storage/resolve.d.ts +1 -1
  49. package/dist/registry/asset-storage/resolve.js +1 -1
  50. package/dist/registry/auth/access-tokens.d.ts +46 -0
  51. package/dist/registry/auth/access-tokens.js +165 -0
  52. package/dist/registry/auth/action-tokens.d.ts +66 -0
  53. package/dist/registry/auth/action-tokens.js +178 -0
  54. package/dist/registry/auth/crypto.d.ts +4 -0
  55. package/dist/registry/auth/crypto.js +16 -0
  56. package/dist/registry/auth/database.d.ts +7 -0
  57. package/dist/registry/auth/database.js +22 -0
  58. package/dist/registry/auth/device-authorizations.d.ts +51 -0
  59. package/dist/registry/auth/device-authorizations.js +170 -0
  60. package/dist/registry/auth/gravatar.d.ts +1 -0
  61. package/dist/registry/auth/gravatar.js +8 -0
  62. package/dist/registry/auth/index.d.ts +11 -0
  63. package/dist/registry/auth/index.js +72 -0
  64. package/dist/registry/auth/installation.d.ts +42 -0
  65. package/dist/registry/auth/installation.js +105 -0
  66. package/dist/registry/auth/password.d.ts +12 -0
  67. package/dist/registry/auth/password.js +58 -0
  68. package/dist/registry/auth/rate-limits.d.ts +22 -0
  69. package/dist/registry/auth/rate-limits.js +75 -0
  70. package/dist/registry/auth/types.d.ts +26 -0
  71. package/dist/registry/auth/types.js +9 -0
  72. package/dist/registry/auth/user-record.d.ts +13 -0
  73. package/dist/registry/auth/user-record.js +16 -0
  74. package/dist/registry/auth/users.d.ts +31 -0
  75. package/dist/registry/auth/users.js +135 -0
  76. package/dist/registry/auth/validation.d.ts +3 -0
  77. package/dist/registry/auth/validation.js +28 -0
  78. package/dist/registry/checkout/assets.js +4 -2
  79. package/dist/registry/checkout/index.d.ts +11 -6
  80. package/dist/registry/checkout/index.js +159 -30
  81. package/dist/registry/checkout/tokens.js +4 -2
  82. package/dist/registry/client.d.ts +2 -1
  83. package/dist/registry/client.js +8 -0
  84. package/dist/registry/connection.d.ts +9 -0
  85. package/dist/registry/connection.js +29 -1
  86. package/dist/registry/db/client.d.ts +9 -9
  87. package/dist/registry/db/client.js +7 -7
  88. package/dist/registry/db/driver.d.ts +46 -0
  89. package/dist/registry/db/{adapter.js → driver.js} +22 -19
  90. package/dist/registry/db/index.d.ts +2 -2
  91. package/dist/registry/db/index.js +15 -9
  92. package/dist/registry/db/migrate.d.ts +1 -1
  93. package/dist/registry/db/migrate.js +5 -5
  94. package/dist/registry/db/schema.d.ts +2521 -750
  95. package/dist/registry/db/schema.js +126 -1
  96. package/dist/registry/publish/assets.js +1 -1
  97. package/dist/registry/publish/errors.js +3 -3
  98. package/dist/registry/publish/index.d.ts +9 -2
  99. package/dist/registry/publish/index.js +96 -10
  100. package/dist/registry/publish/tokens.js +1 -1
  101. package/dist/registry/transfer.d.ts +13 -0
  102. package/dist/types/config.d.ts +11 -4
  103. package/docs/cli.md +4 -0
  104. package/drizzle/0005_add_registry_auth.sql +95 -0
  105. package/drizzle/meta/0005_snapshot.json +1926 -0
  106. package/drizzle/meta/_journal.json +8 -1
  107. package/drizzle.config.ts +1 -1
  108. package/package.json +5 -4
  109. package/src/app/components/Auth/AccountLayout.tsx +87 -0
  110. package/src/app/components/Auth/AuthControls.tsx +99 -0
  111. package/src/app/components/Auth/AuthShell.tsx +73 -0
  112. package/src/app/components/Auth/RegistryInstallGate.tsx +23 -0
  113. package/src/app/components/Auth/RegistrySessionProvider.tsx +15 -0
  114. package/src/app/components/Auth/api.ts +20 -0
  115. package/src/app/components/Foundations/DisplayLogo.tsx +3 -3
  116. package/src/app/components/Layout/Header.tsx +2 -0
  117. package/src/app/components/Navigation/MobileNav.tsx +2 -0
  118. package/src/app/lib/auth/api.ts +113 -0
  119. package/src/app/lib/auth/config.ts +175 -0
  120. package/src/app/lib/auth/email.ts +34 -0
  121. package/src/app/lib/docs-api/runtime-config.ts +10 -24
  122. package/src/app/lib/registry-api/auth.ts +29 -46
  123. package/src/app/lib/registry-api/errors.ts +2 -2
  124. package/src/app/lib/registry-api/handler.ts +19 -14
  125. package/src/app/lib/registry-api/token-transfer.ts +1 -1
  126. package/src/app/lib/registry-api/transfer.ts +31 -2
  127. package/src/app/lib/registry-connection.ts +2 -2
  128. package/src/app/next.config.mjs +13 -4
  129. package/src/app/pages/_app.tsx +10 -4
  130. package/src/app/pages/account/index.tsx +153 -0
  131. package/src/app/pages/account/tokens.tsx +230 -0
  132. package/src/app/pages/account/users.tsx +310 -0
  133. package/src/app/pages/api/account/index.ts +26 -0
  134. package/src/app/pages/api/account/tokens/[id].ts +17 -0
  135. package/src/app/pages/api/account/tokens/index.ts +44 -0
  136. package/src/app/pages/api/admin/users/[id]/[action].ts +89 -0
  137. package/src/app/pages/api/admin/users/index.ts +53 -0
  138. package/src/app/pages/api/auth/[...nextauth].ts +4 -0
  139. package/src/app/pages/api/auth/request-reset.ts +53 -0
  140. package/src/app/pages/api/auth/reset-password.ts +29 -0
  141. package/src/app/pages/api/install.ts +102 -0
  142. package/src/app/pages/api/oauth/device/approve.ts +23 -0
  143. package/src/app/pages/api/oauth/device/index.ts +41 -0
  144. package/src/app/pages/api/oauth/revoke.ts +24 -0
  145. package/src/app/pages/api/oauth/token.ts +31 -0
  146. package/src/app/pages/api/registry/health.ts +4 -3
  147. package/src/app/pages/api/registry/transfer/[kind]/[id].ts +3 -3
  148. package/src/app/pages/api/registry/transfer/[kind]/index.ts +21 -0
  149. package/src/app/pages/api/registry/transfer/assets/[collection].ts +2 -1
  150. package/src/app/pages/api/registry/transfer/assets/blobs/[hash].ts +2 -1
  151. package/src/app/pages/api/registry/transfer/tokens/[...setId].ts +2 -2
  152. package/src/app/pages/cli/device.tsx +143 -0
  153. package/src/app/pages/foundations/icons/index.tsx +2 -2
  154. package/src/app/pages/foundations/icons/{[name] → library/[name]}/index.tsx +6 -6
  155. package/src/app/pages/install.tsx +336 -0
  156. package/src/app/pages/login.tsx +117 -0
  157. package/src/app/pages/reset-password.tsx +159 -0
  158. package/src/app/types/next-auth.d.ts +27 -0
  159. package/src/app-builder/build.ts +96 -59
  160. package/src/app-builder/client-config.ts +12 -11
  161. package/src/app-builder/paths.ts +7 -9
  162. package/src/app-builder/static-export.ts +6 -6
  163. package/src/app-builder/vercel-output.ts +2 -2
  164. package/src/artifacts/url.ts +7 -7
  165. package/src/cli/auth/browser.ts +29 -0
  166. package/src/cli/auth/device.ts +181 -0
  167. package/src/cli/auth/store.ts +137 -0
  168. package/src/cli/create.ts +58 -3
  169. package/src/cli/scaffold.ts +20 -197
  170. package/src/commands/checkout/index.ts +10 -15
  171. package/src/commands/index.ts +4 -0
  172. package/src/commands/login/index.ts +48 -0
  173. package/src/commands/logout/index.ts +51 -0
  174. package/src/commands/publish/index.ts +10 -14
  175. package/src/commands/utils.ts +12 -0
  176. package/src/config/entries.ts +175 -0
  177. package/src/config/index.ts +4 -0
  178. package/src/config/runtime.ts +15 -14
  179. package/src/index.ts +26 -12
  180. package/src/registry/asset-storage/resolve.ts +1 -1
  181. package/src/registry/auth/access-tokens.ts +198 -0
  182. package/src/registry/auth/action-tokens.ts +225 -0
  183. package/src/registry/auth/crypto.ts +13 -0
  184. package/src/registry/auth/database.ts +15 -0
  185. package/src/registry/auth/device-authorizations.ts +223 -0
  186. package/src/registry/auth/gravatar.ts +6 -0
  187. package/src/registry/auth/index.ts +89 -0
  188. package/src/registry/auth/installation.ts +117 -0
  189. package/src/registry/auth/password.ts +46 -0
  190. package/src/registry/auth/rate-limits.ts +84 -0
  191. package/src/registry/auth/types.ts +34 -0
  192. package/src/registry/auth/user-record.ts +25 -0
  193. package/src/registry/auth/users.ts +145 -0
  194. package/src/registry/auth/validation.ts +24 -0
  195. package/src/registry/checkout/assets.ts +4 -2
  196. package/src/registry/checkout/index.ts +182 -30
  197. package/src/registry/checkout/tokens.ts +4 -2
  198. package/src/registry/client.ts +7 -1
  199. package/src/registry/connection.ts +23 -0
  200. package/src/registry/db/client.ts +12 -12
  201. package/src/registry/db/{adapter.ts → driver.ts} +24 -21
  202. package/src/registry/db/index.ts +16 -4
  203. package/src/registry/db/migrate.ts +5 -5
  204. package/src/registry/db/schema.ts +174 -1
  205. package/src/registry/publish/assets.ts +1 -1
  206. package/src/registry/publish/errors.ts +3 -3
  207. package/src/registry/publish/index.ts +108 -10
  208. package/src/registry/publish/tokens.ts +1 -1
  209. package/src/registry/transfer.ts +14 -0
  210. package/src/types/config.ts +11 -4
  211. package/templates/default/README.md.tpl +58 -1
  212. package/templates/default/gitignore.tpl +19 -3
  213. package/templates/default/handoff.config.js.tpl +13 -0
  214. package/templates/default/handoff.config.ts.tpl +13 -0
  215. package/templates/default/handoff.config.with-examples.js.tpl +13 -0
  216. package/templates/default/handoff.config.with-examples.ts.tpl +13 -0
  217. package/templates/default/package.json.tpl +2 -1
  218. package/templates/default/vercel.json.tpl +4 -0
  219. package/dist/registry/db/adapter.d.ts +0 -43
@@ -1,4 +1,27 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
26
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
27
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -13,6 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
36
  };
14
37
  Object.defineProperty(exports, "__esModule", { value: true });
15
38
  exports.devApp = exports.watchApp = exports.getRegistryBuildOutputPath = exports.HandoffBuildError = exports.DEFAULT_BUILD_TARGET = void 0;
39
+ const p = __importStar(require("@clack/prompts"));
16
40
  const cross_spawn_1 = __importDefault(require("cross-spawn"));
17
41
  const fs_extra_1 = __importDefault(require("fs-extra"));
18
42
  const path_1 = __importDefault(require("path"));
@@ -21,7 +45,7 @@ const patterns_1 = require("../pipeline/patterns");
21
45
  const builder_1 = __importDefault(require("../transformers/preview/component/builder"));
22
46
  const css_1 = require("../transformers/preview/component/css");
23
47
  const javascript_1 = require("../transformers/preview/component/javascript");
24
- const adapter_1 = require("../registry/db/adapter");
48
+ const driver_1 = require("../registry/db/driver");
25
49
  const resolve_1 = require("../registry/asset-storage/resolve");
26
50
  const logger_1 = require("../utils/logger");
27
51
  const client_config_1 = require("./client-config");
@@ -78,6 +102,51 @@ const assertStaticBuildAllowed = (handoff) => {
78
102
  'or package the registry app with `handoff-app build --target registry`.');
79
103
  }
80
104
  };
105
+ /**
106
+ * Run the Next.js production build while keeping its delayed output out of the successful CLI flow.
107
+ * Captured output is replayed only when the child fails so compiler diagnostics remain available.
108
+ */
109
+ const runNextBuild = (appPath, target, errorLabel) => __awaiter(void 0, void 0, void 0, function* () {
110
+ const spinner = p.spinner();
111
+ spinner.start('Building Next.js app...');
112
+ const stdout = [];
113
+ const stderr = [];
114
+ yield new Promise((resolve, reject) => {
115
+ var _a, _b;
116
+ const buildProcess = (0, cross_spawn_1.default)('npx', ['next', 'build'], {
117
+ cwd: appPath,
118
+ stdio: ['inherit', 'pipe', 'pipe'],
119
+ env: Object.assign(Object.assign({}, process.env), { NODE_ENV: 'production', HANDOFF_BUILD_TARGET: target }),
120
+ });
121
+ (_a = buildProcess.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (chunk) => stdout.push(Buffer.from(chunk)));
122
+ (_b = buildProcess.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (chunk) => stderr.push(Buffer.from(chunk)));
123
+ let settled = false;
124
+ const fail = (message) => {
125
+ if (settled)
126
+ return;
127
+ settled = true;
128
+ spinner.stop('Next.js app build failed');
129
+ logger_1.Logger.childProcessBuffer(Buffer.concat(stdout));
130
+ logger_1.Logger.childProcessBuffer(Buffer.concat(stderr));
131
+ reject(new Error(message));
132
+ };
133
+ buildProcess.once('error', (error) => {
134
+ fail(`${errorLabel} failed to start\nSpawn error: ${error.message}`);
135
+ });
136
+ buildProcess.once('close', (code, signal) => {
137
+ if (settled)
138
+ return;
139
+ if (code === 0) {
140
+ settled = true;
141
+ spinner.stop('Next.js app built successfully');
142
+ resolve();
143
+ return;
144
+ }
145
+ const reason = signal ? ` because it was terminated by signal ${signal}` : ` with exit code ${code}`;
146
+ fail(`${errorLabel} failed${reason}`);
147
+ });
148
+ });
149
+ });
81
150
  /**
82
151
  * Output directory for the packaged registry app. Lives under the configurable sites output
83
152
  * directory (`sitesOutputDirectory`, default `out`) alongside the static target's `out/<projectId>`
@@ -147,9 +216,8 @@ const initializeProjectApp = (handoff_1, ...args_1) => __awaiter(void 0, [handof
147
216
  // Resolved runtime mode + registry connection inputs (names only) baked into the Next bundle so the
148
217
  // deployed registry app resolves its mode/DB env-var name without any build-machine filesystem.
149
218
  const escapedRuntimeMode = escapeForSingleQuotedJsString(runtimeMode);
150
- const escapedRegistryAdapter = escapeForSingleQuotedJsString((0, adapter_1.resolveRegistryAdapter)(handoff.config));
151
- const escapedDatabaseUrlEnv = escapeForSingleQuotedJsString((0, adapter_1.resolveDatabaseUrlEnv)(handoff.config));
152
- const escapedApiTokenEnv = escapeForSingleQuotedJsString((0, adapter_1.resolveApiTokenEnv)(handoff.config));
219
+ const escapedRegistryDriver = escapeForSingleQuotedJsString((0, driver_1.resolveRegistryDriver)(handoff.config));
220
+ const escapedDatabaseUrlEnv = escapeForSingleQuotedJsString((0, driver_1.resolveDatabaseUrlEnv)(handoff.config));
153
221
  // Asset storage selection baked (provider + module + env-var names + non-secret options JSON).
154
222
  const assetStorage = (0, resolve_1.resolveAssetStorageFromConfig)(handoff.config);
155
223
  const escapedAssetStorageAdapter = escapeForSingleQuotedJsString(assetStorage.adapterKind);
@@ -165,9 +233,8 @@ const initializeProjectApp = (handoff_1, ...args_1) => __awaiter(void 0, [handof
165
233
  '%HANDOFF_EXPORT_PATH%': escapedExportPath,
166
234
  '%HANDOFF_WEBSOCKET_PORT%': escapedWebsocketPort,
167
235
  '%HANDOFF_RUNTIME_MODE%': escapedRuntimeMode,
168
- '%HANDOFF_REGISTRY_ADAPTER%': escapedRegistryAdapter,
236
+ '%HANDOFF_REGISTRY_DRIVER%': escapedRegistryDriver,
169
237
  '%HANDOFF_REGISTRY_DATABASE_URL_ENV%': escapedDatabaseUrlEnv,
170
- '%HANDOFF_REGISTRY_API_TOKEN_ENV%': escapedApiTokenEnv,
171
238
  '%HANDOFF_ASSET_STORAGE_ADAPTER%': escapedAssetStorageAdapter,
172
239
  '%HANDOFF_ASSET_STORAGE_MODULE%': escapedAssetStorageModule,
173
240
  '%HANDOFF_ASSET_STORAGE_TOKEN_ENV%': escapedAssetStorageTokenEnv,
@@ -216,20 +283,7 @@ const buildApp = (handoff_1, ...args_1) => __awaiter(void 0, [handoff_1, ...args
216
283
  (0, static_export_1.validateReferencedArtifacts)(handoff);
217
284
  // Build app. The static target drives Next's `output: 'export'` via HANDOFF_BUILD_TARGET so the
218
285
  // export gate is tied to the resolved target, not to NODE_ENV.
219
- const buildResult = cross_spawn_1.default.sync('npx', ['next', 'build'], {
220
- cwd: appPath,
221
- stdio: ['inherit', 'pipe', 'pipe'],
222
- env: Object.assign(Object.assign({}, process.env), { NODE_ENV: 'production', HANDOFF_BUILD_TARGET: target }),
223
- });
224
- logger_1.Logger.childProcessBuffer(buildResult.stdout);
225
- logger_1.Logger.childProcessBuffer(buildResult.stderr);
226
- if (buildResult.status !== 0) {
227
- let errorMsg = `Next.js build failed with exit code ${buildResult.status}`;
228
- if (buildResult.error) {
229
- errorMsg += `\nSpawn error: ${buildResult.error.message}`;
230
- }
231
- throw new Error(errorMsg);
232
- }
286
+ yield runNextBuild(appPath, target, 'Next.js build');
233
287
  // Reproduce the docs read API (`/api/docs/*`) as route-shaped static files in the export output,
234
288
  // since `output: 'export'` disables the live API routes.
235
289
  const exportDir = path_1.default.resolve(appPath, 'out');
@@ -295,7 +349,11 @@ and \`.next/static/\` already copied alongside so the server serves them.
295
349
  ## Required environment variables (supplied at deploy time, never baked in)
296
350
 
297
351
  - \`${databaseUrlEnv}\` — PostgreSQL/Neon connection string, read at request time.
298
- - \`HANDOFF_REGISTRY_API_TOKEN\` — bearer token for registry management mutations (if used).
352
+ - \`AUTH_SECRET\` — a long, random secret used to sign browser sessions.
353
+ - \`AUTH_URL\` — the canonical public registry URL, including the configured base path.
354
+
355
+ Optional email delivery uses \`RESEND_API_KEY\` and \`AUTH_FROM_EMAIL\`. Without them, invitation
356
+ links are shown once to an administrator for manual delivery.
299
357
 
300
358
  ## Database migrations
301
359
 
@@ -306,10 +364,25 @@ pipeline) before deploying new code:
306
364
  ${databaseUrlEnv}="postgres://…" handoff-app db:migrate
307
365
  \`\`\`
308
366
 
309
- \`db:migrate\` reads your project config + DB env vars, resolves the same database adapter the app was
367
+ \`db:migrate\` reads your project config + DB env vars, resolves the same database driver the app was
310
368
  built with, and applies the package-owned migration set. It runs independently of \`build\` and of
311
369
  starting the server, so run it as a release/one-shot job.
312
370
 
371
+ ## Complete the one-time installation
372
+
373
+ After migrations and deployment, open \`/install\` immediately and create the initial administrator.
374
+ The installer verifies the deployment but never changes the schema. An exposed, uninstalled registry
375
+ can be claimed by its first visitor, so do not leave this step unattended.
376
+
377
+ Once installation completes, sign in and authorize a workspace:
378
+
379
+ \`\`\`bash
380
+ handoff-app login --url https://registry.example.com
381
+ \`\`\`
382
+
383
+ The browser approval issues a revocable, user-owned credential. The former
384
+ \`HANDOFF_REGISTRY_API_TOKEN\` fixed server secret is no longer accepted.
385
+
313
386
  ## Run it (self-hosting)
314
387
 
315
388
  Standalone is the documented artifact for self-hosting on a Node.js server, VPS, Docker, or
@@ -352,15 +425,15 @@ containers, custom Node servers, and other non-Vercel hosts.
352
425
  });
353
426
  /**
354
427
  * Resolve the set of npm packages the packaged registry app must be able to `require`/`import` at
355
- * runtime, given the configured database adapter. `next`/`react`/`react-dom` run the server and React
428
+ * runtime, given the configured database driver. `next`/`react`/`react-dom` run the server and React
356
429
  * runtime; `drizzle-orm` backs both the request-time DB client and the migration runner; the driver
357
- * is adapter-specific (the Neon serverless driver also needs `ws` for its Node WebSocket transport).
430
+ * package is driver-specific (the Neon serverless driver also needs `ws` for its Node WebSocket transport).
358
431
  */
359
432
  const getRequiredRegistryRuntimeModules = (handoff) => {
360
433
  var _a;
361
- const base = ['next', 'react', 'react-dom', 'drizzle-orm'];
362
- const adapter = (0, adapter_1.resolveRegistryAdapter)(handoff.config);
363
- const driver = adapter === 'neon' ? ['@neondatabase/serverless', 'ws'] : ['pg'];
434
+ const base = ['next', 'next-auth', 'react', 'react-dom', 'drizzle-orm'];
435
+ const driver = (0, driver_1.resolveRegistryDriver)(handoff.config);
436
+ const driverModules = driver === 'neon' ? ['@neondatabase/serverless', 'ws'] : ['pg'];
364
437
  // Asset-storage SDKs the deployed registry must be able to load at request time. The pre-packaged
365
438
  // Vercel Blob adapter needs `@vercel/blob`; a custom adapter may declare its installed SDK
366
439
  // package(s) via `assetStorage.options.sdkModules` so the trace/assertion covers them.
@@ -373,7 +446,7 @@ const getRequiredRegistryRuntimeModules = (handoff) => {
373
446
  if (Array.isArray(sdkModules)) {
374
447
  storage.push(...sdkModules.filter((mod) => typeof mod === 'string'));
375
448
  }
376
- return [...base, ...driver, ...storage];
449
+ return [...base, ...driverModules, ...storage];
377
450
  };
378
451
  /**
379
452
  * Build-time guard against the empty-`node_modules` regression (the artifact would otherwise boot-fail
@@ -482,7 +555,7 @@ const assembleRegistryStandalone = (handoff, appPath, standaloneRoot, entryDir)
482
555
  * - `vercel` → the Vercel Build Output API directory (`.vercel/output`) at the repo root: the
483
556
  * traced bundle wrapped as a Node function plus CDN static assets and a route table.
484
557
  *
485
- * Mode is forced to `registry`; the database adapter is honored from config (single-sourced so build
558
+ * Mode is forced to `registry`; the database driver is honored from config (single-sourced so build
486
559
  * and `db:migrate` never diverge). No connection-string value is ever baked — only env-var *names*.
487
560
  */
488
561
  const buildRegistryApp = (handoff_1, ...args_1) => __awaiter(void 0, [handoff_1, ...args_1], void 0, function* (handoff, buildPackage = 'standalone') {
@@ -495,15 +568,15 @@ const buildRegistryApp = (handoff_1, ...args_1) => __awaiter(void 0, [handoff_1,
495
568
  if (sourceMode !== 'registry') {
496
569
  logger_1.Logger.info(`Source runtime.mode is "${sourceMode}"; forcing "registry" mode in the packaged registry app.`);
497
570
  }
498
- // The adapter stays single-sourced from config (never flipped by the package flag), so build and
571
+ // The driver stays single-sourced from config (never flipped by the package flag), so build and
499
572
  // `db:migrate` always resolve the same one. `pg` uses long-lived TCP connections that pool poorly on
500
573
  // serverless; warn (non-fatally) so the operator points DATABASE_URL at a pooled endpoint or selects
501
- // the Neon adapter. The connection-string value is not available at build time, so this cannot be
574
+ // the Neon driver. The connection-string value is not available at build time, so this cannot be
502
575
  // validated — it is guidance only and never fails the build.
503
- if (buildPackage === 'vercel' && (0, adapter_1.resolveRegistryAdapter)(handoff.config) === 'pg') {
504
- logger_1.Logger.warn('Registry Vercel build uses the "pg" adapter (standard TCP connections), which pools poorly on ' +
576
+ if (buildPackage === 'vercel' && (0, driver_1.resolveRegistryDriver)(handoff.config) === 'pg') {
577
+ logger_1.Logger.warn('Registry Vercel build uses the "pg" driver (standard TCP connections), which pools poorly on ' +
505
578
  'serverless and can exhaust the database connection limit. Point DATABASE_URL at a pooled endpoint ' +
506
- '(PgBouncer / transaction pooling), or set runtime.registry.database.adapter: "neon" for a ' +
579
+ '(PgBouncer / transaction pooling), or set runtime.registry.database.driver: "neon" for a ' +
507
580
  'serverless-native HTTP/WebSocket driver. The build will continue.');
508
581
  }
509
582
  // Stage the app without any workspace-derived artifacts — the registry serves from the database.
@@ -524,20 +597,7 @@ const buildRegistryApp = (handoff_1, ...args_1) => __awaiter(void 0, [handoff_1,
524
597
  yield fs_extra_1.default.remove((0, vercel_output_1.getVercelOutputPath)(handoff));
525
598
  // Build the dynamic app. `output: 'standalone'` (driven by HANDOFF_BUILD_TARGET) traces the runtime
526
599
  // and the selected Postgres/Neon driver into a self-contained bundle — never a static export.
527
- const buildResult = cross_spawn_1.default.sync('npx', ['next', 'build'], {
528
- cwd: appPath,
529
- stdio: ['inherit', 'pipe', 'pipe'],
530
- env: Object.assign(Object.assign({}, process.env), { NODE_ENV: 'production', HANDOFF_BUILD_TARGET: 'registry' }),
531
- });
532
- logger_1.Logger.childProcessBuffer(buildResult.stdout);
533
- logger_1.Logger.childProcessBuffer(buildResult.stderr);
534
- if (buildResult.status !== 0) {
535
- let errorMsg = `Registry app build failed with exit code ${buildResult.status}`;
536
- if (buildResult.error) {
537
- errorMsg += `\nSpawn error: ${buildResult.error.message}`;
538
- }
539
- throw new Error(errorMsg);
540
- }
600
+ yield runNextBuild(appPath, 'registry', 'Registry app build');
541
601
  // Assemble the deployable package. Standalone tracing is rooted at the package (modulePath), so the
542
602
  // app's traced files land under `<relAppDir>` inside the standalone tree; static assets and public
543
603
  // files are not traced and must be copied alongside the server entry.
@@ -561,7 +621,7 @@ const buildRegistryApp = (handoff_1, ...args_1) => __awaiter(void 0, [handoff_1,
561
621
  // `handoff-app` source/CLI (`handoff-app db:migrate`) as a controlled release step (e.g. CI/CD),
562
622
  // never from the deployed bundle. See the generated README for the migrate-then-deploy flow.
563
623
  const entryRelativePath = path_1.default.relative(output, serverEntry).split(path_1.default.sep).join('/');
564
- yield writeRegistryDeploymentReadme(output, entryRelativePath, (0, adapter_1.resolveDatabaseUrlEnv)(handoff.config));
624
+ yield writeRegistryDeploymentReadme(output, entryRelativePath, (0, driver_1.resolveDatabaseUrlEnv)(handoff.config));
565
625
  logger_1.Logger.success(`Packaged registry app at ${output} (start: \`node ${entryRelativePath}\`, migrate: \`handoff-app db:migrate\`).`);
566
626
  });
567
627
  /**
@@ -17,7 +17,8 @@ const fs_extra_1 = __importDefault(require("fs-extra"));
17
17
  const path_1 = __importDefault(require("path"));
18
18
  const config_1 = require("../config");
19
19
  const resolve_1 = require("../registry/asset-storage/resolve");
20
- const adapter_1 = require("../registry/db/adapter");
20
+ const connection_1 = require("../registry/connection");
21
+ const driver_1 = require("../registry/db/driver");
21
22
  const paths_1 = require("./paths");
22
23
  /**
23
24
  * Publishes the tokens API files to the public directory.
@@ -55,7 +56,7 @@ exports.generateTokensApi = generateTokensApi;
55
56
  * Build the server-only runtime config consumed by the docs read API.
56
57
  *
57
58
  * The browser-facing `client.config.json` carries only the resolved mode; the server additionally
58
- * needs the registry connection *inputs* (selected adapter + the *name* of the database-URL env
59
+ * needs the registry connection *inputs* (selected driver + the *name* of the database-URL env
59
60
  * var) to back the registry-mode docs read API. These are non-secret — the connection-string value
60
61
  * itself is never persisted, only resolved from the env var at request time.
61
62
  */
@@ -65,9 +66,8 @@ const buildServerRuntimeConfig = (config, modeOverride) => {
65
66
  return {
66
67
  mode: (_b = modeOverride !== null && modeOverride !== void 0 ? modeOverride : (_a = config === null || config === void 0 ? void 0 : config.runtime) === null || _a === void 0 ? void 0 : _a.mode) !== null && _b !== void 0 ? _b : 'workspace',
67
68
  registry: {
68
- adapter: (0, adapter_1.resolveRegistryAdapter)(config),
69
- databaseUrlEnv: (0, adapter_1.resolveDatabaseUrlEnv)(config),
70
- apiTokenEnv: (0, adapter_1.resolveApiTokenEnv)(config),
69
+ driver: (0, driver_1.resolveRegistryDriver)(config),
70
+ databaseUrlEnv: (0, driver_1.resolveDatabaseUrlEnv)(config),
71
71
  },
72
72
  assetStorage: {
73
73
  adapter: assetStorage.adapterKind,
@@ -84,17 +84,18 @@ const buildServerRuntimeConfig = (config, modeOverride) => {
84
84
  * connection inputs from. Both are always written together so they never drift.
85
85
  */
86
86
  const persistClientConfig = (handoff_1, ...args_1) => __awaiter(void 0, [handoff_1, ...args_1], void 0, function* (handoff, options = {}) {
87
+ var _a;
87
88
  const appPath = (0, paths_1.getAppPath)(handoff);
88
89
  // Ensure directory exists
89
90
  yield fs_extra_1.default.ensureDir(appPath);
90
91
  const clientConfig = (0, config_1.getClientConfig)(handoff.config);
91
- if (options.runtimeModeOverride) {
92
- // A forced registry mode (the registry build target) is never a connected workspace, so the
93
- // baked client config must report `connected: false` even when packaged from a connected
94
- // workspace project.
95
- const connected = options.runtimeModeOverride === 'registry' ? false : clientConfig.runtime.connected;
96
- clientConfig.runtime = Object.assign(Object.assign({}, clientConfig.runtime), { mode: options.runtimeModeOverride, connected });
97
- }
92
+ const mode = (_a = options.runtimeModeOverride) !== null && _a !== void 0 ? _a : clientConfig.runtime.mode;
93
+ // A registry build target is never a connected workspace, so it must bake `connected: false`
94
+ // even when packaged from a connected project. Otherwise a workspace is connected whenever a
95
+ // registry URL resolves — from config/env or a saved device login — so `login` alone surfaces
96
+ // the publish/checkout UI without also requiring HANDOFF_REGISTRY_URL.
97
+ const connected = mode === 'registry' ? false : Boolean((yield (0, connection_1.resolveAuthenticatedRegistryConnection)(handoff.config, handoff.workingPath)).url);
98
+ clientConfig.runtime = Object.assign(Object.assign({}, clientConfig.runtime), { mode, connected });
98
99
  yield fs_extra_1.default.writeJson(path_1.default.resolve(appPath, 'client.config.json'), { config: clientConfig }, { spaces: 2 });
99
100
  yield fs_extra_1.default.writeJson(path_1.default.resolve(appPath, 'runtime.server.json'), buildServerRuntimeConfig(handoff.config, options.runtimeModeOverride), { spaces: 2 });
100
101
  });
@@ -51,13 +51,11 @@ const mirrorDirectory = (sourcePath, destinationPath) => __awaiter(void 0, void
51
51
  yield fs_extra_1.default.copy(sourcePath, destinationPath, { overwrite: true });
52
52
  });
53
53
  /**
54
- * Stage the fetch-produced asset download bundles (`exported/<id>/{collection}.zip`) into the app
55
- * public root, where the docs read API's workspace asset store serves them at
56
- * `/api/docs/assets/{collection}/{collection}.zip` (the canonical download URL). `fetch` writes these
57
- * bundles, but a plain `build`/`start` cleans and re-stages `.handoff` without carrying them, so the
58
- * download links would 404 until a fetch happened to run. Copying the committed export bundles here
59
- * makes the route resolve deterministically in workspace dev and static builds. Absent bundles (a
60
- * project with no logos/icons) are skipped.
54
+ * Copy the fetch-produced asset bundles (`exported/<id>/{collection}.zip`) into the app's public
55
+ * root, where the docs read API serves them at `/api/docs/assets/{collection}/{collection}.zip`.
56
+ * `fetch` writes these bundles, but `build`/`start` cleans and re-stages `.handoff` without them, so
57
+ * the download links 404 until the next fetch. Copying them here keeps the route working in workspace
58
+ * dev and static builds. Projects with no logos/icons have no bundles, so those are skipped.
61
59
  */
62
60
  const stageAssetDownloadBundles = (handoff, destinationPublicPath) => __awaiter(void 0, void 0, void 0, function* () {
63
61
  const bundles = [
@@ -90,8 +88,8 @@ const syncPublicFiles = (handoff) => __awaiter(void 0, void 0, void 0, function*
90
88
  });
91
89
  yield mirrorDirectory(sourceApiPath, destinationApiPath);
92
90
  }
93
- // Stage the asset download bundles regardless of whether the workspace has a `public/` dir — they
94
- // come from the fetch output, not the workspace public tree.
91
+ // Stage the asset bundles even when the workspace has no `public/` dir; they come from the fetch
92
+ // output, not the workspace public tree.
95
93
  yield stageAssetDownloadBundles(handoff, destinationPublicPath);
96
94
  });
97
95
  exports.syncPublicFiles = syncPublicFiles;
@@ -181,12 +181,12 @@ const materializeDocsReadModel = (handoff, outDir) => __awaiter(void 0, void 0,
181
181
  });
182
182
  exports.materializeDocsReadModel = materializeDocsReadModel;
183
183
  /**
184
- * Materialize the whole-collection asset download bundles at the canonical asset route
185
- * (`api/docs/assets/{collection}/{collection}.zip`) — the same URL the docs read API serves in
186
- * registry mode, so the "Download" links resolve identically on a static host. The bundles come from
187
- * the fetch output (`exported/<id>/{collection}.zip`); `next build` never re-creates them, and the
188
- * fetch-time copy into the app `public/` is wiped by the build's clean+restage, so they are copied
189
- * here. Absent bundles (a project with no logos/icons) are skipped, not an error.
184
+ * Write the whole-collection asset bundles to the asset route
185
+ * (`api/docs/assets/{collection}/{collection}.zip`), the same URL the docs read API serves in
186
+ * registry mode, so the "Download" links work identically on a static host. The bundles come from the
187
+ * fetch output (`exported/<id>/{collection}.zip`); `next build` never re-creates them and the
188
+ * fetch-time copy into `public/` is wiped by the build's clean+restage, so we copy them here again.
189
+ * Projects with no logos/icons have no bundles, so those are skipped rather than treated as an error.
190
190
  */
191
191
  const materializeAssetDownloads = (handoff, docsRoot) => __awaiter(void 0, void 0, void 0, function* () {
192
192
  const bundles = [
@@ -74,7 +74,7 @@ const writeStaticVercelOutput = (handoff, exportDir) => __awaiter(void 0, void 0
74
74
  });
75
75
  exports.writeStaticVercelOutput = writeStaticVercelOutput;
76
76
  /**
77
- * Node.js runtime identifier baked into the registry function's `.vc-config.json`. The `pg` adapter
77
+ * Node.js runtime identifier baked into the registry function's `.vc-config.json`. The `pg` driver
78
78
  * (and the Neon serverless driver in its Node fallback) need the Node.js runtime, not the edge
79
79
  * runtime — this is the required baseline. Vercel resolves the patch
80
80
  * version within this major; pinning the major keeps the artifact stable.
@@ -102,7 +102,7 @@ const MUTABLE_REGISTRY_PAGE_SRC_ROUTE = '/[...slug]';
102
102
  * data routes resolve the same way they do under the standalone server. `minimalMode:false` keeps
103
103
  * the function doing full in-process routing (the catch-all route forwards everything here).
104
104
  *
105
- * Runtime mode, adapter, and the DB env-var *name* are already baked into the resolved config the
105
+ * Runtime mode, driver, and the DB env-var *name* are already baked into the resolved config the
106
106
  * standalone build wrote to `.next/required-server-files.json`; the connection-string value is still
107
107
  * read from the named env var at request time, never baked.
108
108
  */
@@ -17,7 +17,7 @@
17
17
  export declare const ARTIFACTS_ROUTE_SEGMENT = "api/docs/artifacts";
18
18
  /** Route prefix for docs read API metadata (`.json`) reads. */
19
19
  export declare const DOCS_ROUTE_SEGMENT = "api/docs";
20
- /** Canonical route prefix all asset content URLs are served under (mode-independent). */
20
+ /** Route prefix all asset content URLs are served under, regardless of runtime mode. */
21
21
  export declare const ASSETS_ROUTE_SEGMENT = "api/docs/assets";
22
22
  /**
23
23
  * Normalize a configured basePath to either an empty string or a single-leading-slash,
@@ -50,18 +50,18 @@ export declare const buildComponentDetailUrl: (id: string, basePath?: string | n
50
50
  */
51
51
  export declare const buildPatternDetailUrl: (id: string, basePath?: string | null) => string;
52
52
  /**
53
- * Build the canonical, basePath-aware content URL for one asset within a collection
53
+ * Build the basePath-aware content URL for a single asset in a collection
54
54
  * (`{basePath}/api/docs/assets/{collection}/{logicalPath}`). This is the same route the docs read API
55
55
  * serves in every runtime mode: from the database in registry mode, and from statically materialized
56
- * files in a static export. Each segment is individually encoded and traversal segments are rejected,
57
- * mirroring {@link buildArtifactUrl}.
56
+ * files in a static export. Each segment is encoded on its own and traversal segments are rejected,
57
+ * matching {@link buildArtifactUrl}.
58
58
  *
59
59
  * @throws If the collection or logical path has no usable segments, or contains a `.`/`..` segment.
60
60
  */
61
61
  export declare const buildAssetUrl: (collection: string, logicalPath: string, basePath?: string | null) => string;
62
62
  /**
63
- * Build the canonical download URL for a collection's whole-collection zip bundle
64
- * (`{basePath}/api/docs/assets/{collection}/{collection}.zip`) — the default target for the docs
65
- * "Download" links, overridable via `assets_zip_links`.
63
+ * Build the download URL for a collection's zip bundle
64
+ * (`{basePath}/api/docs/assets/{collection}/{collection}.zip`). This is the default target for the
65
+ * docs "Download" links and can be overridden via `assets_zip_links`.
66
66
  */
67
67
  export declare const buildAssetDownloadUrl: (collection: string, basePath?: string | null) => string;
@@ -20,7 +20,7 @@ exports.buildAssetDownloadUrl = exports.buildAssetUrl = exports.buildPatternDeta
20
20
  exports.ARTIFACTS_ROUTE_SEGMENT = 'api/docs/artifacts';
21
21
  /** Route prefix for docs read API metadata (`.json`) reads. */
22
22
  exports.DOCS_ROUTE_SEGMENT = 'api/docs';
23
- /** Canonical route prefix all asset content URLs are served under (mode-independent). */
23
+ /** Route prefix all asset content URLs are served under, regardless of runtime mode. */
24
24
  exports.ASSETS_ROUTE_SEGMENT = 'api/docs/assets';
25
25
  /**
26
26
  * Normalize a configured basePath to either an empty string or a single-leading-slash,
@@ -87,11 +87,11 @@ exports.buildComponentDetailUrl = buildComponentDetailUrl;
87
87
  const buildPatternDetailUrl = (id, basePath) => `${(0, exports.normalizeBasePath)(basePath)}/${exports.DOCS_ROUTE_SEGMENT}/patterns/${encodeURIComponent(id)}.json`;
88
88
  exports.buildPatternDetailUrl = buildPatternDetailUrl;
89
89
  /**
90
- * Build the canonical, basePath-aware content URL for one asset within a collection
90
+ * Build the basePath-aware content URL for a single asset in a collection
91
91
  * (`{basePath}/api/docs/assets/{collection}/{logicalPath}`). This is the same route the docs read API
92
92
  * serves in every runtime mode: from the database in registry mode, and from statically materialized
93
- * files in a static export. Each segment is individually encoded and traversal segments are rejected,
94
- * mirroring {@link buildArtifactUrl}.
93
+ * files in a static export. Each segment is encoded on its own and traversal segments are rejected,
94
+ * matching {@link buildArtifactUrl}.
95
95
  *
96
96
  * @throws If the collection or logical path has no usable segments, or contains a `.`/`..` segment.
97
97
  */
@@ -108,9 +108,9 @@ const buildAssetUrl = (collection, logicalPath, basePath) => {
108
108
  };
109
109
  exports.buildAssetUrl = buildAssetUrl;
110
110
  /**
111
- * Build the canonical download URL for a collection's whole-collection zip bundle
112
- * (`{basePath}/api/docs/assets/{collection}/{collection}.zip`) — the default target for the docs
113
- * "Download" links, overridable via `assets_zip_links`.
111
+ * Build the download URL for a collection's zip bundle
112
+ * (`{basePath}/api/docs/assets/{collection}/{collection}.zip`). This is the default target for the
113
+ * docs "Download" links and can be overridden via `assets_zip_links`.
114
114
  */
115
115
  const buildAssetDownloadUrl = (collection, basePath) => (0, exports.buildAssetUrl)(collection, `${collection}.zip`, basePath);
116
116
  exports.buildAssetDownloadUrl = buildAssetDownloadUrl;
@@ -0,0 +1,2 @@
1
+ /** Fire-and-forget opening of a URL in the system browser. The printed URL remains the fallback. */
2
+ export declare const tryOpenBrowser: (url: string) => void;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.tryOpenBrowser = void 0;
4
+ const child_process_1 = require("child_process");
5
+ /** Fire-and-forget opening of a URL in the system browser. The printed URL remains the fallback. */
6
+ const tryOpenBrowser = (url) => {
7
+ try {
8
+ const { protocol } = new URL(url);
9
+ if (protocol !== 'http:' && protocol !== 'https:')
10
+ return;
11
+ }
12
+ catch (_a) {
13
+ return;
14
+ }
15
+ const platform = process.platform;
16
+ // rundll32 is a plain executable rather than a shell, so spawn hands it the URL as one
17
+ // untouched argument. `cmd /c start` would re-parse shell metacharacters in it.
18
+ const command = platform === 'darwin' ? 'open' : platform === 'win32' ? 'rundll32.exe' : 'xdg-open';
19
+ const args = platform === 'win32' ? ['url.dll,FileProtocolHandler', url] : [url];
20
+ try {
21
+ const child = (0, child_process_1.spawn)(command, args, {
22
+ detached: true,
23
+ stdio: 'ignore',
24
+ windowsHide: platform === 'win32',
25
+ });
26
+ child.on('error', () => undefined);
27
+ child.unref();
28
+ }
29
+ catch (_b) {
30
+ // Headless and SSH environments can use the URL printed by the login flow.
31
+ }
32
+ };
33
+ exports.tryOpenBrowser = tryOpenBrowser;
@@ -0,0 +1,11 @@
1
+ import { type CliAuth } from './store';
2
+ export interface LoginOptions {
3
+ openBrowser?: boolean;
4
+ }
5
+ /** Complete the device grant and persist the one-time plaintext access token for this workspace. */
6
+ export declare const loginWithDevice: (workingPath: string, remoteUrl: string, options?: LoginOptions) => Promise<CliAuth>;
7
+ /**
8
+ * Best-effort remote revocation. The caller must clear the local credential regardless of failure.
9
+ * The bearer header identifies the current token while the body supports RFC 7009-style handlers.
10
+ */
11
+ export declare const revokeAccessToken: (auth: CliAuth) => Promise<void>;