@vibe-commit/capture 0.2.3 → 0.4.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 (58) hide show
  1. package/README.md +33 -2
  2. package/dist/agent_hooks.d.ts +53 -8
  3. package/dist/agent_hooks.js +180 -19
  4. package/dist/agent_hooks.js.map +1 -1
  5. package/dist/agent_install.d.ts +0 -17
  6. package/dist/agent_install.js +1 -24
  7. package/dist/agent_install.js.map +1 -1
  8. package/dist/agents/cursor.js +54 -18
  9. package/dist/agents/cursor.js.map +1 -1
  10. package/dist/commands/auth.d.ts +24 -9
  11. package/dist/commands/auth.js +38 -10
  12. package/dist/commands/auth.js.map +1 -1
  13. package/dist/commands/browser_credential.d.ts +89 -0
  14. package/dist/commands/browser_credential.js +181 -0
  15. package/dist/commands/browser_credential.js.map +1 -0
  16. package/dist/commands/connect.d.ts +26 -9
  17. package/dist/commands/connect.js +52 -90
  18. package/dist/commands/connect.js.map +1 -1
  19. package/dist/commands/finalize.d.ts +35 -0
  20. package/dist/commands/finalize.js +283 -0
  21. package/dist/commands/finalize.js.map +1 -0
  22. package/dist/commands/status.js +56 -2
  23. package/dist/commands/status.js.map +1 -1
  24. package/dist/copy/index.d.ts +1 -1
  25. package/dist/copy/index.js +1 -1
  26. package/dist/copy/index.js.map +1 -1
  27. package/dist/copy/strings.d.ts +81 -2
  28. package/dist/copy/strings.js +94 -1
  29. package/dist/copy/strings.js.map +1 -1
  30. package/dist/exit.d.ts +7 -0
  31. package/dist/exit.js +7 -0
  32. package/dist/exit.js.map +1 -1
  33. package/dist/hooks/entry.d.ts +83 -0
  34. package/dist/hooks/entry.js +117 -28
  35. package/dist/hooks/entry.js.map +1 -1
  36. package/dist/hooks/post_rewrite.js +20 -3
  37. package/dist/hooks/post_rewrite.js.map +1 -1
  38. package/dist/index.d.ts +1 -1
  39. package/dist/index.js +14 -2
  40. package/dist/index.js.map +1 -1
  41. package/dist/oauth/mint.d.ts +97 -0
  42. package/dist/oauth/mint.js +120 -0
  43. package/dist/oauth/mint.js.map +1 -0
  44. package/dist/oauth/signin.d.ts +25 -0
  45. package/dist/oauth/signin.js +30 -0
  46. package/dist/oauth/signin.js.map +1 -1
  47. package/dist/post.d.ts +53 -0
  48. package/dist/post.js +73 -5
  49. package/dist/post.js.map +1 -1
  50. package/dist/spool.d.ts +37 -0
  51. package/dist/spool.js +244 -36
  52. package/dist/spool.js.map +1 -1
  53. package/dist/state.d.ts +152 -0
  54. package/dist/state.js +255 -2
  55. package/dist/state.js.map +1 -1
  56. package/dist/version.d.ts +1 -1
  57. package/dist/version.js +1 -1
  58. package/package.json +1 -1
@@ -36,19 +36,27 @@
36
36
  * that discipline theatre. It is refused loudly rather than quietly ignored,
37
37
  * because a user who typed it needs to be told it is now in their history.
38
38
  *
39
- * ## What this verb is NOT
39
+ * ## ⚠ Browser FIRST since `CR-226`; the paste is the fallback, not gone
40
40
  *
41
- * It is not sign-in, and it does not mint. `mintIngestCredential` exists in
42
- * `vibecommit-mcp` with no route in front of it, so on this side of the wire the
43
- * plaintext still comes from the web app's `POST /api/ingest-credentials`, shown
44
- * once, and the human carries it here. Putting a route in front of the minter is
45
- * the PKCE work `connect.ts` defers; this verb makes the paste path first-class,
46
- * it does not remove the paste.
41
+ * This note used to say the verb "is not sign-in, and it does not mint", because
42
+ * `mintIngestCredential` in `vibecommit-mcp` had no route in front of it and the
43
+ * only plaintext producer was the web app's `POST /api/ingest-credentials`. The
44
+ * route now exists (`POST /oauth/ingest-credential`), so on a terminal this verb
45
+ * signs in through the browser and mints the credential there
46
+ * (`browser_credential.ts`). The paste below is unchanged and still reached:
47
47
  *
48
- * @provenance vibecommit-mcp src/oauth/ingest_credential.ts — no route mints one, re-verified
49
- * @provenance vibecommit-web app/api/ingest-credentials/route.ts — the only producer, verified
48
+ * - `--stdin`, or no TTY — the CI shape. Never a browser, exactly as before.
49
+ * - `--paste` — a human who already holds a credential from the dashboard.
50
+ * - any browser step failing — its reason is printed, then the prompt runs.
51
+ *
52
+ * ⚠ So there is no path on which `CR-226` makes the pre-existing flow worse:
53
+ * every failure lands on the prompt that was the whole verb before it.
54
+ *
55
+ * @provenance vibecommit-mcp src/transport/server.ts — POST /oauth/ingest-credential is the mint route, verified
56
+ * @provenance vibecommit-web app/api/ingest-credentials/route.ts — the dashboard producer the paste path still serves, verified
50
57
  */
51
58
  import type { CommandContext } from "./context.js";
59
+ import type { BrowserCredential, Retry } from "./browser_credential.js";
52
60
  export interface AuthDeps {
53
61
  /**
54
62
  * Read the secret from a terminal WITHOUT ECHOING IT. Injected for the same
@@ -62,5 +70,12 @@ export interface AuthDeps {
62
70
  readonly readSecret?: (prompt: string) => Promise<string>;
63
71
  /** Read the whole of stdin. The pipe path, and the only path on a non-TTY. */
64
72
  readonly readStdin?: () => Promise<string>;
73
+ /**
74
+ * The browser mint — `CR-226`. Injected like the two readers above, and for
75
+ * the same reason: left out, this verb is the paste-only verb it was before,
76
+ * so a test that forgot to stub it cannot reach a server or open a browser.
77
+ * `src/index.ts` passes the real one.
78
+ */
79
+ readonly browser?: (ctx: CommandContext, retry: Retry) => Promise<BrowserCredential>;
65
80
  }
66
81
  export declare function auth(ctx: CommandContext, argv: readonly string[], deps?: AuthDeps): Promise<number>;
@@ -36,29 +36,38 @@
36
36
  * that discipline theatre. It is refused loudly rather than quietly ignored,
37
37
  * because a user who typed it needs to be told it is now in their history.
38
38
  *
39
- * ## What this verb is NOT
39
+ * ## ⚠ Browser FIRST since `CR-226`; the paste is the fallback, not gone
40
40
  *
41
- * It is not sign-in, and it does not mint. `mintIngestCredential` exists in
42
- * `vibecommit-mcp` with no route in front of it, so on this side of the wire the
43
- * plaintext still comes from the web app's `POST /api/ingest-credentials`, shown
44
- * once, and the human carries it here. Putting a route in front of the minter is
45
- * the PKCE work `connect.ts` defers; this verb makes the paste path first-class,
46
- * it does not remove the paste.
41
+ * This note used to say the verb "is not sign-in, and it does not mint", because
42
+ * `mintIngestCredential` in `vibecommit-mcp` had no route in front of it and the
43
+ * only plaintext producer was the web app's `POST /api/ingest-credentials`. The
44
+ * route now exists (`POST /oauth/ingest-credential`), so on a terminal this verb
45
+ * signs in through the browser and mints the credential there
46
+ * (`browser_credential.ts`). The paste below is unchanged and still reached:
47
47
  *
48
- * @provenance vibecommit-mcp src/oauth/ingest_credential.ts — no route mints one, re-verified
49
- * @provenance vibecommit-web app/api/ingest-credentials/route.ts — the only producer, verified
48
+ * - `--stdin`, or no TTY — the CI shape. Never a browser, exactly as before.
49
+ * - `--paste` — a human who already holds a credential from the dashboard.
50
+ * - any browser step failing — its reason is printed, then the prompt runs.
51
+ *
52
+ * ⚠ So there is no path on which `CR-226` makes the pre-existing flow worse:
53
+ * every failure lands on the prompt that was the whole verb before it.
54
+ *
55
+ * @provenance vibecommit-mcp src/transport/server.ts — POST /oauth/ingest-credential is the mint route, verified
56
+ * @provenance vibecommit-web app/api/ingest-credentials/route.ts — the dashboard producer the paste path still serves, verified
50
57
  */
51
58
  import { AUTH, URLS } from "../copy/index.js";
52
59
  import { writeLines } from "./context.js";
53
60
  import { EXIT } from "../exit.js";
54
61
  import { INGEST_TOKEN_PREFIX, IngestCredential, saveCredential } from "../credential.js";
55
62
  import { credentialsPath } from "../paths.js";
56
- import { renderErrorBlock, tildePath } from "../term.js";
63
+ import { paint, renderErrorBlock, tildePath, wrap } from "../term.js";
57
64
  /**
58
65
  * `--stdin` forces the pipe even on a terminal — the CI shape, where a job may
59
66
  * well have a TTY attached and must not start prompting because of it.
60
67
  */
61
68
  const STDIN_FLAG = "--stdin";
69
+ /** `CR-226`. Skip the browser and go straight to the prompt. */
70
+ const PASTE_FLAG = "--paste";
62
71
  export async function auth(ctx, argv, deps = {}) {
63
72
  // ⛔ THE ARGV REFUSAL COMES FIRST, before stdin is read, resumed or opened.
64
73
  // Anything else would mean the secret in `argv` had already been accepted by
@@ -75,6 +84,21 @@ export async function auth(ctx, argv, deps = {}) {
75
84
  return EXIT.usage;
76
85
  }
77
86
  const piped = argv.includes(STDIN_FLAG) || !ctx.stdinIsTty;
87
+ // ── `CR-226`: the browser, first, on a terminal only. ──────────────────────
88
+ if (!piped && !argv.includes(PASTE_FLAG) && deps.browser !== undefined) {
89
+ const minted = await deps.browser(ctx, "auth");
90
+ if (minted.kind === "saved")
91
+ return saved(ctx);
92
+ // The reason is already on stderr. The documented fallback is the paste
93
+ // this verb has always had, with the page that mints one named first.
94
+ writeLines(ctx.stdout, [
95
+ "",
96
+ ...wrap(AUTH.pasteInsteadLabel, 2),
97
+ // `accent` on a URL — §13.1's one exception, one colour on the line.
98
+ ` ${paint(ctx.colour, "accent", URLS.settings)}`,
99
+ "",
100
+ ]);
101
+ }
78
102
  const raw = piped
79
103
  ? await (deps.readStdin ?? (() => Promise.resolve("")))()
80
104
  : await (deps.readSecret ?? (() => Promise.resolve("")))(AUTH.prompt);
@@ -107,6 +131,10 @@ export async function auth(ctx, argv, deps = {}) {
107
131
  return EXIT.failure;
108
132
  }
109
133
  saveCredential(ctx.home, new IngestCredential(secret, "file"));
134
+ return saved(ctx);
135
+ }
136
+ /** The one confirmation, whichever path wrote the file. */
137
+ function saved(ctx) {
110
138
  writeLines(ctx.stdout, renderErrorBlock({
111
139
  kind: "ok",
112
140
  what: AUTH.savedWhat,
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * The browser beats — sign in, then buy this machine's ingest credential with
3
+ * that sign-in. `CR-084d` wrote the first; `CR-226` adds the second.
4
+ *
5
+ * ## Why these live together, and outside `connect`
6
+ *
7
+ * Sign-in used to be `connect`'s private beat, reachable only behind
8
+ * `--sign-in`, for a reason its own note gave: *"sign-in cannot mint the
9
+ * credential the rest of `connect` is about."* `CR-226` put a route in front of
10
+ * the minter, so that sentence stopped being true — and two verbs now need the
11
+ * pair. `auth` runs it first and falls back to the paste it has always had;
12
+ * `connect` runs it when a fresh machine reaches the credential beat with a
13
+ * human at the terminal. One copy of the sequence, two callers.
14
+ *
15
+ * ## ⚠ Browser FIRST, never browser ONLY (FOUNDER_OAUTH_PROPOSAL decision 2)
16
+ *
17
+ * Every failure here returns `failed` and leaves the machine exactly as it was:
18
+ * no half-written file, no deleted session. The caller then offers the paste
19
+ * path, which is the path that worked before this module existed. There is no
20
+ * outcome of this function that makes the pre-`CR-226` state worse, and that is
21
+ * the property that made it safe to wire a mechanism that had never run.
22
+ *
23
+ * ## ⚠ And never for a machine that already has a credential
24
+ *
25
+ * `VIBECOMMIT_TOKEN` is read before the file (D56) and still is; this module
26
+ * does not read either. It is the CALLER's job to reach it only when there is
27
+ * nothing to use — `connect` gates on `absent`, and `auth` is the verb a user
28
+ * types precisely to replace what is there.
29
+ */
30
+ import { type IngestCredential } from "../credential.js";
31
+ import { type CommandContext } from "./context.js";
32
+ /**
33
+ * The network and the browser, injectable. Defaults are the real ones; a test
34
+ * supplies a `fetch` aimed at a stand-in server and an `openBrowser` that follows
35
+ * the authorize redirect itself.
36
+ */
37
+ export interface BrowserDeps {
38
+ readonly fetch?: typeof fetch;
39
+ readonly openBrowser?: (url: string) => boolean;
40
+ /** The machine's name, for the credential's label. */
41
+ readonly hostname?: () => string;
42
+ }
43
+ /**
44
+ * Which verb a failed step tells the user to run again.
45
+ *
46
+ * ⚠ A NAME, NOT THE COMMAND STRING, and that is for `test/copy-width.test.ts`.
47
+ * Its fix-line width gate DERIVES every string that can reach a `fixes:` array
48
+ * by reading the array literals; a `fixes: [retry]` holding a runtime string is
49
+ * invisible to it, which would quietly drop `COMMANDS.signIn` out of the set it
50
+ * checks. The conditional in `signInError` names all three copy paths inline,
51
+ * where the gate can see them.
52
+ */
53
+ export type Retry = "auth" | "connect" | "sign-in";
54
+ export interface SignInBeatOptions extends BrowserDeps {
55
+ /** The verb a failed sign-in's fix line tells the user to run again. */
56
+ readonly retry: Retry;
57
+ /**
58
+ * Sign in even though a session file loads. Only for a session the server has
59
+ * already REFUSED to renew — see `browserCredential` — never to second-guess a
60
+ * working one.
61
+ */
62
+ readonly replace?: boolean;
63
+ }
64
+ /**
65
+ * The browser sign-in beat — `CR-084d`.
66
+ *
67
+ * Opt-in inside `connect` (`--sign-in`) for the read lane, and the first half of
68
+ * `browserCredential` below. When a session already exists it is a no-op that
69
+ * says so; it never replaces a working session.
70
+ */
71
+ export declare function signInBeat(ctx: CommandContext, options: SignInBeatOptions): Promise<number>;
72
+ export type BrowserCredential =
73
+ /** Minted AND written to `credentials.json`. The caller confirms it on screen. */
74
+ {
75
+ readonly kind: "saved";
76
+ readonly credential: IngestCredential;
77
+ }
78
+ /** Nothing was written. The reason is already on stderr; the caller offers the paste. */
79
+ | {
80
+ readonly kind: "failed";
81
+ };
82
+ /**
83
+ * Sign in if needed, mint this machine's credential, save it — `CR-226`.
84
+ *
85
+ * Prints its own progress and its own failure; prints NOTHING on success,
86
+ * because what comes next differs by caller (`auth` ends, `connect` goes on to
87
+ * capture) and a confirmation here would be a second one.
88
+ */
89
+ export declare function browserCredential(ctx: CommandContext, retry: Retry, deps?: BrowserDeps): Promise<BrowserCredential>;
@@ -0,0 +1,181 @@
1
+ /**
2
+ * The browser beats — sign in, then buy this machine's ingest credential with
3
+ * that sign-in. `CR-084d` wrote the first; `CR-226` adds the second.
4
+ *
5
+ * ## Why these live together, and outside `connect`
6
+ *
7
+ * Sign-in used to be `connect`'s private beat, reachable only behind
8
+ * `--sign-in`, for a reason its own note gave: *"sign-in cannot mint the
9
+ * credential the rest of `connect` is about."* `CR-226` put a route in front of
10
+ * the minter, so that sentence stopped being true — and two verbs now need the
11
+ * pair. `auth` runs it first and falls back to the paste it has always had;
12
+ * `connect` runs it when a fresh machine reaches the credential beat with a
13
+ * human at the terminal. One copy of the sequence, two callers.
14
+ *
15
+ * ## ⚠ Browser FIRST, never browser ONLY (FOUNDER_OAUTH_PROPOSAL decision 2)
16
+ *
17
+ * Every failure here returns `failed` and leaves the machine exactly as it was:
18
+ * no half-written file, no deleted session. The caller then offers the paste
19
+ * path, which is the path that worked before this module existed. There is no
20
+ * outcome of this function that makes the pre-`CR-226` state worse, and that is
21
+ * the property that made it safe to wire a mechanism that had never run.
22
+ *
23
+ * ## ⚠ And never for a machine that already has a credential
24
+ *
25
+ * `VIBECOMMIT_TOKEN` is read before the file (D56) and still is; this module
26
+ * does not read either. It is the CALLER's job to reach it only when there is
27
+ * nothing to use — `connect` gates on `absent`, and `auth` is the verb a user
28
+ * types precisely to replace what is there.
29
+ */
30
+ import { hostname as osHostname } from "node:os";
31
+ import { AUTH, COMMANDS, SIGNIN } from "../copy/index.js";
32
+ import { saveCredential } from "../credential.js";
33
+ import { EXIT } from "../exit.js";
34
+ import { mcpUrl } from "../oauth/discovery.js";
35
+ import { machineLabel } from "../oauth/mint.js";
36
+ import { loadSession } from "../oauth/session.js";
37
+ import { HTTP_TIMEOUT_MS, mintWithSession, openBrowser as platformOpenBrowser, signIn, } from "../oauth/signin.js";
38
+ import { paint, renderErrorBlock, wrap } from "../term.js";
39
+ import { writeLines } from "./context.js";
40
+ /**
41
+ * The browser sign-in beat — `CR-084d`.
42
+ *
43
+ * Opt-in inside `connect` (`--sign-in`) for the read lane, and the first half of
44
+ * `browserCredential` below. When a session already exists it is a no-op that
45
+ * says so; it never replaces a working session.
46
+ */
47
+ export async function signInBeat(ctx, options) {
48
+ if (options.replace !== true && loadSession(ctx.home).kind === "ok") {
49
+ writeLines(ctx.stdout, wrap(SIGNIN.alreadySignedIn, 2));
50
+ return EXIT.ok;
51
+ }
52
+ const endpoint = mcpUrl(ctx.env);
53
+ if (endpoint === null) {
54
+ writeLines(ctx.stderr, signInError(ctx, options.retry, SIGNIN.noServerWhat, SIGNIN.noServerRefusedWhy));
55
+ return EXIT.failure;
56
+ }
57
+ const outcome = await signIn({
58
+ home: ctx.home,
59
+ mcpEndpoint: endpoint,
60
+ fetch: options.fetch ?? fetch,
61
+ nowMs: () => ctx.now().getTime(),
62
+ openBrowser: options.openBrowser ?? platformOpenBrowser,
63
+ onAuthorizeUrl: (url, opened) => {
64
+ writeLines(ctx.stdout, [
65
+ "",
66
+ ...wrap(opened ? SIGNIN.opening : SIGNIN.manualLabel, 2),
67
+ // `accent` on a URL is §13.1's one exception, and the line carries no
68
+ // second colour. Printed on BOTH paths: a user who wants to open it in a
69
+ // different browser than the default should not have to guess it.
70
+ ` ${paint(ctx.colour, "accent", url)}`,
71
+ ...(opened ? wrap(SIGNIN.waiting, 2) : []),
72
+ ]);
73
+ },
74
+ page: { done: SIGNIN.browserDone, refused: SIGNIN.browserRefused },
75
+ });
76
+ if (outcome.kind === "ok") {
77
+ writeLines(ctx.stdout, ["", ...wrap(SIGNIN.done, 2)]);
78
+ return EXIT.ok;
79
+ }
80
+ writeLines(ctx.stderr, ["", ...signInFailure(ctx, options.retry, outcome)]);
81
+ return EXIT.failure;
82
+ }
83
+ /** §13.6's shape for a sign-in failure. One renderer, six outcomes. */
84
+ function signInFailure(ctx, retry, outcome) {
85
+ switch (outcome.kind) {
86
+ case "no-server":
87
+ return signInError(ctx, retry, SIGNIN.noServerWhat, outcome.detail === "unreachable"
88
+ ? SIGNIN.noServerUnreachableWhy
89
+ : SIGNIN.noServerMalformedWhy, SIGNIN.noServerFix);
90
+ case "denied":
91
+ return signInError(ctx, retry, SIGNIN.deniedWhat, SIGNIN.deniedWhy, SIGNIN.deniedFix);
92
+ case "timeout":
93
+ return signInError(ctx, retry, SIGNIN.timeoutWhat, SIGNIN.timeoutWhy, SIGNIN.timeoutFix);
94
+ case "rejected":
95
+ return signInError(ctx, retry, SIGNIN.rejectedWhat, SIGNIN.rejectedWhy, SIGNIN.rejectedFix);
96
+ case "unreachable":
97
+ return signInError(ctx, retry, SIGNIN.unreachableWhat, SIGNIN.unreachableWhy, SIGNIN.unreachableFix);
98
+ case "malformed":
99
+ return signInError(ctx, retry, SIGNIN.malformedWhat, SIGNIN.malformedWhy, SIGNIN.malformedFix);
100
+ }
101
+ }
102
+ function signInError(ctx, retry, what, why, fixLabel = SIGNIN.noServerFix) {
103
+ return renderErrorBlock({
104
+ kind: "bad",
105
+ what,
106
+ why: [why],
107
+ fixLabel,
108
+ fixes: [
109
+ retry === "auth" ? COMMANDS.auth : retry === "connect" ? COMMANDS.connect : COMMANDS.signIn,
110
+ ],
111
+ }, ctx.colour);
112
+ }
113
+ /**
114
+ * Sign in if needed, mint this machine's credential, save it — `CR-226`.
115
+ *
116
+ * Prints its own progress and its own failure; prints NOTHING on success,
117
+ * because what comes next differs by caller (`auth` ends, `connect` goes on to
118
+ * capture) and a confirmation here would be a second one.
119
+ */
120
+ export async function browserCredential(ctx, retry, deps = {}) {
121
+ // Resolved ONCE and handed to both halves, so the session and the credential
122
+ // cannot come from two different servers.
123
+ const endpoint = mcpUrl(ctx.env);
124
+ if (endpoint === null) {
125
+ writeLines(ctx.stderr, signInError(ctx, retry, SIGNIN.noServerWhat, SIGNIN.noServerRefusedWhy));
126
+ return { kind: "failed" };
127
+ }
128
+ if ((await signInBeat(ctx, { ...deps, retry })) !== EXIT.ok)
129
+ return { kind: "failed" };
130
+ const label = machineLabel((deps.hostname ?? osHostname)());
131
+ writeLines(ctx.stdout, ["", ...wrap(AUTH.minting(label), 2)]);
132
+ const fetchImpl = deps.fetch ?? fetch;
133
+ const mint = () => mintWithSession(endpoint, label, {
134
+ home: ctx.home,
135
+ fetch: fetchImpl,
136
+ nowMs: () => ctx.now().getTime(),
137
+ mint: { fetch: fetchImpl, timeoutMs: HTTP_TIMEOUT_MS },
138
+ });
139
+ let outcome = await mint();
140
+ // ⚠ A SESSION THE SERVER WILL NOT RENEW IS OVER, and a stored file that loads
141
+ // is not the same as a session that works. `signInBeat` above declines to
142
+ // replace any session that LOADS — correct for the read lane, and a dead end
143
+ // here: the user would be told to sign in by a verb that then says "already
144
+ // signed in". So an `expired` (the refresh grant was refused) gets exactly one
145
+ // fresh sign-in and one more mint. Nothing else does: a `busy` lock or an
146
+ // unreachable server is not fixed by a browser.
147
+ if (outcome.kind === "not-authorized" && outcome.authorized.kind === "expired") {
148
+ if ((await signInBeat(ctx, { ...deps, retry, replace: true })) !== EXIT.ok) {
149
+ return { kind: "failed" };
150
+ }
151
+ outcome = await mint();
152
+ }
153
+ if (outcome.kind === "ok") {
154
+ // Through the CR-216 writer, so the 0700/0600 double-chmod and the
155
+ // wrong-class refusal apply exactly as they do to a pasted credential.
156
+ saveCredential(ctx.home, outcome.credential);
157
+ return { kind: "saved", credential: outcome.credential };
158
+ }
159
+ writeLines(ctx.stderr, ["", ...renderErrorBlock({ kind: "bad", what: AUTH.mintFailedWhat, why: [mintWhy(outcome)] }, ctx.colour)]);
160
+ return { kind: "failed" };
161
+ }
162
+ /** One `why` per way the mint can fail. No status codes on screen (§13.6). */
163
+ function mintWhy(outcome) {
164
+ switch (outcome.kind) {
165
+ case "not-authorized":
166
+ case "unauthorized":
167
+ case "wrong-client":
168
+ return AUTH.mintSessionWhy;
169
+ case "refused":
170
+ if (outcome.error === "not_a_member")
171
+ return AUTH.mintNotMemberWhy;
172
+ if (outcome.error === "rate_limited")
173
+ return AUTH.mintRateLimitedWhy;
174
+ return AUTH.mintRefusedWhy;
175
+ case "unreachable":
176
+ return AUTH.mintUnreachableWhy;
177
+ case "malformed":
178
+ return AUTH.mintMalformedWhy;
179
+ }
180
+ }
181
+ //# sourceMappingURL=browser_credential.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser_credential.js","sourceRoot":"","sources":["../../src/commands/browser_credential.ts"],"names":[],"mappings":"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"}
@@ -12,17 +12,22 @@
12
12
  * - `CR-025` owns the ending: capture the current session and print the live
13
13
  * `/app/commits/<sha>` URL. The seam is marked below.
14
14
  *
15
- * `CR-084d` lands the browser sign-in beat (PKCE loopback → a user-principal
16
- * token for the READ lane) behind `--sign-in`. It is opt-in and not part of the
17
- * default install for one reason: **sign-in does not produce an ingest
18
- * credential**, so it cannot replace the beat below. No route in
19
- * `vibecommit-mcp` mints a `vcik_` — `mintIngestCredential` exists and has no
20
- * call site outside that repo's own tests — so `VIBECOMMIT_TOKEN` remains the
21
- * supported path for capture, exactly as before.
15
+ * `CR-084d` landed the browser sign-in beat (PKCE loopback → a user-principal
16
+ * token for the READ lane) behind `--sign-in`, opt-in because sign-in did not
17
+ * produce an ingest credential.
22
18
  *
23
- * @provenance vibecommit-mcp src/oauth/ingest_credential.ts — no route mints one, verified
19
+ * ⚠ `CR-226` CHANGED THAT, AND THIS NOTE USED TO SAY THE OPPOSITE. The server
20
+ * now has a route in front of `mintIngestCredential`, so a fresh machine that
21
+ * reaches the credential beat with a human at the terminal signs in through the
22
+ * browser and mints its own credential there (`browser_credential.ts`) — browser
23
+ * FIRST, with the old warning pointing at `vibecommit auth` as the fallback when
24
+ * any step fails. `--sign-in` keeps its meaning (sign in for the read lane even
25
+ * when a credential exists), and `VIBECOMMIT_TOKEN` still wins over everything.
26
+ *
27
+ * @provenance vibecommit-mcp src/transport/server.ts — POST /oauth/ingest-credential now mints one, verified
24
28
  */
25
29
  import { type CommandContext } from "./context.js";
30
+ import { type BrowserCredential, type Retry } from "./browser_credential.js";
26
31
  /**
27
32
  * What `connect` was asked to do beyond the default.
28
33
  *
@@ -33,4 +38,16 @@ export interface ConnectOptions {
33
38
  /** `--sign-in`. Off by default — see the module note for why. */
34
39
  readonly signIn?: boolean;
35
40
  }
36
- export declare function connect(ctx: CommandContext, options?: ConnectOptions): Promise<number>;
41
+ /**
42
+ * The browser beat, INJECTED — `CR-226`. `src/index.ts` passes the real
43
+ * `browserCredential`; left out, the credential beat behaves exactly as it did
44
+ * before the route existed and never opens a browser. That default is the same
45
+ * one `auth` takes for its readers, and for the same reason: a suite that
46
+ * forgot to stub it must not reach a real server or launch a real browser.
47
+ * `test/cli-wiring.test.ts` asserts the boot path supplies it, so "forgot" is
48
+ * caught where it matters.
49
+ */
50
+ export interface ConnectDeps {
51
+ readonly browser?: (ctx: CommandContext, retry: Retry) => Promise<BrowserCredential>;
52
+ }
53
+ export declare function connect(ctx: CommandContext, options?: ConnectOptions, deps?: ConnectDeps): Promise<number>;