@vibe-commit/capture 0.4.4 → 0.5.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 (100) hide show
  1. package/README.md +60 -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 +2 -2
  6. package/dist/commands/auth.js +46 -11
  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 +8 -2
  12. package/dist/commands/connect.js +72 -11
  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/finalize.js +9 -1
  21. package/dist/commands/finalize.js.map +1 -1
  22. package/dist/commands/finish_mint.d.ts +73 -0
  23. package/dist/commands/finish_mint.js +140 -0
  24. package/dist/commands/finish_mint.js.map +1 -0
  25. package/dist/commands/probe.d.ts +14 -0
  26. package/dist/commands/probe.js +29 -0
  27. package/dist/commands/probe.js.map +1 -0
  28. package/dist/commands/report.js +6 -1
  29. package/dist/commands/report.js.map +1 -1
  30. package/dist/commands/status.d.ts +1 -1
  31. package/dist/commands/status.js +112 -57
  32. package/dist/commands/status.js.map +1 -1
  33. package/dist/commands/why.d.ts +11 -8
  34. package/dist/commands/why.js +32 -5
  35. package/dist/commands/why.js.map +1 -1
  36. package/dist/copy/index.d.ts +1 -1
  37. package/dist/copy/index.js +1 -1
  38. package/dist/copy/index.js.map +1 -1
  39. package/dist/copy/strings.d.ts +214 -3
  40. package/dist/copy/strings.js +248 -3
  41. package/dist/copy/strings.js.map +1 -1
  42. package/dist/credential.d.ts +25 -0
  43. package/dist/credential.js +41 -3
  44. package/dist/credential.js.map +1 -1
  45. package/dist/credential_meta.d.ts +66 -0
  46. package/dist/credential_meta.js +164 -0
  47. package/dist/credential_meta.js.map +1 -0
  48. package/dist/device_mode.d.ts +18 -0
  49. package/dist/device_mode.js +28 -0
  50. package/dist/device_mode.js.map +1 -0
  51. package/dist/hooks/entry.d.ts +5 -0
  52. package/dist/hooks/entry.js +40 -5
  53. package/dist/hooks/entry.js.map +1 -1
  54. package/dist/index.js +33 -3
  55. package/dist/index.js.map +1 -1
  56. package/dist/install.d.ts +37 -0
  57. package/dist/install.js +29 -2
  58. package/dist/install.js.map +1 -1
  59. package/dist/oauth/device.d.ts +129 -0
  60. package/dist/oauth/device.js +194 -0
  61. package/dist/oauth/device.js.map +1 -0
  62. package/dist/oauth/discovery.d.ts +5 -0
  63. package/dist/oauth/discovery.js +6 -0
  64. package/dist/oauth/discovery.js.map +1 -1
  65. package/dist/oauth/mint.d.ts +18 -5
  66. package/dist/oauth/mint.js +18 -5
  67. package/dist/oauth/mint.js.map +1 -1
  68. package/dist/oauth/retire.d.ts +64 -0
  69. package/dist/oauth/retire.js +106 -0
  70. package/dist/oauth/retire.js.map +1 -0
  71. package/dist/oauth/signin.d.ts +12 -0
  72. package/dist/oauth/signin.js +5 -1
  73. package/dist/oauth/signin.js.map +1 -1
  74. package/dist/oauth/token.d.ts +1 -0
  75. package/dist/oauth/token.js +5 -0
  76. package/dist/oauth/token.js.map +1 -1
  77. package/dist/oauth/whoami.d.ts +55 -0
  78. package/dist/oauth/whoami.js +77 -0
  79. package/dist/oauth/whoami.js.map +1 -0
  80. package/dist/paths.d.ts +19 -0
  81. package/dist/paths.js +25 -0
  82. package/dist/paths.js.map +1 -1
  83. package/dist/post.d.ts +14 -0
  84. package/dist/post.js +15 -0
  85. package/dist/post.js.map +1 -1
  86. package/dist/state.d.ts +80 -0
  87. package/dist/state.js +239 -1
  88. package/dist/state.js.map +1 -1
  89. package/dist/system_message.d.ts +21 -2
  90. package/dist/system_message.js +37 -7
  91. package/dist/system_message.js.map +1 -1
  92. package/dist/term.d.ts +6 -0
  93. package/dist/term.js +15 -0
  94. package/dist/term.js.map +1 -1
  95. package/dist/update_nudge.d.ts +31 -0
  96. package/dist/update_nudge.js +127 -0
  97. package/dist/update_nudge.js.map +1 -0
  98. package/dist/version.d.ts +1 -1
  99. package/dist/version.js +1 -1
  100. package/package.json +3 -3
package/README.md CHANGED
@@ -25,13 +25,33 @@ the approved direction points at `Vibe-Commit/claude-plugin`, which vendors the
25
25
 
26
26
  | Command | What it does |
27
27
  |---|---|
28
- | `vibecommit connect` | Connect this repo and capture the current session. |
28
+ | `vibecommit connect` | Connect this repo and capture the current session. On a machine with no credential it signs you in through the browser first. |
29
+ | `vibecommit auth` | Replace this machine's credential: sign in through the browser, or `--paste` one you made in Settings. Run it again whenever `status` or a hook says the credential is no longer accepted. |
29
30
  | `vibecommit status` | Show whether capture is on for this repo and when it last succeeded. |
30
31
  | `vibecommit off` | Stop capturing for this repo. |
31
32
  | `vibecommit why <file>:<line>` | Show the conversation turns recorded against the commit that last changed this line. |
32
33
  | `vibecommit report --since <when>` | Summarise capture coverage over a time window. |
33
34
  | `vibecommit finalize --session <id>` \| `--latest` | Close a finished session's last turn so commits made in it can be recorded. |
34
35
 
36
+ ### Keeping a machine connected
37
+
38
+ `vibecommit connect` asks the server whether this machine's credential is still accepted **before** it sends
39
+ anything. If it is not, `connect` says so, prints the command that fixes it last, and exits `3`; it does not print
40
+ "Connected". Turns held while the credential was rejected upload once you reconnect. If the server cannot be
41
+ reached, `connect` finishes as it always did and adds one line saying it could not confirm the credential.
42
+
43
+ The fix is `vibecommit auth` (for the Claude Code plugin, which puts nothing on your `PATH`, the line names
44
+ `node <path> auth`). Running it again on a machine that already has a credential **retires the credential it replaces**,
45
+ so Settings shows one row per machine, but only when both are in the same workspace. If the browser lands you in a
46
+ different workspace, `auth` shows the account and workspace and asks first, defaulting to no; answering no changes
47
+ nothing on this machine. A credential set through `VIBECOMMIT_TOKEN` is never replaced or retired by `auth`.
48
+
49
+ `vibecommit status` shows when a credential is being rejected (`status --json` carries it as `credential_stopped`,
50
+ which is `null` when nothing is stopped).
51
+
52
+ After a successful `status`, `connect` or `auth`, on a terminal, a final line tells you when a newer version is on
53
+ npm. It never appears in a hook, under `--json`, or after an error. Set `VIBECOMMIT_NO_UPDATE_CHECK=1` to turn it off.
54
+
35
55
  ### One-shot runs: `vibecommit finalize`
36
56
 
37
57
  A turn is recorded only once a following prompt opens the next one, so the **last turn of a
@@ -98,6 +118,45 @@ sends the same delta again — immediately, within the same `vibecommit finalize
98
118
  otherwise — and keeps sending that way for the rest of the session; `vibecommit status` names what is being
99
119
  held back under that reduced form and why, instead of promising a delivery it will never make.
100
120
 
121
+ 0.4.5 makes `vibecommit status` say when data is stuck instead of reading healthy. A live session whose
122
+ oldest unsent span has been held for over 30 minutes is now reported as `live_stuck`; a session no hook
123
+ has touched in over 30 minutes with bytes still held is reported as `held_idle`; either one flips the
124
+ overall reading to a warning, and `status --json` carries each as its own byte-and-session count (plus
125
+ an additive `lean_sessions` count). A spooled commit whose own `Stop` already fired and was refused no
126
+ longer renders the misleading "waiting for a turn to close" sentence once its session is reported as
127
+ `live_stuck` or `held_idle`; it is counted in that line instead. Every command now answers
128
+ `--help`/`-h` with its help text and no other side effect (`vibecommit auth` still refuses a credential
129
+ typed on the command line first, so it can tell you to revoke it) — including `vibecommit off`, which
130
+ used to silently revoke consent on any trailing argument; it now refuses an argument it does not
131
+ recognize instead, while still accepting the global `--no-color` flag exactly as before. `vibecommit why`
132
+ accepts a third `match_kind`,
133
+ `asserted` — recorded when an agent's own tool call reports a rewrite pairing rather than git's hook
134
+ witnessing it directly — rendered with its own basis sentence, never folded into the unqualified `exact`
135
+ copy.
136
+
137
+ 0.5.0 makes connecting a machine something you can check and undo. `vibecommit connect` and `vibecommit auth` ask
138
+ the server who a stored credential belongs to before sending anything: a credential the server no longer accepts
139
+ is named as the fault and nothing is sent, and an unreachable server keeps today's behaviour with one muted line.
140
+ Signing in again over a credential on this machine asks a question when the workspace would change — or when the
141
+ new credential cannot be confirmed while the old one could — showing the new workspace beside the one it
142
+ replaces; answering *no* revokes the credential it just created, and says so if it could not. Signing in again
143
+ as the same workspace saves without asking. Saving a replacement also retires the credential it replaces, but
144
+ only for a credential stored in a file, only within the same workspace, and best effort: what was and was not
145
+ retired is printed. `vibecommit status` (and `status --json`, as `credential_stopped`) say when the server no
146
+ longer accepts a machine's credential and capture has stopped sending for it.
147
+ `vibecommit connect --device` (also `auth --device`) signs a machine in without a browser on it. It is chosen
148
+ automatically over SSH, on Linux with no display, or when the browser cannot be opened. It prints a code and
149
+ `https://www.vibecommit.ai/device`; you approve there on any device that has a browser; the machine then shows
150
+ the account and workspace that approved and asks `[y/N]`, and nothing is saved until you answer `y`. Approving
151
+ with a code needs the account setting *Allow approving machines with a code*, which is per account and off by
152
+ default; with it off, the approval page says so and the code simply stays valid for its five minutes. The server
153
+ sets a credential's end date and moves it forward while the machine keeps capturing; a credential idle for 90
154
+ days ends. The hook records the end date in the credential's metadata file (never in `credentials.json`);
155
+ within 14 days of it the hook says so once a day, naming the command that renews it, and `vibecommit status` turns
156
+ to a warning. On a terminal, a successful `connect` also mentions on its last line when a newer version exists.
157
+ It needs a server that has the matching routes (the workspace question and the device sign-in), which the
158
+ production server has.
159
+
101
160
  ## Status
102
161
 
103
162
  **Implemented**, not a scaffold. All six commands above have real bodies — OAuth sign-in
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Write a file so that no reader ever sees half of it — temp file in the SAME
3
+ * directory, then `rename`.
4
+ *
5
+ * `rename(2)` within one filesystem replaces the destination in one step, so a
6
+ * hook that reads `credentials.json` while `auth` is saving gets the old file or
7
+ * the new one and never a torn one (`FC5`). The temp file is created `wx` at the
8
+ * final mode, so the bytes are never readable by group or other even briefly,
9
+ * and the `chmod` after is the same belt `saveCredential` has always worn
10
+ * (`writeFileSync`'s `mode` is masked by the umask).
11
+ *
12
+ * A failure removes the temp file and rethrows: the destination is untouched.
13
+ */
14
+ export declare function writeFileAtomic(path: string, data: string, mode: number): void;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Write a file so that no reader ever sees half of it — temp file in the SAME
3
+ * directory, then `rename`.
4
+ *
5
+ * `rename(2)` within one filesystem replaces the destination in one step, so a
6
+ * hook that reads `credentials.json` while `auth` is saving gets the old file or
7
+ * the new one and never a torn one (`FC5`). The temp file is created `wx` at the
8
+ * final mode, so the bytes are never readable by group or other even briefly,
9
+ * and the `chmod` after is the same belt `saveCredential` has always worn
10
+ * (`writeFileSync`'s `mode` is masked by the umask).
11
+ *
12
+ * A failure removes the temp file and rethrows: the destination is untouched.
13
+ */
14
+ import { randomBytes } from "node:crypto";
15
+ import { chmodSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
16
+ export function writeFileAtomic(path, data, mode) {
17
+ const tmp = `${path}.${process.pid}.${randomBytes(4).toString("hex")}.tmp`;
18
+ try {
19
+ writeFileSync(tmp, data, { mode, flag: "wx" });
20
+ chmodSync(tmp, mode);
21
+ renameSync(tmp, path);
22
+ }
23
+ catch (error) {
24
+ try {
25
+ unlinkSync(tmp);
26
+ }
27
+ catch {
28
+ /* nothing to clean up */
29
+ }
30
+ throw error;
31
+ }
32
+ }
33
+ //# sourceMappingURL=atomic_write.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic_write.js","sourceRoot":"","sources":["../src/atomic_write.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE3E,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,IAAY,EAAE,IAAY;IACtE,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;IAC3E,IAAI,CAAC;QACH,aAAa,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/C,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACrB,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACxB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC;YACH,UAAU,CAAC,GAAG,CAAC,CAAC;QAClB,CAAC;QAAC,MAAM,CAAC;YACP,yBAAyB;QAC3B,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -56,7 +56,7 @@
56
56
  * @provenance vibecommit-web app/api/ingest-credentials/route.ts — the dashboard producer the paste path still serves, verified
57
57
  */
58
58
  import type { CommandContext } from "./context.js";
59
- import type { BrowserCredential, Retry } from "./browser_credential.js";
59
+ import type { BrowserCredential, BrowserOptions, Retry } from "./browser_credential.js";
60
60
  export interface AuthDeps {
61
61
  /**
62
62
  * Read the secret from a terminal WITHOUT ECHOING IT. Injected for the same
@@ -76,6 +76,6 @@ export interface AuthDeps {
76
76
  * so a test that forgot to stub it cannot reach a server or open a browser.
77
77
  * `src/index.ts` passes the real one.
78
78
  */
79
- readonly browser?: (ctx: CommandContext, retry: Retry) => Promise<BrowserCredential>;
79
+ readonly browser?: (ctx: CommandContext, retry: Retry, options?: BrowserOptions) => Promise<BrowserCredential>;
80
80
  }
81
81
  export declare function auth(ctx: CommandContext, argv: readonly string[], deps?: AuthDeps): Promise<number>;
@@ -55,7 +55,8 @@
55
55
  * @provenance vibecommit-mcp src/transport/server.ts — POST /oauth/ingest-credential is the mint route, verified
56
56
  * @provenance vibecommit-web app/api/ingest-credentials/route.ts — the dashboard producer the paste path still serves, verified
57
57
  */
58
- import { AUTH, URLS } from "../copy/index.js";
58
+ import { AUTH, COMMANDS, DEVICE, HELP, URLS } from "../copy/index.js";
59
+ import { authCommandFor } from "../install.js";
59
60
  import { writeLines } from "./context.js";
60
61
  import { EXIT } from "../exit.js";
61
62
  import { INGEST_TOKEN_PREFIX, IngestCredential, saveCredential } from "../credential.js";
@@ -68,6 +69,8 @@ import { paint, renderErrorBlock, tildePath, wrap } from "../term.js";
68
69
  const STDIN_FLAG = "--stdin";
69
70
  /** `CR-226`. Skip the browser and go straight to the prompt. */
70
71
  const PASTE_FLAG = "--paste";
72
+ /** `CR-234d`. Sign in by CODE, approved on another device — TTY only. */
73
+ const DEVICE_FLAG = "--device";
71
74
  export async function auth(ctx, argv, deps = {}) {
72
75
  // ⛔ THE ARGV REFUSAL COMES FIRST, before stdin is read, resumed or opened.
73
76
  // Anything else would mean the secret in `argv` had already been accepted by
@@ -83,12 +86,42 @@ export async function auth(ctx, argv, deps = {}) {
83
86
  }, ctx.colour));
84
87
  return EXIT.usage;
85
88
  }
89
+ // `TODOS[184]`. SECOND, deliberately — VG's V3-1: a credential typed as an argument must still be
90
+ // flagged for revocation even if `--help` also appears in `argv`, so the refusal above (which makes
91
+ // exactly that a usage error) runs first. By the time this is reached, `positional.length === 0`, so
92
+ // `--help` still has no side effects — nothing below has read or written anything yet either way.
93
+ if (argv.includes("--help") || argv.includes("-h")) {
94
+ writeLines(ctx.stdout, [HELP.commands.auth]);
95
+ return EXIT.ok;
96
+ }
86
97
  const piped = argv.includes(STDIN_FLAG) || !ctx.stdinIsTty;
98
+ // `CR-234d`: `--device` is TTY only (the approval is a person's) and is the opposite of `--paste`.
99
+ if (argv.includes(DEVICE_FLAG) && (piped || argv.includes(PASTE_FLAG))) {
100
+ writeLines(ctx.stderr, renderErrorBlock(piped
101
+ ? {
102
+ kind: "bad",
103
+ what: DEVICE.needsTerminalWhat,
104
+ why: [DEVICE.needsTerminalWhy],
105
+ fixLabel: DEVICE.needsTerminalFix,
106
+ fixes: [DEVICE.needsTerminalCommand(authCommandFor(ctx.env, ctx.selfPath))],
107
+ }
108
+ : {
109
+ kind: "bad",
110
+ what: DEVICE.conflictWhat,
111
+ why: [DEVICE.conflictWhy],
112
+ fixLabel: DEVICE.conflictFix,
113
+ fixes: [COMMANDS.auth, `${COMMANDS.auth} ${PASTE_FLAG}`],
114
+ }, ctx.colour));
115
+ return EXIT.usage;
116
+ }
87
117
  // ── `CR-226`: the browser, first, on a terminal only. ──────────────────────
88
118
  if (!piped && !argv.includes(PASTE_FLAG) && deps.browser !== undefined) {
89
- const minted = await deps.browser(ctx, "auth");
119
+ const minted = await deps.browser(ctx, "auth", { device: argv.includes(DEVICE_FLAG) });
90
120
  if (minted.kind === "saved")
91
- return saved(ctx);
121
+ return saved(ctx, minted.notes);
122
+ // The user said NO at the replace confirmation: not a failure of the browser, so no paste prompt.
123
+ if (minted.kind === "declined")
124
+ return EXIT.failure;
92
125
  // The reason is already on stderr. The documented fallback is the paste
93
126
  // this verb has always had, with the page that mints one named first.
94
127
  writeLines(ctx.stdout, [
@@ -134,14 +167,16 @@ export async function auth(ctx, argv, deps = {}) {
134
167
  return saved(ctx);
135
168
  }
136
169
  /** The one confirmation, whichever path wrote the file. */
137
- function saved(ctx) {
138
- writeLines(ctx.stdout, renderErrorBlock({
139
- kind: "ok",
140
- what: AUTH.savedWhat,
141
- why: [AUTH.savedWhy(tildePath(credentialsPath(ctx.home), ctx.home))],
142
- fixLabel: AUTH.nextLabel,
143
- fixes: [AUTH.nextCommand],
144
- }, ctx.colour));
170
+ function saved(ctx, notes = []) {
171
+ writeLines(ctx.stdout, [
172
+ ...renderErrorBlock({
173
+ kind: "ok",
174
+ what: AUTH.savedWhat,
175
+ why: [AUTH.savedWhy(tildePath(credentialsPath(ctx.home), ctx.home)), ...notes],
176
+ fixLabel: AUTH.nextLabel,
177
+ fixes: [AUTH.nextCommand],
178
+ }, ctx.colour),
179
+ ]);
145
180
  return EXIT.ok;
146
181
  }
147
182
  //# sourceMappingURL=auth.js.map
@@ -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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"}
@@ -27,8 +27,9 @@
27
27
  * nothing to use — `connect` gates on `absent`, and `auth` is the verb a user
28
28
  * types precisely to replace what is there.
29
29
  */
30
- import { type IngestCredential } from "../credential.js";
31
30
  import { type CommandContext } from "./context.js";
31
+ import { type BrowserCredential } from "./finish_mint.js";
32
+ export { finishMint, type BrowserCredential, type FinishMintInput, type Prior } from "./finish_mint.js";
32
33
  /**
33
34
  * The network and the browser, injectable. Defaults are the real ones; a test
34
35
  * supplies a `fetch` aimed at a stand-in server and an `openBrowser` that follows
@@ -39,6 +40,27 @@ export interface BrowserDeps {
39
40
  readonly openBrowser?: (url: string) => boolean;
40
41
  /** The machine's name, for the credential's label. */
41
42
  readonly hostname?: () => string;
43
+ /** How long the browser round trip may take. Tests only: the real default is `SIGNIN_DEADLINE_MS`. */
44
+ readonly deadlineMs?: number;
45
+ /** `process.platform`, for the headless check. Tests only. */
46
+ readonly platform?: NodeJS.Platform;
47
+ /** The device flow's wait between polls. Tests only: the real one is a timer. */
48
+ readonly sleep?: (ms: number) => Promise<void>;
49
+ /** The device flow's Ctrl+C. Tests only: the real one listens for SIGINT. */
50
+ readonly interrupt?: () => {
51
+ readonly promise: Promise<void>;
52
+ readonly dispose: () => void;
53
+ };
54
+ }
55
+ /**
56
+ * What the caller asked of the browser beat beyond the default (`CR-234d`).
57
+ *
58
+ * `device: true` is `--device`: sign in by CODE even where a browser would open. Absent, the beat still
59
+ * chooses the device flow by itself on an SSH session or a Linux box with no display (`headlessReason`),
60
+ * and falls back to it when the browser cannot be opened.
61
+ */
62
+ export interface BrowserOptions {
63
+ readonly device?: boolean;
42
64
  }
43
65
  /**
44
66
  * Which verb a failed step tells the user to run again.
@@ -60,7 +82,22 @@ export interface SignInBeatOptions extends BrowserDeps {
60
82
  * working one.
61
83
  */
62
84
  readonly replace?: boolean;
85
+ /**
86
+ * `G8a`. A session file that LOADS is not a session that WORKS: before saying "already signed in",
87
+ * ask the server to renew it. Only a refused refresh (`expired`) falls through to a fresh sign-in;
88
+ * a busy lock, an unreachable server or a malformed answer proves nothing, and says "already".
89
+ */
90
+ readonly verify?: boolean;
91
+ /** Ask the authorize page to bind this org (re-auth, plan item 2(a), brief A1). */
92
+ readonly orgId?: string;
93
+ /**
94
+ * `CR-234d`. If the browser cannot be opened, return `NO_BROWSER` instead of printing the URL and waiting
95
+ * for a callback nobody can make. Only `browserCredential` sets this, and only it handles the result.
96
+ */
97
+ readonly fallbackToDevice?: boolean;
63
98
  }
99
+ /** `signInBeat`'s sentinel for "no browser, go to the device flow". Never an exit code a verb returns. */
100
+ export declare const NO_BROWSER = -1;
64
101
  /**
65
102
  * The browser sign-in beat — `CR-084d`.
66
103
  *
@@ -69,16 +106,6 @@ export interface SignInBeatOptions extends BrowserDeps {
69
106
  * says so; it never replaces a working session.
70
107
  */
71
108
  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
109
  /**
83
110
  * Sign in if needed, mint this machine's credential, save it — `CR-226`.
84
111
  *
@@ -86,4 +113,4 @@ export type BrowserCredential =
86
113
  * because what comes next differs by caller (`auth` ends, `connect` goes on to
87
114
  * capture) and a confirmation here would be a second one.
88
115
  */
89
- export declare function browserCredential(ctx: CommandContext, retry: Retry, deps?: BrowserDeps): Promise<BrowserCredential>;
116
+ export declare function browserCredential(ctx: CommandContext, retry: Retry, deps?: BrowserDeps, options?: BrowserOptions): Promise<BrowserCredential>;
@@ -28,15 +28,25 @@
28
28
  * types precisely to replace what is there.
29
29
  */
30
30
  import { hostname as osHostname } from "node:os";
31
+ import { headlessReason } from "../device_mode.js";
32
+ import { resolveOnPath } from "../install.js";
31
33
  import { AUTH, COMMANDS, SIGNIN } from "../copy/index.js";
32
- import { saveCredential } from "../credential.js";
34
+ import { loadCredential } from "../credential.js";
33
35
  import { EXIT } from "../exit.js";
34
36
  import { mcpUrl } from "../oauth/discovery.js";
35
37
  import { machineLabel } from "../oauth/mint.js";
36
38
  import { loadSession } from "../oauth/session.js";
37
- import { HTTP_TIMEOUT_MS, mintWithSession, openBrowser as platformOpenBrowser, signIn, } from "../oauth/signin.js";
39
+ import { authorizedAccessToken, HTTP_TIMEOUT_MS, mintWithSession, openBrowser as platformOpenBrowser, signIn, } from "../oauth/signin.js";
38
40
  import { paint, renderErrorBlock, wrap } from "../term.js";
39
41
  import { writeLines } from "./context.js";
42
+ import { deviceCredential } from "./device_credential.js";
43
+ import { finishMint } from "./finish_mint.js";
44
+ import { probeCredential } from "./probe.js";
45
+ // `finishMint` and its types moved to `finish_mint.ts` (`CR-234d`) so the device flow can share them without
46
+ // a cycle. Re-exported here because this is where every caller and test has always found them.
47
+ export { finishMint } from "./finish_mint.js";
48
+ /** `signInBeat`'s sentinel for "no browser, go to the device flow". Never an exit code a verb returns. */
49
+ export const NO_BROWSER = -1;
40
50
  /**
41
51
  * The browser sign-in beat — `CR-084d`.
42
52
  *
@@ -46,8 +56,18 @@ import { writeLines } from "./context.js";
46
56
  */
47
57
  export async function signInBeat(ctx, options) {
48
58
  if (options.replace !== true && loadSession(ctx.home).kind === "ok") {
49
- writeLines(ctx.stdout, wrap(SIGNIN.alreadySignedIn, 2));
50
- return EXIT.ok;
59
+ const live = options.verify === true
60
+ ? await authorizedAccessToken({
61
+ home: ctx.home,
62
+ fetch: options.fetch ?? fetch,
63
+ nowMs: () => ctx.now().getTime(),
64
+ })
65
+ : null;
66
+ if (live === null || live.kind !== "expired") {
67
+ writeLines(ctx.stdout, wrap(SIGNIN.alreadySignedIn, 2));
68
+ return EXIT.ok;
69
+ }
70
+ // The refresh grant was refused: the session is over. Sign in again below.
51
71
  }
52
72
  const endpoint = mcpUrl(ctx.env);
53
73
  if (endpoint === null) {
@@ -60,6 +80,9 @@ export async function signInBeat(ctx, options) {
60
80
  fetch: options.fetch ?? fetch,
61
81
  nowMs: () => ctx.now().getTime(),
62
82
  openBrowser: options.openBrowser ?? platformOpenBrowser,
83
+ ...(options.orgId === undefined ? {} : { orgId: options.orgId }),
84
+ ...(options.deadlineMs === undefined ? {} : { deadlineMs: options.deadlineMs }),
85
+ ...(options.fallbackToDevice === true ? { fallbackOnNoBrowser: true } : {}),
63
86
  onAuthorizeUrl: (url, opened) => {
64
87
  writeLines(ctx.stdout, [
65
88
  "",
@@ -77,6 +100,8 @@ export async function signInBeat(ctx, options) {
77
100
  writeLines(ctx.stdout, ["", ...wrap(SIGNIN.done, 2)]);
78
101
  return EXIT.ok;
79
102
  }
103
+ if (outcome.kind === "no-browser")
104
+ return NO_BROWSER;
80
105
  writeLines(ctx.stderr, ["", ...signInFailure(ctx, options.retry, outcome)]);
81
106
  return EXIT.failure;
82
107
  }
@@ -117,7 +142,7 @@ function signInError(ctx, retry, what, why, fixLabel = SIGNIN.noServerFix) {
117
142
  * because what comes next differs by caller (`auth` ends, `connect` goes on to
118
143
  * capture) and a confirmation here would be a second one.
119
144
  */
120
- export async function browserCredential(ctx, retry, deps = {}) {
145
+ export async function browserCredential(ctx, retry, deps = {}, options = {}) {
121
146
  // Resolved ONCE and handed to both halves, so the session and the credential
122
147
  // cannot come from two different servers.
123
148
  const endpoint = mcpUrl(ctx.env);
@@ -125,11 +150,45 @@ export async function browserCredential(ctx, retry, deps = {}) {
125
150
  writeLines(ctx.stderr, signInError(ctx, retry, SIGNIN.noServerWhat, SIGNIN.noServerRefusedWhy));
126
151
  return { kind: "failed" };
127
152
  }
128
- if ((await signInBeat(ctx, { ...deps, retry })) !== EXIT.ok)
153
+ const fetchImpl = deps.fetch ?? fetch;
154
+ // ⛔ THE CREDENTIAL THIS RE-AUTH REPLACES, and only from the FILE (plan item 2). `VIBECOMMIT_TOKEN`
155
+ // wins `loadCredential`, so an exported variable makes `source` "env" and `prior` null: an
156
+ // environment credential is the CI path (D56) and is never probed, bound or retired from here.
157
+ const loaded = loadCredential({ env: ctx.env, home: ctx.home });
158
+ const prior = loaded.kind === "ok" && loaded.credential.source === "file"
159
+ ? {
160
+ credential: loaded.credential,
161
+ // Not recorded: it is about to be replaced, and its facts must not outlive it.
162
+ who: await probeCredential(ctx, loaded.credential, fetchImpl, { record: false }),
163
+ }
164
+ : null;
165
+ // ⛔ DEVICE MODE (`CR-234d`, plan item 7). By request (`--device`), or because this machine has no browser
166
+ // of its own: an SSH session, or a Linux box with no display. The loopback sign-in would open a browser
167
+ // nobody can see and wait out its deadline, so the machine asks for a code instead. The prefilled link is
168
+ // opened only where a browser plausibly exists — `--device` on a desktop — never on a headless box.
169
+ const headless = headlessReason(ctx.env, deps.platform ?? process.platform);
170
+ if (options.device === true || headless !== null) {
171
+ return await deviceCredential(ctx, { endpoint, prior, openLink: headless === null }, deps);
172
+ }
173
+ const signedIn = await signInBeat(ctx, {
174
+ ...deps,
175
+ retry,
176
+ // 2(a): ask the authorize page to bind the org the old credential belongs to. Only on a 200 —
177
+ // an org the server would not vouch for is not worth asking for.
178
+ ...(prior?.who.kind === "ok" ? { orgId: prior.who.orgId } : {}),
179
+ // A browser that cannot be opened is a headless machine the check above did not recognise: go to
180
+ // the device flow rather than print a URL and wait for a callback nobody can make.
181
+ fallbackToDevice: true,
182
+ openBrowser: deps.openBrowser ?? ((url) => canOpenBrowser(ctx.env, deps.platform ?? process.platform) && platformOpenBrowser(url)),
183
+ });
184
+ if (signedIn === NO_BROWSER) {
185
+ return await deviceCredential(ctx, { endpoint, prior, openLink: false }, deps);
186
+ }
187
+ if (signedIn !== EXIT.ok) {
129
188
  return { kind: "failed" };
189
+ }
130
190
  const label = machineLabel((deps.hostname ?? osHostname)());
131
191
  writeLines(ctx.stdout, ["", ...wrap(AUTH.minting(label), 2)]);
132
- const fetchImpl = deps.fetch ?? fetch;
133
192
  const mint = () => mintWithSession(endpoint, label, {
134
193
  home: ctx.home,
135
194
  fetch: fetchImpl,
@@ -151,14 +210,28 @@ export async function browserCredential(ctx, retry, deps = {}) {
151
210
  outcome = await mint();
152
211
  }
153
212
  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 };
213
+ return await finishMint(ctx, {
214
+ minted: outcome.credential,
215
+ facts: outcome.facts,
216
+ prior,
217
+ endpoint,
218
+ fetch: fetchImpl,
219
+ // The block guards a re-auth that lands in a DIFFERENT workspace. The device flow passes
220
+ // "always" — there the approver is not the person at this terminal.
221
+ confirm: "on-replace",
222
+ });
158
223
  }
159
224
  writeLines(ctx.stderr, ["", ...renderErrorBlock({ kind: "bad", what: AUTH.mintFailedWhat, why: [mintWhy(outcome)] }, ctx.colour)]);
160
225
  return { kind: "failed" };
161
226
  }
227
+ /**
228
+ * Is there anything on this machine that could open a URL? `openBrowser` reports `true` once `spawn` returns,
229
+ * and a missing `xdg-open` only surfaces later as an asynchronous error — so on Linux the answer is checked
230
+ * up front. macOS and Windows ship `open` and `start`.
231
+ */
232
+ function canOpenBrowser(env, platform) {
233
+ return platform !== "linux" || resolveOnPath(env, "xdg-open") !== null;
234
+ }
162
235
  /** One `why` per way the mint can fail. No status codes on screen (§13.6). */
163
236
  function mintWhy(outcome) {
164
237
  switch (outcome.kind) {
@@ -1 +1 @@
1
- {"version":3,"file":"browser_credential.js","sourceRoot":"","sources":["../../src/commands/browser_credential.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"browser_credential.js","sourceRoot":"","sources":["../../src/commands/browser_credential.ts"],"names":[],"mappings":"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"}
@@ -27,7 +27,7 @@
27
27
  * @provenance vibecommit-mcp src/transport/server.ts — POST /oauth/ingest-credential now mints one, verified
28
28
  */
29
29
  import { type CommandContext } from "./context.js";
30
- import { type BrowserCredential, type Retry } from "./browser_credential.js";
30
+ import { type BrowserCredential, type BrowserOptions, type Retry } from "./browser_credential.js";
31
31
  /**
32
32
  * What `connect` was asked to do beyond the default.
33
33
  *
@@ -37,6 +37,10 @@ import { type BrowserCredential, type Retry } from "./browser_credential.js";
37
37
  export interface ConnectOptions {
38
38
  /** `--sign-in`. Off by default — see the module note for why. */
39
39
  readonly signIn?: boolean;
40
+ /** `CR-234d`: `--device`. Obtain a missing credential by CODE, approved on another device. TTY only. */
41
+ readonly device?: boolean;
42
+ /** `TODOS[184]`: `--help`/`-h`. `connect` has no `argv` param, so this is computed at the call site. */
43
+ readonly help?: boolean;
40
44
  }
41
45
  /**
42
46
  * The browser beat, INJECTED — `CR-226`. `src/index.ts` passes the real
@@ -48,6 +52,8 @@ export interface ConnectOptions {
48
52
  * caught where it matters.
49
53
  */
50
54
  export interface ConnectDeps {
51
- readonly browser?: (ctx: CommandContext, retry: Retry) => Promise<BrowserCredential>;
55
+ readonly browser?: (ctx: CommandContext, retry: Retry, options?: BrowserOptions) => Promise<BrowserCredential>;
56
+ /** The network for the credential probe. Left out, the real `fetch` — the stand-in server is a URL. */
57
+ readonly fetch?: typeof fetch;
52
58
  }
53
59
  export declare function connect(ctx: CommandContext, options?: ConnectOptions, deps?: ConnectDeps): Promise<number>;