@mycadet/cli 0.12.7 → 0.12.9-dev.202610131452.9acab64

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 (185) hide show
  1. package/README.md +2 -2
  2. package/knip.json +1 -1
  3. package/node_modules/@mycadet/assistant-api/events/cloud-disconnected.ts +21 -0
  4. package/node_modules/@mycadet/assistant-api/events/host-bash.ts +1 -1
  5. package/node_modules/@mycadet/assistant-api/events/host-cu.ts +1 -1
  6. package/node_modules/@mycadet/assistant-api/events/host-file.ts +1 -1
  7. package/node_modules/@mycadet/assistant-api/events/notification-intent.ts +1 -1
  8. package/node_modules/@mycadet/assistant-api/events/show-cloud-login.ts +18 -0
  9. package/node_modules/@mycadet/assistant-api/index.ts +11 -11
  10. package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/package.json +2 -2
  11. package/node_modules/@mycadet/{service-contracts/src/platform-credential.ts → assistant-api/node_modules/@mycadet/service-contracts/src/cloud-credential.ts} +9 -9
  12. package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/handles.ts +3 -3
  13. package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/index.ts +1 -1
  14. package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/ingress.ts +11 -5
  15. package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/remote-web-pairing.ts +1 -1
  16. package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/rpc.ts +14 -14
  17. package/node_modules/@mycadet/assistant-api/node_modules/@mycadet/service-contracts/src/trust-rules.ts +5 -5
  18. package/node_modules/@mycadet/assistant-api/responses/home.ts +1 -1
  19. package/node_modules/@mycadet/assistant-api/surfaces.ts +4 -4
  20. package/node_modules/@mycadet/avatar-manifest/src/__tests__/notification-avatar.test.ts +1 -1
  21. package/node_modules/@mycadet/avatar-manifest/src/notification-avatar.ts +2 -2
  22. package/node_modules/@mycadet/environments/src/seeds.ts +6 -6
  23. package/node_modules/@mycadet/environments/src/shell.test.ts +1 -1
  24. package/node_modules/@mycadet/environments/src/types.ts +6 -6
  25. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/avatar-manifest/src/__tests__/notification-avatar.test.ts +1 -1
  26. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/avatar-manifest/src/notification-avatar.ts +2 -2
  27. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/environments/src/seeds.ts +6 -6
  28. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/environments/src/shell.test.ts +1 -1
  29. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/environments/src/types.ts +6 -6
  30. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/package.json +2 -2
  31. package/node_modules/@mycadet/{assistant-api/node_modules/@mycadet/service-contracts/src/platform-credential.ts → local-mode/node_modules/@mycadet/service-contracts/src/cloud-credential.ts} +9 -9
  32. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/handles.ts +3 -3
  33. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/index.ts +1 -1
  34. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/ingress.ts +11 -5
  35. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/remote-web-pairing.ts +1 -1
  36. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/rpc.ts +14 -14
  37. package/node_modules/@mycadet/local-mode/node_modules/@mycadet/service-contracts/src/trust-rules.ts +5 -5
  38. package/node_modules/@mycadet/local-mode/src/__tests__/pair.test.ts +5 -5
  39. package/node_modules/@mycadet/local-mode/src/__tests__/retire.test.ts +1 -1
  40. package/node_modules/@mycadet/local-mode/src/config.ts +2 -2
  41. package/node_modules/@mycadet/local-mode/src/index.ts +1 -1
  42. package/node_modules/@mycadet/local-mode/src/lockfile-contract.test.ts +3 -3
  43. package/node_modules/@mycadet/local-mode/src/lockfile-contract.ts +10 -5
  44. package/node_modules/@mycadet/local-mode/src/lockfile.test.ts +15 -15
  45. package/node_modules/@mycadet/local-mode/src/lockfile.ts +9 -9
  46. package/node_modules/@mycadet/local-mode/src/pair.ts +2 -2
  47. package/node_modules/@mycadet/local-mode/src/retire.ts +3 -3
  48. package/node_modules/@mycadet/local-mode/src/teleport-backup-policy.ts +2 -2
  49. package/node_modules/@mycadet/local-mode/src/unpair.ts +2 -2
  50. package/node_modules/@mycadet/service-contracts/package.json +2 -2
  51. package/node_modules/@mycadet/{local-mode/node_modules/@mycadet/service-contracts/src/platform-credential.ts → service-contracts/src/cloud-credential.ts} +9 -9
  52. package/node_modules/@mycadet/service-contracts/src/handles.ts +3 -3
  53. package/node_modules/@mycadet/service-contracts/src/index.ts +1 -1
  54. package/node_modules/@mycadet/service-contracts/src/ingress.ts +11 -5
  55. package/node_modules/@mycadet/service-contracts/src/remote-web-pairing.ts +1 -1
  56. package/node_modules/@mycadet/service-contracts/src/rpc.ts +14 -14
  57. package/node_modules/@mycadet/service-contracts/src/trust-rules.ts +5 -5
  58. package/package.json +6 -4
  59. package/src/__tests__/assistant-api-key-resolution.test.ts +6 -6
  60. package/src/__tests__/{assistant-client-platform-auth.test.ts → assistant-client-cloud-auth.test.ts} +10 -10
  61. package/src/__tests__/assistant-client-refresh.test.ts +2 -2
  62. package/src/__tests__/backup.test.ts +86 -91
  63. package/src/__tests__/client-token.test.ts +1 -1
  64. package/src/__tests__/client-tui-refresh.test.ts +1 -1
  65. package/src/__tests__/client-web-config.test.ts +1 -2
  66. package/src/__tests__/{platform-client.test.ts → cloud-client.test.ts} +59 -59
  67. package/src/__tests__/{platform-releases.test.ts → cloud-releases.test.ts} +28 -18
  68. package/src/__tests__/{cloudflare-tunnel.test.ts → cloudflare.test.ts} +1 -1
  69. package/src/__tests__/connect-import.test.ts +55 -39
  70. package/src/__tests__/debug.test.ts +38 -38
  71. package/src/__tests__/ensure-binary.test.ts +182 -0
  72. package/src/__tests__/guardian-token.test.ts +149 -56
  73. package/src/__tests__/hatch-provider-secrets.test.ts +2 -2
  74. package/src/__tests__/helpers/guardian-token.ts +22 -0
  75. package/src/__tests__/helpers/http.ts +10 -0
  76. package/src/__tests__/nginx-ingress.test.ts +15 -20
  77. package/src/__tests__/ngrok-spawn.test.ts +49 -0
  78. package/src/__tests__/orphan-detection.test.ts +1 -1
  79. package/src/__tests__/pair.test.ts +149 -85
  80. package/src/__tests__/{ps-platform-status.test.ts → ps-cloud-status.test.ts} +80 -26
  81. package/src/__tests__/ps-table.test.ts +79 -0
  82. package/src/__tests__/{recover.test.ts → restore.test.ts} +29 -28
  83. package/src/__tests__/retire.test.ts +58 -199
  84. package/src/__tests__/root-process-lockfile.test.ts +99 -0
  85. package/src/__tests__/sleep.test.ts +19 -19
  86. package/src/__tests__/statefulset.test.ts +8 -8
  87. package/src/__tests__/{tailscale-tunnel.test.ts → tailscale.test.ts} +2 -2
  88. package/src/__tests__/teleport-backup.test.ts +3 -3
  89. package/src/__tests__/teleport.test.ts +180 -211
  90. package/src/__tests__/tunnel.test.ts +490 -270
  91. package/src/__tests__/uninstall.test.ts +150 -9
  92. package/src/__tests__/upgrade-local.test.ts +3 -3
  93. package/src/__tests__/upgrade-replay-env.test.ts +12 -12
  94. package/src/__tests__/wake.test.ts +260 -43
  95. package/src/__tests__/workos-pkce.test.ts +2 -2
  96. package/src/commands/backup.ts +24 -24
  97. package/src/commands/client.ts +76 -78
  98. package/src/commands/completions.test.ts +104 -0
  99. package/src/commands/completions.ts +87 -0
  100. package/src/commands/debug.ts +20 -20
  101. package/src/commands/exec.ts +12 -12
  102. package/src/commands/flags.ts +2 -2
  103. package/src/commands/hatch.ts +27 -24
  104. package/src/commands/login.ts +22 -22
  105. package/src/commands/logs.ts +8 -8
  106. package/src/commands/pair.ts +140 -37
  107. package/src/commands/ps.ts +232 -86
  108. package/src/commands/restart.ts +30 -0
  109. package/src/commands/restore.ts +140 -22
  110. package/src/commands/retire.ts +48 -150
  111. package/src/commands/rollback.ts +20 -26
  112. package/src/commands/setup.ts +2 -2
  113. package/src/commands/sleep.ts +2 -2
  114. package/src/commands/teleport.ts +110 -129
  115. package/src/commands/terminal.ts +158 -18
  116. package/src/commands/tunnel.ts +156 -175
  117. package/src/commands/uninstall.ts +137 -54
  118. package/src/commands/unpair.ts +3 -3
  119. package/src/commands/upgrade.ts +30 -54
  120. package/src/commands/use.ts +1 -1
  121. package/src/commands/wake.ts +166 -50
  122. package/src/index.ts +18 -9
  123. package/src/lib/__tests__/{platform-client-signed-url.test.ts → cloud-client-signed-url.test.ts} +16 -16
  124. package/src/lib/__tests__/docker.test.ts +168 -20
  125. package/src/lib/__tests__/job-polling.test.ts +2 -2
  126. package/src/lib/__tests__/local-gateway-cli.test.ts +42 -0
  127. package/src/lib/__tests__/local-runtime-client.test.ts +4 -4
  128. package/src/lib/__tests__/runtime-url.test.ts +1 -1
  129. package/src/lib/assistant-api-key-resolution.ts +5 -5
  130. package/src/lib/assistant-client.ts +8 -8
  131. package/src/lib/assistant-config.ts +58 -12
  132. package/src/lib/{platform-client.ts → cloud-client.ts} +105 -99
  133. package/src/lib/cloud-login.ts +23 -24
  134. package/src/lib/{platform-releases.ts → cloud-releases.ts} +11 -9
  135. package/src/lib/cloud-values.ts +3 -0
  136. package/src/lib/color.ts +4 -0
  137. package/src/lib/config-utils.ts +1 -1
  138. package/src/lib/detached-process.ts +1 -1
  139. package/src/lib/docker.ts +300 -111
  140. package/src/lib/ensure-binary.ts +171 -0
  141. package/src/lib/environments/__tests__/paths.test.ts +29 -3
  142. package/src/lib/environments/__tests__/resolve.test.ts +26 -26
  143. package/src/lib/environments/paths.ts +16 -8
  144. package/src/lib/environments/resolve.ts +10 -12
  145. package/src/lib/flag-args.test.ts +1 -1
  146. package/src/lib/flag-args.ts +1 -1
  147. package/src/lib/guardian-token.ts +37 -5
  148. package/src/lib/hatch-local.ts +32 -29
  149. package/src/lib/hatch-next-steps.ts +5 -9
  150. package/src/lib/health-check.ts +6 -6
  151. package/src/lib/ingress-config.ts +8 -35
  152. package/src/lib/job-polling.ts +2 -2
  153. package/src/lib/live-voice/connection.ts +12 -12
  154. package/src/lib/live-voice/tunnel.test.ts +11 -11
  155. package/src/lib/live-voice/tunnel.ts +15 -15
  156. package/src/lib/local-runtime-client.ts +20 -20
  157. package/src/lib/local.ts +46 -34
  158. package/src/lib/nginx-ingress.ts +40 -15
  159. package/src/lib/pair.ts +1 -1
  160. package/src/lib/process.ts +1 -1
  161. package/src/lib/provider-secrets.ts +3 -3
  162. package/src/lib/retire-apple-container.ts +1 -1
  163. package/src/lib/runtime-url.ts +8 -8
  164. package/src/lib/statefulset.ts +2 -2
  165. package/src/lib/step-runner.ts +6 -1
  166. package/src/lib/sync-cloud-assistants.ts +19 -19
  167. package/src/lib/teleport-backup.ts +4 -4
  168. package/src/lib/terminal-client.ts +17 -17
  169. package/src/lib/terminal-session.ts +24 -24
  170. package/src/lib/{tunnel-edge.ts → tunnel.ts} +199 -11
  171. package/src/lib/tunnels/cadet.ts +29 -0
  172. package/src/lib/{cloudflare-tunnel.ts → tunnels/cloudflare.ts} +36 -15
  173. package/src/lib/tunnels/gateway-notify.ts +61 -0
  174. package/src/lib/{ngrok.ts → tunnels/ngrok.ts} +350 -89
  175. package/src/lib/{tailscale-tunnel.ts → tunnels/tailscale.ts} +53 -9
  176. package/src/lib/upgrade-lifecycle.ts +8 -21
  177. package/src/lib/upgrade-preflight.ts +2 -2
  178. package/src/lib/workos-pkce.ts +5 -5
  179. package/node_modules/@mycadet/assistant-api/events/platform-disconnected.ts +0 -21
  180. package/node_modules/@mycadet/assistant-api/events/show-platform-login.ts +0 -20
  181. package/src/commands/recover.ts +0 -131
  182. package/src/commands/roadmap.ts +0 -459
  183. package/src/commands/ssh.ts +0 -144
  184. package/src/commands/workflows.ts +0 -305
  185. package/src/lib/managed-cloud.ts +0 -3
@@ -3,9 +3,12 @@
3
3
  *
4
4
  * Print the pairing link for another device, and the same link rendered as a
5
5
  * QR code. Runs on the machine hosting the assistant: it mints a remote-web
6
- * pairing challenge over the loopback gateway and approves it immediately, so
7
- * running this command IS the local-presence proof and one scan (or one paste
8
- * into `cadet connect import`) completes the pairing.
6
+ * pairing challenge through the gateway CLI (`gateway pairing mint <url>` —
7
+ * locally, or docker-exec'd into the gateway container) which approves it
8
+ * immediately, so running this command IS the local-presence proof and one
9
+ * scan (or one paste into `cadet connect import`) completes the pairing.
10
+ * local and docker assistants mint without any HTTP; other topologies keep
11
+ * the loopback HTTP mint.
9
12
  *
10
13
  * `--web-approve` is the reverse direction: approve a code a device already
11
14
  * shows, for pairings that device started on its own.
@@ -38,8 +41,11 @@ import {
38
41
  import { GATEWAY_PORT } from "../lib/constants.js";
39
42
  import { getCurrentEnvironment } from "../lib/environments/resolve.js";
40
43
  import { isLoopbackUrl, loopbackSafeFetch } from "../lib/loopback-fetch.js";
44
+ import { dockerResourceNames } from "../lib/docker.js";
45
+ import { gatewayCliInvocation } from "../lib/local.js";
41
46
  import { formatWebApproveFailure, parseGatewayErrorCode } from "../lib/pair.js";
42
47
  import { STALE_CLI_UPDATE_HINT } from "../lib/stale-cli-hint.js";
48
+ import { execOutput } from "../lib/step-runner.js";
43
49
 
44
50
  function assistantDisplayName(entry: AssistantEntry): string {
45
51
  return entry.name || entry.assistantName || entry.assistantId;
@@ -89,9 +95,10 @@ function printUsage(): void {
89
95
  USAGE:
90
96
  cadet pair [assistant] [options]
91
97
 
92
- Mints a pairing challenge on the assistant's gateway over loopback and
93
- approves it on the spot (running this command on the host machine is the
94
- approval), then prints the pairing link and the same link as a QR code.
98
+ Mints a pairing challenge on the assistant's gateway (via the gateway CLI
99
+ for local and docker assistants, over loopback HTTP otherwise) and approves
100
+ it on the spot (running this command on the host machine is the approval),
101
+ then prints the pairing link and the same link as a QR code.
95
102
  Scanning the QR, opening the link, or passing it to 'cadet connect import'
96
103
  on the other device completes the pairing in one step.
97
104
 
@@ -214,26 +221,122 @@ async function exitOnHttpError(response: Response): Promise<Response> {
214
221
  return response;
215
222
  }
216
223
 
217
- /** Create a remote-web pairing challenge (RFC 8628 device-code flow). */
218
- async function createRemoteWebPairingChallenge(
219
- gatewayUrl: string,
224
+ /**
225
+ * Mint (and approve) a remote-web pairing challenge. `local` and `docker`
226
+ * remotes mint through the gateway CLI — `gateway pairing mint <url>` run on
227
+ * the assistant's host (locally, or docker-exec'd into the gateway container)
228
+ * — replacing the old loopback HTTP mint whose loopback-peer gate
229
+ * containerized gateways could never satisfy (published-port NAT rewrites
230
+ * host-originated peers to the docker bridge address). The mint executes the
231
+ * pairing logic directly against the file-backed challenge store, so the
232
+ * running gateway's verification routes see it with no IPC round-trip.
233
+ * Process execution is the local-presence proof; the mint command approves
234
+ * on the spot, so the returned challenge is already paired-ready. Other
235
+ * topologies keep the loopback HTTP mint they used before.
236
+ */
237
+ async function mintPairingChallengeOrExit(
238
+ entry: AssistantEntry,
239
+ publicBaseUrl: string,
240
+ ): Promise<RemoteWebPairingChallengeResponse> {
241
+ if (entry.cloud !== "local" && entry.cloud !== "docker") {
242
+ return mintPairingChallengeOverLoopbackOrExit(entry, publicBaseUrl);
243
+ }
244
+
245
+ let stdout: string;
246
+ try {
247
+ if (entry.cloud === "docker") {
248
+ stdout = await execOutput(
249
+ "docker",
250
+ [
251
+ "exec",
252
+ dockerResourceNames(entry.assistantId).gatewayContainer,
253
+ "bun",
254
+ "run",
255
+ "src/cli/index.ts",
256
+ "pairing",
257
+ "mint",
258
+ publicBaseUrl,
259
+ ],
260
+ { timeoutMs: 30_000 },
261
+ );
262
+ } else {
263
+ const invocation = gatewayCliInvocation(entry.resources);
264
+ stdout = await execOutput(
265
+ invocation.command,
266
+ [...invocation.args, "pairing", "mint", publicBaseUrl],
267
+ {
268
+ cwd: invocation.cwd,
269
+ env: invocation.env,
270
+ timeoutMs: 30_000,
271
+ },
272
+ );
273
+ }
274
+ } catch (err) {
275
+ console.error(
276
+ `Error: could not mint a pairing challenge on the gateway ` +
277
+ `(${err instanceof Error ? err.message : String(err)}).`,
278
+ );
279
+ console.error("Is the assistant running? Try `cadet wake`.");
280
+ process.exit(1);
281
+ }
282
+
283
+ const lastLine = stdout.split("\n").filter(Boolean).pop() ?? "";
284
+ let challenge: RemoteWebPairingChallengeResponse;
285
+ try {
286
+ challenge = JSON.parse(lastLine) as RemoteWebPairingChallengeResponse;
287
+ } catch {
288
+ console.error(
289
+ "Error: could not parse the gateway's pairing mint output.",
290
+ );
291
+ process.exit(1);
292
+ }
293
+ if (!challenge.deviceCode || !challenge.userCode) {
294
+ console.error(
295
+ "Error: the gateway's pairing mint output is missing the challenge codes.",
296
+ );
297
+ process.exit(1);
298
+ }
299
+ return challenge;
300
+ }
301
+
302
+ /**
303
+ * Mint a challenge over the gateway's loopback HTTP route — the path every
304
+ * topology took before the gateway-CLI mint existed. Kept for remotes that
305
+ * are neither `local` nor `docker` (apple containers, SSH-managed hosts,
306
+ * cloud-managed), where no gateway-CLI execution surface is defined.
307
+ */
308
+ async function mintPairingChallengeOverLoopbackOrExit(
309
+ entry: AssistantEntry,
220
310
  publicBaseUrl: string,
221
311
  ): Promise<RemoteWebPairingChallengeResponse> {
312
+ // Mint over loopback (localUrl avoids mDNS for same-machine calls), then
313
+ // approve immediately: running this CLI on the host IS the local-presence
314
+ // proof, so the other device completes pairing in one step from the link.
315
+ const mintUrl = (
316
+ entry.localUrl ||
317
+ entry.runtimeUrl ||
318
+ `http://127.0.0.1:${GATEWAY_PORT}`
319
+ ).replace(/\/+$/, "");
222
320
  const response = await gatewayPostOrExit(
223
- gatewayUrl,
321
+ mintUrl,
224
322
  "/v1/remote-web/pairing-challenge",
225
323
  { publicBaseUrl } satisfies RemoteWebPairingChallengeRequest,
226
324
  );
227
- return (await response.json()) as RemoteWebPairingChallengeResponse;
325
+ const challenge: RemoteWebPairingChallengeResponse =
326
+ await response.json();
327
+ await gatewayPostOrExit(mintUrl, "/v1/remote-web/pairing-verification", {
328
+ userCode: challenge.userCode,
329
+ } satisfies RemoteWebPairingVerificationRequest);
330
+ return challenge;
228
331
  }
229
332
 
230
333
  /**
231
334
  * Approve a pending pairing challenge by its user code, the local-presence
232
335
  * proof for the device-code flow. Single owner of the pairing-verification
233
- * route and request body: the link flow approves via
234
- * {@link approveRemoteWebPairing} (generic exit on non-2xx), while
235
- * `--web-approve` calls this directly to inspect rejections and print mismatch
236
- * diagnostics (see {@link formatWebApproveFailure}).
336
+ * route and request body: `--web-approve` calls this directly to inspect
337
+ * rejections and print mismatch diagnostics (see
338
+ * {@link formatWebApproveFailure}); the link flow needs no separate
339
+ * verification call — the gateway-CLI mint approves on the spot.
237
340
  */
238
341
  async function postPairingVerification(
239
342
  gatewayUrl: string,
@@ -244,20 +347,6 @@ async function postPairingVerification(
244
347
  } satisfies RemoteWebPairingVerificationRequest);
245
348
  }
246
349
 
247
- /**
248
- * Approve the challenge this command just minted so one scan completes
249
- * pairing, exiting with a generic message on non-2xx.
250
- */
251
- async function approveRemoteWebPairing(
252
- gatewayUrl: string,
253
- userCode: string,
254
- ): Promise<RemoteWebPairingVerificationResponse> {
255
- const response = await exitOnHttpError(
256
- await postPairingVerification(gatewayUrl, userCode),
257
- );
258
- return (await response.json()) as RemoteWebPairingVerificationResponse;
259
- }
260
-
261
350
  export const pair = Object.assign(
262
351
  async function pair(): Promise<void> {
263
352
  const rawArgs = process.argv.slice(3);
@@ -355,6 +444,24 @@ export const pair = Object.assign(
355
444
  }
356
445
  }
357
446
 
447
+ // Minting requires host presence on this machine. Native and docker
448
+ // topologies qualify (the latter via docker exec); an assistant this
449
+ // machine only knows through `connect import` has no host presence here
450
+ // at all, and `cadet pair` never worked against it (its loopback gate
451
+ // denied remote mints). Refuse clearly instead of a confusing mint
452
+ // failure.
453
+ if (entry.cloud === "paired") {
454
+ console.error(
455
+ `Error: ${formatAssistantReference(entry)} was paired as a remote ` +
456
+ "assistant (cadet connect import), so this machine does not host it.",
457
+ );
458
+ console.error(
459
+ "Run `cadet pair` on the machine hosting the assistant, or approve " +
460
+ "a code it shows with `cadet pair --web-approve <code>`.",
461
+ );
462
+ process.exit(1);
463
+ }
464
+
358
465
  // Mint over loopback (localUrl avoids mDNS for same-machine calls), but
359
466
  // advertise a REACHABLE url in the link, since the loopback url would point
360
467
  // the other device at its own localhost. Prefer an explicit --url, then the
@@ -437,14 +544,10 @@ export const pair = Object.assign(
437
544
  }
438
545
  const publicBaseUrl = resolved.url;
439
546
 
440
- // Mint a challenge and immediately approve it: running this CLI on the host
441
- // IS the local-presence proof, so the other device completes pairing in one
442
- // step from the link alone.
443
- const challenge = await createRemoteWebPairingChallenge(
444
- mintUrl,
445
- publicBaseUrl,
446
- );
447
- await approveRemoteWebPairing(mintUrl, challenge.userCode);
547
+ // Mint a challenge (approved on the spot by the gateway-CLI invocation):
548
+ // running this CLI on the host IS the local-presence proof, so the other
549
+ // device completes pairing in one step from the link alone.
550
+ const challenge = await mintPairingChallengeOrExit(entry, publicBaseUrl);
448
551
  const pairUrl = buildRemoteWebPairingUrl(challenge);
449
552
  const displayName = assistantDisplayName(entry);
450
553
  const appUrl = appVariant