@vibe-commit/capture 0.4.5 → 0.6.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 (92) hide show
  1. package/README.md +57 -1
  2. package/dist/atomic_write.d.ts +14 -0
  3. package/dist/atomic_write.js +33 -0
  4. package/dist/atomic_write.js.map +1 -0
  5. package/dist/commands/auth.d.ts +26 -2
  6. package/dist/commands/auth.js +68 -13
  7. package/dist/commands/auth.js.map +1 -1
  8. package/dist/commands/browser_credential.d.ts +39 -12
  9. package/dist/commands/browser_credential.js +84 -11
  10. package/dist/commands/browser_credential.js.map +1 -1
  11. package/dist/commands/connect.d.ts +6 -2
  12. package/dist/commands/connect.js +140 -42
  13. package/dist/commands/connect.js.map +1 -1
  14. package/dist/commands/credential_problem.d.ts +43 -0
  15. package/dist/commands/credential_problem.js +81 -0
  16. package/dist/commands/credential_problem.js.map +1 -0
  17. package/dist/commands/device_credential.d.ts +47 -0
  18. package/dist/commands/device_credential.js +275 -0
  19. package/dist/commands/device_credential.js.map +1 -0
  20. package/dist/commands/finish_mint.d.ts +73 -0
  21. package/dist/commands/finish_mint.js +140 -0
  22. package/dist/commands/finish_mint.js.map +1 -0
  23. package/dist/commands/probe.d.ts +14 -0
  24. package/dist/commands/probe.js +29 -0
  25. package/dist/commands/probe.js.map +1 -0
  26. package/dist/commands/status.js +36 -52
  27. package/dist/commands/status.js.map +1 -1
  28. package/dist/copy/index.d.ts +1 -1
  29. package/dist/copy/index.js +1 -1
  30. package/dist/copy/index.js.map +1 -1
  31. package/dist/copy/strings.d.ts +183 -4
  32. package/dist/copy/strings.js +192 -6
  33. package/dist/copy/strings.js.map +1 -1
  34. package/dist/credential.d.ts +25 -0
  35. package/dist/credential.js +41 -3
  36. package/dist/credential.js.map +1 -1
  37. package/dist/credential_meta.d.ts +66 -0
  38. package/dist/credential_meta.js +164 -0
  39. package/dist/credential_meta.js.map +1 -0
  40. package/dist/device_mode.d.ts +18 -0
  41. package/dist/device_mode.js +28 -0
  42. package/dist/device_mode.js.map +1 -0
  43. package/dist/hooks/entry.d.ts +5 -0
  44. package/dist/hooks/entry.js +40 -5
  45. package/dist/hooks/entry.js.map +1 -1
  46. package/dist/index.js +33 -2
  47. package/dist/index.js.map +1 -1
  48. package/dist/install.d.ts +37 -0
  49. package/dist/install.js +29 -2
  50. package/dist/install.js.map +1 -1
  51. package/dist/oauth/device.d.ts +129 -0
  52. package/dist/oauth/device.js +194 -0
  53. package/dist/oauth/device.js.map +1 -0
  54. package/dist/oauth/discovery.d.ts +5 -0
  55. package/dist/oauth/discovery.js +6 -0
  56. package/dist/oauth/discovery.js.map +1 -1
  57. package/dist/oauth/mint.d.ts +18 -5
  58. package/dist/oauth/mint.js +18 -5
  59. package/dist/oauth/mint.js.map +1 -1
  60. package/dist/oauth/retire.d.ts +64 -0
  61. package/dist/oauth/retire.js +106 -0
  62. package/dist/oauth/retire.js.map +1 -0
  63. package/dist/oauth/signin.d.ts +12 -0
  64. package/dist/oauth/signin.js +5 -1
  65. package/dist/oauth/signin.js.map +1 -1
  66. package/dist/oauth/token.d.ts +1 -0
  67. package/dist/oauth/token.js +5 -0
  68. package/dist/oauth/token.js.map +1 -1
  69. package/dist/oauth/whoami.d.ts +55 -0
  70. package/dist/oauth/whoami.js +77 -0
  71. package/dist/oauth/whoami.js.map +1 -0
  72. package/dist/paths.d.ts +19 -0
  73. package/dist/paths.js +25 -0
  74. package/dist/paths.js.map +1 -1
  75. package/dist/post.d.ts +39 -4
  76. package/dist/post.js +103 -16
  77. package/dist/post.js.map +1 -1
  78. package/dist/state.d.ts +11 -0
  79. package/dist/state.js +33 -0
  80. package/dist/state.js.map +1 -1
  81. package/dist/system_message.d.ts +21 -2
  82. package/dist/system_message.js +37 -7
  83. package/dist/system_message.js.map +1 -1
  84. package/dist/term.d.ts +6 -0
  85. package/dist/term.js +15 -0
  86. package/dist/term.js.map +1 -1
  87. package/dist/update_nudge.d.ts +31 -0
  88. package/dist/update_nudge.js +127 -0
  89. package/dist/update_nudge.js.map +1 -0
  90. package/dist/version.d.ts +1 -1
  91. package/dist/version.js +1 -1
  92. package/package.json +3 -3
@@ -0,0 +1,275 @@
1
+ /**
2
+ * The device sign-in beat — `CR-234d`, D219, plan item 7 (capture half), T8/T9.
3
+ *
4
+ * `browserCredential`'s sibling for a machine whose browser is not on this machine: it asks for a code,
5
+ * shows it, polls while the person approves elsewhere, spends the resulting mint ticket on ONE credential,
6
+ * and hands the credential to the SAME `finishMint` the loopback flow ends in.
7
+ *
8
+ * ```
9
+ * discover → POST device_authorization → print URL + code (always) → [open the prefilled link]
10
+ * → poll /oauth/token ─ pending / slow_down ─┐
11
+ * ├ denied | expired | rate-limited → a §13.6 block, nothing changed
12
+ * └ ticket → mint (Bearer vcmt_) → finishMint({ confirm: "always", ticket })
13
+ * ```
14
+ *
15
+ * ## ⛔ What the shared tail guarantees, and why the device flow is the case it exists for
16
+ *
17
+ * In the loopback flow the approver IS the person at this terminal. Here they are not — the code may have
18
+ * been typed by someone a phishing script sent it to (the reverse attack, RFC 8628 §5.4). So the CLI's own
19
+ * confirmation (`account / workspace`, one default-NO question) is the control, and `confirm: "always"`
20
+ * means it is never skipped: not by an unanswered probe, not by a missing handle. The ticket response's
21
+ * `account` and `org_name` stand in when whoami(new) is not answered, and if there is nothing to show at
22
+ * all `finishMint` FAILS CLOSED (A4 §3).
23
+ *
24
+ * ## ⚠ Nothing here writes `session.json`
25
+ *
26
+ * A ticket buys a machine credential, not a read session (D219). The read lane is still `--sign-in`.
27
+ *
28
+ * ## ⚠ The wait is STATIC
29
+ *
30
+ * No spinner and no ticking countdown (DESIGN.md §13.8): the waiting line is printed once, and the expiry is
31
+ * an absolute local time so the screen stays true in a scrollback. The poll loop's clock and sleep are
32
+ * injected — a test advances a virtual clock rather than waiting out five-second intervals.
33
+ */
34
+ import { hostname as osHostname } from "node:os";
35
+ import { connectDeviceCommandFor } from "../install.js";
36
+ import { AUTH, DEVICE, SIGNIN } from "../copy/index.js";
37
+ import { discover } from "../oauth/discovery.js";
38
+ import { DEFAULT_INTERVAL_S, pollDeviceToken, SLOW_DOWN_STEP_S, startDeviceAuthorization, } from "../oauth/device.js";
39
+ import { machineLabel, mintUrl, requestMint } from "../oauth/mint.js";
40
+ import { HTTP_TIMEOUT_MS, openBrowser as platformOpenBrowser } from "../oauth/signin.js";
41
+ import { paint, renderErrorBlock, wrap } from "../term.js";
42
+ import { finishMint } from "./finish_mint.js";
43
+ import { writeLines } from "./context.js";
44
+ /** Poll failures in a row (a deploy, a blip) before the wait gives up on the service. */
45
+ const MAX_TRANSIENT = 3;
46
+ /** 429s in a row on the poll before it is reported as a rate limit. */
47
+ const MAX_LIMITED = 5;
48
+ /** Slack past `expires_in` before the CLIENT calls the code expired; the server's own `expired_token` normally lands first. */
49
+ const DEADLINE_GRACE_S = 15;
50
+ /** `14:32`, local time, 24-hour — an absolute clock reading, never "in 5 minutes". */
51
+ export function clockTime(date) {
52
+ const two = (n) => String(n).padStart(2, "0");
53
+ return `${two(date.getHours())}:${two(date.getMinutes())}`;
54
+ }
55
+ /** The real SIGINT wait: resolves on the first Ctrl+C, and `dispose` puts the default behaviour back. */
56
+ function sigintOnce() {
57
+ let handler = null;
58
+ const promise = new Promise((resolve) => {
59
+ handler = () => resolve();
60
+ process.once("SIGINT", handler);
61
+ });
62
+ return {
63
+ promise,
64
+ dispose: () => {
65
+ if (handler !== null)
66
+ process.removeListener("SIGINT", handler);
67
+ },
68
+ };
69
+ }
70
+ const realSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
71
+ function errorBlock(ctx, what, why, fixLabel, fixes) {
72
+ return renderErrorBlock({
73
+ kind: "bad",
74
+ what,
75
+ why: [why],
76
+ ...(fixLabel === undefined || fixes === undefined ? {} : { fixLabel, fixes }),
77
+ }, ctx.colour);
78
+ }
79
+ /** The static waiting block (plan decision 4A): URL accent, code strong, the rest muted. */
80
+ function waitingBlock(ctx, grant) {
81
+ const expiresAt = new Date(ctx.now().getTime() + grant.expiresInS * 1000);
82
+ const muted = (text) => wrap(text, 2).map((line) => paint(ctx.colour, "muted", line));
83
+ return [
84
+ "",
85
+ ...muted(DEVICE.heading),
86
+ "",
87
+ ` ${paint(ctx.colour, "accent", grant.verificationUri)}`,
88
+ ` ${DEVICE.codeLabel} ${paint(ctx.colour, "strong", grant.userCode)}`,
89
+ "",
90
+ ...muted(DEVICE.expires(clockTime(expiresAt))),
91
+ ...muted(DEVICE.shareWarning),
92
+ "",
93
+ paint(ctx.colour, "muted", ` ${DEVICE.waiting}`),
94
+ ];
95
+ }
96
+ export async function deviceCredential(ctx, input, deps = {}) {
97
+ const fetchImpl = deps.fetch ?? fetch;
98
+ const rerun = connectDeviceCommandFor(ctx.env, ctx.selfPath);
99
+ const discovery = await discover(input.endpoint, { fetch: fetchImpl, timeoutMs: HTTP_TIMEOUT_MS });
100
+ if (discovery.kind !== "ok") {
101
+ writeLines(ctx.stderr, [
102
+ "",
103
+ ...errorBlock(ctx, SIGNIN.noServerWhat, discovery.kind === "unreachable" ? SIGNIN.noServerUnreachableWhy : SIGNIN.noServerMalformedWhy, DEVICE.retryFix, [rerun]),
104
+ ]);
105
+ return { kind: "failed" };
106
+ }
107
+ const { device_authorization_endpoint, token_endpoint } = discovery.metadata;
108
+ if (device_authorization_endpoint === undefined) {
109
+ writeLines(ctx.stderr, ["", ...errorBlock(ctx, DEVICE.unsupportedWhat, DEVICE.unsupportedWhy)]);
110
+ return { kind: "failed" };
111
+ }
112
+ const label = machineLabel((deps.hostname ?? osHostname)());
113
+ const http = { fetch: fetchImpl, timeoutMs: HTTP_TIMEOUT_MS };
114
+ const started = await startDeviceAuthorization(device_authorization_endpoint, {
115
+ machineLabel: label,
116
+ // 15A: the org the replaced credential belongs to, only on a 200 — a HINT the approver can change.
117
+ ...(input.prior?.who.kind === "ok" ? { orgId: input.prior.who.orgId } : {}),
118
+ }, http);
119
+ switch (started.kind) {
120
+ case "ok":
121
+ break;
122
+ case "rate-limited":
123
+ writeLines(ctx.stderr, ["", ...rateLimitedBlock(ctx, started.retryAfterS, rerun)]);
124
+ return { kind: "declined" };
125
+ case "refused":
126
+ writeLines(ctx.stderr, ["", ...errorBlock(ctx, DEVICE.refusedWhat, DEVICE.refusedWhy, DEVICE.retryFix, [rerun])]);
127
+ return { kind: "failed" };
128
+ case "unreachable":
129
+ writeLines(ctx.stderr, ["", ...errorBlock(ctx, DEVICE.unreachableWhat, DEVICE.unreachableWhy, DEVICE.retryFix, [rerun])]);
130
+ return { kind: "failed" };
131
+ case "malformed":
132
+ writeLines(ctx.stderr, ["", ...errorBlock(ctx, DEVICE.malformedWhat, DEVICE.malformedWhy, DEVICE.retryFix, [rerun])]);
133
+ return { kind: "failed" };
134
+ }
135
+ const grant = started.grant;
136
+ // Printed ALWAYS — whether or not a browser opens (RFC 8628 §3.3.1) — and BEFORE the open, so a
137
+ // browser that steals focus cannot leave the person with nothing to type.
138
+ writeLines(ctx.stdout, waitingBlock(ctx, grant));
139
+ if (input.openLink)
140
+ (deps.openBrowser ?? platformOpenBrowser)(grant.verificationUriComplete);
141
+ const polled = await waitForApproval(ctx, grant, token_endpoint, http, deps);
142
+ if (polled.kind !== "ticket") {
143
+ switch (polled.kind) {
144
+ case "cancelled":
145
+ writeLines(ctx.stdout, ["", paint(ctx.colour, "muted", ` ${DEVICE.cancelled}`)]);
146
+ return { kind: "declined" };
147
+ case "denied":
148
+ writeLines(ctx.stderr, ["", ...errorBlock(ctx, DEVICE.deniedWhat, DEVICE.deniedWhy, DEVICE.deniedFix, [rerun])]);
149
+ return { kind: "declined" };
150
+ case "code-signin-off":
151
+ writeLines(ctx.stderr, ["", ...gateOffBlock(ctx, rerun)]);
152
+ return { kind: "declined" };
153
+ case "expired":
154
+ writeLines(ctx.stderr, ["", ...errorBlock(ctx, DEVICE.expiredWhat, DEVICE.expiredWhy, DEVICE.expiredFix, [rerun])]);
155
+ return { kind: "declined" };
156
+ case "rate-limited":
157
+ writeLines(ctx.stderr, ["", ...rateLimitedBlock(ctx, polled.retryAfterS, rerun)]);
158
+ return { kind: "declined" };
159
+ case "invalid":
160
+ writeLines(ctx.stderr, ["", ...errorBlock(ctx, DEVICE.invalidWhat, DEVICE.invalidWhy, DEVICE.retryFix, [rerun])]);
161
+ return { kind: "failed" };
162
+ case "unreachable":
163
+ writeLines(ctx.stderr, ["", ...errorBlock(ctx, DEVICE.unreachableWhat, DEVICE.unreachableWhy, DEVICE.retryFix, [rerun])]);
164
+ return { kind: "failed" };
165
+ case "malformed":
166
+ writeLines(ctx.stderr, ["", ...errorBlock(ctx, DEVICE.malformedWhat, DEVICE.malformedWhy, DEVICE.retryFix, [rerun])]);
167
+ return { kind: "failed" };
168
+ }
169
+ }
170
+ // Approved. Spend the ticket on ONE credential.
171
+ writeLines(ctx.stdout, ["", ...wrap(AUTH.minting(label), 2)]);
172
+ const minted = await requestMint(mintUrl(input.endpoint), polled.ticket, label, {
173
+ fetch: fetchImpl,
174
+ timeoutMs: HTTP_TIMEOUT_MS,
175
+ });
176
+ if (minted.kind === "refused" && minted.error === "code_signin_off") {
177
+ writeLines(ctx.stderr, ["", ...gateOffBlock(ctx, rerun)]);
178
+ return { kind: "declined" };
179
+ }
180
+ if (minted.kind !== "ok") {
181
+ writeLines(ctx.stderr, [
182
+ "",
183
+ ...errorBlock(ctx, AUTH.mintFailedWhat, minted.kind === "unreachable"
184
+ ? AUTH.mintUnreachableWhy
185
+ : minted.kind === "refused" && minted.error === "rate_limited"
186
+ ? AUTH.mintRateLimitedWhy
187
+ : minted.kind === "malformed"
188
+ ? AUTH.mintMalformedWhy
189
+ : DEVICE.redeemFailedWhy, DEVICE.retryFix, [rerun]),
190
+ ]);
191
+ return { kind: "failed" };
192
+ }
193
+ return await finishMint(ctx, {
194
+ minted: minted.credential,
195
+ facts: minted.facts,
196
+ prior: input.prior,
197
+ endpoint: input.endpoint,
198
+ fetch: fetchImpl,
199
+ // The approver is not necessarily the person at this terminal: the question is never skipped (A4 §3).
200
+ confirm: "always",
201
+ ticket: { account: polled.account, orgName: polled.orgName },
202
+ rerun,
203
+ });
204
+ }
205
+ function gateOffBlock(ctx, rerun) {
206
+ return errorBlock(ctx, DEVICE.gateOffWhat, DEVICE.gateOffWhy, DEVICE.gateOffFix, [rerun]);
207
+ }
208
+ function rateLimitedBlock(ctx, retryAfterS, rerun) {
209
+ const fixLabel = retryAfterS === null
210
+ ? DEVICE.rateLimitedFixUnknown
211
+ : DEVICE.rateLimitedFix(clockTime(new Date(ctx.now().getTime() + retryAfterS * 1000)));
212
+ return errorBlock(ctx, DEVICE.rateLimitedWhat, DEVICE.rateLimitedWhy, fixLabel, [rerun]);
213
+ }
214
+ /**
215
+ * RFC 8628 §3.5's loop. The interval is the server's, `slow_down` raises it for good, and every poll is
216
+ * raced against Ctrl+C — a Ctrl+C during a ten-second request must not wait for the request.
217
+ */
218
+ async function waitForApproval(ctx, grant, tokenEndpoint, http, deps) {
219
+ const sleep = deps.sleep ?? realSleep;
220
+ const interrupt = (deps.interrupt ?? sigintOnce)();
221
+ const CANCEL = Symbol("cancel");
222
+ const cancelled = interrupt.promise.then(() => CANCEL);
223
+ const startMs = ctx.now().getTime();
224
+ const deadlineMs = startMs + (grant.expiresInS + DEADLINE_GRACE_S) * 1000;
225
+ let intervalS = grant.intervalS > 0 ? grant.intervalS : DEFAULT_INTERVAL_S;
226
+ let transient = 0;
227
+ let limited = 0;
228
+ try {
229
+ for (;;) {
230
+ if ((await Promise.race([sleep(intervalS * 1000).then(() => null), cancelled])) === CANCEL) {
231
+ return { kind: "cancelled" };
232
+ }
233
+ if (ctx.now().getTime() > deadlineMs)
234
+ return { kind: "expired" };
235
+ const polled = await Promise.race([pollDeviceToken(tokenEndpoint, grant.deviceCode, http), cancelled]);
236
+ if (polled === CANCEL)
237
+ return { kind: "cancelled" };
238
+ switch (polled.kind) {
239
+ case "ticket":
240
+ return polled;
241
+ case "pending":
242
+ transient = 0;
243
+ limited = 0;
244
+ break;
245
+ case "slow-down":
246
+ transient = 0;
247
+ intervalS += SLOW_DOWN_STEP_S;
248
+ break;
249
+ case "denied":
250
+ case "code-signin-off":
251
+ case "expired":
252
+ case "invalid":
253
+ case "malformed":
254
+ return polled;
255
+ case "rate-limited":
256
+ // The poll endpoint's own limiter. Back off like `slow_down`, and only report it when it persists.
257
+ limited += 1;
258
+ if (limited >= MAX_LIMITED)
259
+ return polled;
260
+ intervalS += SLOW_DOWN_STEP_S;
261
+ break;
262
+ case "server-error":
263
+ case "unreachable":
264
+ transient += 1;
265
+ if (transient >= MAX_TRANSIENT)
266
+ return { kind: "unreachable" };
267
+ break;
268
+ }
269
+ }
270
+ }
271
+ finally {
272
+ interrupt.dispose();
273
+ }
274
+ }
275
+ //# sourceMappingURL=device_credential.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device_credential.js","sourceRoot":"","sources":["../../src/commands/device_credential.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Everything between "the server minted a credential" and "this machine has it" — `finishMint` — and the
3
+ * result type both browser beats return. Shared by the loopback flow (`browser_credential.ts`) and the
4
+ * device flow (`device_credential.ts`, `CR-234d`), and living in neither so that the two can import it
5
+ * without importing each other.
6
+ */
7
+ import { type IngestCredential } from "../credential.js";
8
+ import type { MintFacts } from "../oauth/mint.js";
9
+ import type { Whoami } from "../oauth/whoami.js";
10
+ import { type CommandContext } from "./context.js";
11
+ export type BrowserCredential =
12
+ /**
13
+ * Minted AND written to `credentials.json`. The caller confirms it on screen, and appends `notes`
14
+ * (the retire outcome, the env-shadow warning — one sentence each) to that block's `why`, so the
15
+ * block's own fix line stays the last thing printed.
16
+ */
17
+ {
18
+ readonly kind: "saved";
19
+ readonly credential: IngestCredential;
20
+ readonly notes?: readonly string[];
21
+ }
22
+ /** Nothing was written. The reason is already on stderr; the caller offers the paste. */
23
+ | {
24
+ readonly kind: "failed";
25
+ }
26
+ /**
27
+ * The person said NO — at the replace confirmation, or by denying, cancelling or letting a device code
28
+ * lapse (`CR-234d`). Nothing was saved, the reason is already on screen, and no paste is offered: the
29
+ * answer was an answer, not a fault.
30
+ */
31
+ | {
32
+ readonly kind: "declined";
33
+ };
34
+ /** The credential being replaced, and what the server said about it before sign-in. */
35
+ export interface Prior {
36
+ readonly credential: IngestCredential;
37
+ readonly who: Whoami;
38
+ }
39
+ export interface FinishMintInput {
40
+ readonly minted: IngestCredential;
41
+ readonly facts: MintFacts;
42
+ readonly prior: Prior | null;
43
+ /** The `/mcp` endpoint, whose origin hosts the retire route. */
44
+ readonly endpoint: string;
45
+ readonly fetch: typeof fetch;
46
+ readonly confirm: "on-replace" | "always";
47
+ /**
48
+ * The device flow's ticket response, for the confirmation when whoami(new) is not answered (A4 §3).
49
+ * `orgName` null means there is nothing to show, and `confirm: "always"` then fails closed.
50
+ */
51
+ readonly ticket?: {
52
+ readonly account: string | null;
53
+ readonly orgName: string | null;
54
+ };
55
+ /** The command a fail-closed refusal tells the user to run again. Default: this install's `auth`. */
56
+ readonly rerun?: string;
57
+ }
58
+ /**
59
+ * Everything between "the server minted a credential" and "this machine has it" — shared by the
60
+ * loopback flow now and the device flow in slice 2 (eng review D1 = B, the smaller arrangement).
61
+ *
62
+ * 1. probe the NEW credential (whoami) — a credential the server then refuses is never saved
63
+ * 2. the confirmation block, when it is called for — ONE question, default NO
64
+ * 3. N: save nothing, leave the old file byte-identical (see `REPLACE.declinedOrphan`)
65
+ * 4. y: atomic save → sidecar → retire the old credential
66
+ *
67
+ * ⛔ THE ORDER IS THE SAFETY. Retire is last because a retired old credential plus a failed save would
68
+ * leave the machine with nothing; it is attempted only when the old one came from the FILE (a null
69
+ * `prior` is every env case), only when the old one is not already known dead, and never when the
70
+ * two credentials are known to be in different workspaces ("re-auth across orgs retires nothing and
71
+ * says so").
72
+ */
73
+ export declare function finishMint(ctx: CommandContext, input: FinishMintInput): Promise<BrowserCredential>;
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Everything between "the server minted a credential" and "this machine has it" — `finishMint` — and the
3
+ * result type both browser beats return. Shared by the loopback flow (`browser_credential.ts`) and the
4
+ * device flow (`device_credential.ts`, `CR-234d`), and living in neither so that the two can import it
5
+ * without importing each other.
6
+ */
7
+ import { isAffirmative } from "../consent.js";
8
+ import { authCommandFor } from "../install.js";
9
+ import { CONNECT, REPLACE, RETIRE, AUTH } from "../copy/index.js";
10
+ import { loadedFingerprints, recordCredentialMeta } from "../credential_meta.js";
11
+ import { saveCredential } from "../credential.js";
12
+ import { retireCredential, selfRevokeCredential } from "../oauth/retire.js";
13
+ import { credentialFingerprint } from "../post.js";
14
+ import { paint, labelled, renderErrorBlock, truncateMiddle, wrap, WRAP_COLUMNS, LABEL_GUTTER } from "../term.js";
15
+ import { writeLines } from "./context.js";
16
+ import { probeCredential } from "./probe.js";
17
+ /**
18
+ * Everything between "the server minted a credential" and "this machine has it" — shared by the
19
+ * loopback flow now and the device flow in slice 2 (eng review D1 = B, the smaller arrangement).
20
+ *
21
+ * 1. probe the NEW credential (whoami) — a credential the server then refuses is never saved
22
+ * 2. the confirmation block, when it is called for — ONE question, default NO
23
+ * 3. N: save nothing, leave the old file byte-identical (see `REPLACE.declinedOrphan`)
24
+ * 4. y: atomic save → sidecar → retire the old credential
25
+ *
26
+ * ⛔ THE ORDER IS THE SAFETY. Retire is last because a retired old credential plus a failed save would
27
+ * leave the machine with nothing; it is attempted only when the old one came from the FILE (a null
28
+ * `prior` is every env case), only when the old one is not already known dead, and never when the
29
+ * two credentials are known to be in different workspaces ("re-auth across orgs retires nothing and
30
+ * says so").
31
+ */
32
+ export async function finishMint(ctx, input) {
33
+ const { minted, facts, prior } = input;
34
+ const fresh = await probeCredential(ctx, minted, input.fetch, { record: false });
35
+ if (fresh.kind === "unauthorized") {
36
+ writeLines(ctx.stderr, ["", ...renderErrorBlock({ kind: "bad", what: AUTH.mintFailedWhat, why: [AUTH.mintUnconfirmedWhy] }, ctx.colour)]);
37
+ return { kind: "failed" };
38
+ }
39
+ const priorOk = prior !== null && prior.who.kind === "ok";
40
+ const crossOrg = priorOk && fresh.kind === "ok" && prior.who.orgId !== fresh.orgId;
41
+ // ⛔ NEVER SAVE WITHOUT THE QUESTION BECAUSE A PROBE FAILED (A4 §3). "A re-auth never silently moves
42
+ // capture between orgs" and the confirmation is the reverse-attack control, so an unanswered probe
43
+ // of the NEW credential cannot be a way past it:
44
+ // - on-replace, the old credential was confirmed but the new one was not: the workspace match
45
+ // cannot be shown, so ask — without naming a workspace;
46
+ // - always (the device flow): never save unasked. Show the ticket's own account/workspace when the
47
+ // probe failed; if there is none of that either, FAIL CLOSED.
48
+ // No prior (or an env credential) means nothing to move from, so an unanswered probe still saves.
49
+ const unconfirmed = fresh.kind !== "ok";
50
+ const askUnnamed = input.confirm === "on-replace" && priorOk && unconfirmed;
51
+ const shown = fresh.kind === "ok"
52
+ ? { account: fresh.account, orgName: fresh.orgName }
53
+ : input.confirm === "always"
54
+ ? { account: input.ticket?.account ?? null, orgName: input.ticket?.orgName ?? null }
55
+ : { account: facts.account, orgName: null };
56
+ if (input.confirm === "always" && unconfirmed && shown?.orgName == null) {
57
+ const revoked = await selfRevokeCredential(input.endpoint, minted, { fetch: input.fetch });
58
+ writeLines(ctx.stderr, [
59
+ "",
60
+ ...renderErrorBlock({
61
+ kind: "bad",
62
+ what: REPLACE.unconfirmableWhat,
63
+ why: [revoked.kind === "retired" ? REPLACE.unconfirmableWhyRevoked : REPLACE.unconfirmableWhyOrphan],
64
+ fixLabel: REPLACE.unconfirmableFix,
65
+ fixes: [input.rerun ?? authCommandFor(ctx.env, ctx.selfPath)],
66
+ }, ctx.colour),
67
+ ]);
68
+ return { kind: "failed" };
69
+ }
70
+ if (crossOrg || askUnnamed || input.confirm === "always") {
71
+ // Values are text this package does not control (a workspace name, a handle): cut each in the
72
+ // MIDDLE to what the gutter leaves, so no row passes 80 columns (§13.4).
73
+ const room = WRAP_COLUMNS - 2 - LABEL_GUTTER;
74
+ const orgName = shown?.orgName ?? null;
75
+ writeLines(ctx.stdout, [
76
+ "",
77
+ labelled(2, REPLACE.accountLabel, truncateMiddle(shown?.account == null ? REPLACE.accountUnavailable : `@${shown.account}`, room)),
78
+ labelled(2, REPLACE.workspaceLabel, truncateMiddle(orgName ?? REPLACE.workspaceUnconfirmed, room)),
79
+ ...(crossOrg && prior?.who.kind === "ok"
80
+ ? [labelled(2, REPLACE.replacesLabel, truncateMiddle(REPLACE.replacesValue(prior.who.orgName), room))]
81
+ : []),
82
+ ]);
83
+ // ⛔ A REJECTED QUESTION IS AN N (VQ F1). On a terminal, Ctrl+C at a `readline/promises` question does
84
+ // not raise SIGINT: the interface closes and the promise REJECTS with an AbortError. Left to escape,
85
+ // that skipped the self-revoke below and surfaced as an "unexpected error", leaving the credential
86
+ // just created LIVE and unused in the person's Settings. Any rejection means nobody said yes.
87
+ let answer;
88
+ try {
89
+ answer = await ctx.ask(` ${orgName === null ? REPLACE.questionUnconfirmed : REPLACE.question(truncateMiddle(orgName, 30))} ${paint(ctx.colour, "muted", CONNECT.consentAnswers)} `);
90
+ }
91
+ catch {
92
+ answer = null;
93
+ }
94
+ if (answer === null || !isAffirmative(answer)) {
95
+ // N: nothing is saved, and the credential the server just created is unused.
96
+ // Revoke it with itself as the bearer (brief A3) — the two-token retire cannot, because the only
97
+ // credential this machine holds in that workspace is the one to remove. Anything but a clean
98
+ // "retired" leaves it live, and the sentence says where to remove it.
99
+ const revoked = await selfRevokeCredential(input.endpoint, minted, { fetch: input.fetch });
100
+ writeLines(ctx.stdout, [
101
+ "",
102
+ ...wrap(answer === null ? REPLACE.cancelled : REPLACE.declined, 2),
103
+ ...wrap(revoked.kind === "retired" ? REPLACE.declinedRevoked : REPLACE.declinedOrphan, 2),
104
+ ]);
105
+ return { kind: "declined" };
106
+ }
107
+ }
108
+ // y — or no question was called for.
109
+ saveCredential(ctx.home, minted);
110
+ recordCredentialMeta(ctx.home, credentialFingerprint(minted), {
111
+ expires_at: fresh.kind === "ok" ? fresh.expiresAt ?? facts.expiresAt : facts.expiresAt,
112
+ credential_id: fresh.kind === "ok" ? fresh.credentialId : facts.credentialId,
113
+ org_id: fresh.kind === "ok" ? fresh.orgId : null,
114
+ account: fresh.kind === "ok" ? fresh.account ?? facts.account : facts.account,
115
+ seen_at: ctx.now().toISOString(),
116
+ }, loadedFingerprints(ctx.home, ctx.env));
117
+ const notes = [];
118
+ const envToken = ctx.env.VIBECOMMIT_TOKEN;
119
+ if (envToken !== undefined && envToken.trim() !== "") {
120
+ // D206 §4: the variable still wins, and nothing was retired — `prior` is null for an env credential.
121
+ notes.push(RETIRE.envShadows);
122
+ }
123
+ if (prior !== null && prior.who.kind !== "unauthorized") {
124
+ if (crossOrg) {
125
+ notes.push(RETIRE.notRetired);
126
+ }
127
+ else {
128
+ const retired = await retireCredential(input.endpoint, minted, prior.credential, { fetch: input.fetch });
129
+ notes.push(retired.kind === "retired"
130
+ ? RETIRE.retired
131
+ : retired.kind === "not-retired"
132
+ ? RETIRE.notRetired
133
+ : retired.kind === "unknown"
134
+ ? RETIRE.unknown
135
+ : RETIRE.failed);
136
+ }
137
+ }
138
+ return { kind: "saved", credential: minted, notes };
139
+ }
140
+ //# sourceMappingURL=finish_mint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finish_mint.js","sourceRoot":"","sources":["../../src/commands/finish_mint.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AACjF,OAAO,EAAE,cAAc,EAAyB,MAAM,kBAAkB,CAAC;AAEzE,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE5E,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAAE,cAAc,EAAE,IAAI,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACjH,OAAO,EAAE,UAAU,EAAuB,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AA0C7C;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAmB,EAAE,KAAsB;IAC1E,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAEvC,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IACjF,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QAClC,UAAU,CACR,GAAG,CAAC,MAAM,EACV,CAAC,EAAE,EAAE,GAAG,gBAAgB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,kBAAkB,CAAC,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAClH,CAAC;QACF,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAC5B,CAAC;IAED,MAAM,OAAO,GAAG,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC;IAC1D,MAAM,QAAQ,GAAG,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK,CAAC;IAEnF,oGAAoG;IACpG,mGAAmG;IACnG,iDAAiD;IACjD,gGAAgG;IAChG,4DAA4D;IAC5D,qGAAqG;IACrG,kEAAkE;IAClE,kGAAkG;IAClG,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;IACxC,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,KAAK,YAAY,IAAI,OAAO,IAAI,WAAW,CAAC;IAC5E,MAAM,KAAK,GACT,KAAK,CAAC,IAAI,KAAK,IAAI;QACjB,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE;QACpD,CAAC,CAAC,KAAK,CAAC,OAAO,KAAK,QAAQ;YAC1B,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,IAAI,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,IAAI,IAAI,EAAE;YACpF,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAElD,IAAI,KAAK,CAAC,OAAO,KAAK,QAAQ,IAAI,WAAW,IAAI,KAAK,EAAE,OAAO,IAAI,IAAI,EAAE,CAAC;QACxE,MAAM,OAAO,GAAG,MAAM,oBAAoB,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;QAC3F,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE;YACrB,EAAE;YACF,GAAG,gBAAgB,CACjB;gBACE,IAAI,EAAE,KAAK;gBACX,IAAI,EAAE,OAAO,CAAC,iBAAiB;gBAC/B,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC;gBACpG,QAAQ,EAAE,OAAO,CAAC,gBAAgB;gBAClC,KAAK,EAAE,CAAC,KAAK,CAAC,KAAK,IAAI,cAAc,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;aAC9D,EACD,GAAG,CAAC,MAAM,CACX;SACF,CAAC,CAAC;QACH,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAC5B,CAAC;IAED,IAAI,QAAQ,IAAI,UAAU,IAAI,KAAK,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACzD,8FAA8F;QAC9F,yEAAyE;QACzE,MAAM,IAAI,GAAG,YAAY,GAAG,CAAC,GAAG,YAAY,CAAC;QAC7C,MAAM,OAAO,GAAG,KAAK,EAAE,OAAO,IAAI,IAAI,CAAC;QACvC,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE;YACrB,EAAE;YACF,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,YAAY,EAAE,cAAc,CAAC,KAAK,EAAE,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,CAAC,CAAC;YAClI,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,cAAc,EAAE,cAAc,CAAC,OAAO,IAAI,OAAO,CAAC,oBAAoB,EAAE,IAAI,CAAC,CAAC;YAClG,GAAG,CAAC,QAAQ,IAAI,KAAK,EAAE,GAAG,CAAC,IAAI,KAAK,IAAI;gBACtC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,aAAa,EAAE,cAAc,CAAC,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;gBACtG,CAAC,CAAC,EAAE,CAAC;SACR,CAAC,CAAC;QACH,sGAAsG;QACtG,qGAAqG;QACrG,mGAAmG;QACnG,8FAA8F;QAC9F,IAAI,MAAqB,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,GAAG,CAAC,GAAG,CACpB,KAAK,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,CAC7J,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,GAAG,IAAI,CAAC;QAChB,CAAC;QACD,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9C,6EAA6E;YAC7E,iGAAiG;YACjG,6FAA6F;YAC7F,sEAAsE;YACtE,MAAM,OAAO,GAAG,MAAM,oBAAoB,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;YAC3F,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE;gBACrB,EAAE;gBACF,GAAG,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAClE,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;aAC1F,CAAC,CAAC;YACH,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,qCAAqC;IACrC,cAAc,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACjC,oBAAoB,CAClB,GAAG,CAAC,IAAI,EACR,qBAAqB,CAAC,MAAM,CAAC,EAC7B;QACE,UAAU,EAAE,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS;QACtF,aAAa,EAAE,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY;QAC5E,MAAM,EAAE,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;QAChD,OAAO,EAAE,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO;QAC7E,OAAO,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;KACjC,EACD,kBAAkB,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,CACtC,CAAC;IAEF,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,gBAAgB,CAAC;IAC1C,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACrD,qGAAqG;QACrG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAChC,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QACxD,IAAI,QAAQ,EAAE,CAAC;YACb,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;YACzG,KAAK,CAAC,IAAI,CACR,OAAO,CAAC,IAAI,KAAK,SAAS;gBACxB,CAAC,CAAC,MAAM,CAAC,OAAO;gBAChB,CAAC,CAAC,OAAO,CAAC,IAAI,KAAK,aAAa;oBAC9B,CAAC,CAAC,MAAM,CAAC,UAAU;oBACnB,CAAC,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS;wBAC1B,CAAC,CAAC,MAAM,CAAC,OAAO;wBAChB,CAAC,CAAC,MAAM,CAAC,MAAM,CACtB,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AACtD,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Ask the server whether a credential is still accepted, and remember what it said — `R12`, `R10`.
3
+ *
4
+ * One function for `connect` (the gate before its send) and `browser_credential` (the old credential
5
+ * before sign-in, the new one before it is saved). A 200 also refreshes the sidecar: the whoami 200
6
+ * handler is one of its three writers, and it is how a pasted or `VIBECOMMIT_TOKEN` credential, which
7
+ * was never minted here, comes to have an `expires_at` the client can say "expired" from.
8
+ */
9
+ import type { IngestCredential } from "../credential.js";
10
+ import { type Whoami } from "../oauth/whoami.js";
11
+ import type { CommandContext } from "./context.js";
12
+ export declare function probeCredential(ctx: CommandContext, credential: IngestCredential, fetchImpl?: typeof fetch, options?: {
13
+ readonly record?: boolean;
14
+ }): Promise<Whoami>;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Ask the server whether a credential is still accepted, and remember what it said — `R12`, `R10`.
3
+ *
4
+ * One function for `connect` (the gate before its send) and `browser_credential` (the old credential
5
+ * before sign-in, the new one before it is saved). A 200 also refreshes the sidecar: the whoami 200
6
+ * handler is one of its three writers, and it is how a pasted or `VIBECOMMIT_TOKEN` credential, which
7
+ * was never minted here, comes to have an `expires_at` the client can say "expired" from.
8
+ */
9
+ import { loadedFingerprints, recordCredentialMeta } from "../credential_meta.js";
10
+ import { whoami } from "../oauth/whoami.js";
11
+ import { credentialFingerprint, ingestUrl } from "../post.js";
12
+ export async function probeCredential(ctx, credential, fetchImpl = fetch, options = {}) {
13
+ const url = ingestUrl(ctx.env);
14
+ // An override we refuse to send to is not a server that answered.
15
+ if (url === null)
16
+ return { kind: "unavailable" };
17
+ const result = await whoami(url, credential, { fetch: fetchImpl });
18
+ if (result.kind === "ok" && options.record !== false) {
19
+ recordCredentialMeta(ctx.home, credentialFingerprint(credential), {
20
+ expires_at: result.expiresAt,
21
+ credential_id: result.credentialId,
22
+ org_id: result.orgId,
23
+ account: result.account,
24
+ seen_at: ctx.now().toISOString(),
25
+ }, loadedFingerprints(ctx.home, ctx.env));
26
+ }
27
+ return result;
28
+ }
29
+ //# sourceMappingURL=probe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"probe.js","sourceRoot":"","sources":["../../src/commands/probe.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAEjF,OAAO,EAAE,MAAM,EAAe,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG9D,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,GAAmB,EACnB,UAA4B,EAC5B,YAA0B,KAAK,EAC/B,UAAyC,EAAE;IAE3C,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC/B,kEAAkE;IAClE,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;IACjD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IACnE,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;QACrD,oBAAoB,CAClB,GAAG,CAAC,IAAI,EACR,qBAAqB,CAAC,UAAU,CAAC,EACjC;YACE,UAAU,EAAE,MAAM,CAAC,SAAS;YAC5B,aAAa,EAAE,MAAM,CAAC,YAAY;YAClC,MAAM,EAAE,MAAM,CAAC,KAAK;YACpB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,OAAO,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;SACjC,EACD,kBAAkB,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,CACtC,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}