@vibe-commit/capture 0.6.1 → 0.8.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.
- package/README.md +191 -9
- package/dist/account_not_here.d.ts +15 -0
- package/dist/account_not_here.js +25 -0
- package/dist/account_not_here.js.map +1 -0
- package/dist/agent_hooks.d.ts +8 -0
- package/dist/agent_hooks.js +31 -1
- package/dist/agent_hooks.js.map +1 -1
- package/dist/agents/claude_code.d.ts +34 -0
- package/dist/agents/claude_code.js +141 -8
- package/dist/agents/claude_code.js.map +1 -1
- package/dist/agents/codex_cli.d.ts +13 -0
- package/dist/agents/codex_cli.js +241 -2
- package/dist/agents/codex_cli.js.map +1 -1
- package/dist/agents/cursor.d.ts +34 -0
- package/dist/agents/cursor.js +115 -1
- package/dist/agents/cursor.js.map +1 -1
- package/dist/agents/session_listing.d.ts +29 -0
- package/dist/agents/session_listing.js +49 -0
- package/dist/agents/session_listing.js.map +1 -0
- package/dist/agents/types.d.ts +118 -8
- package/dist/agents/types.js +17 -3
- package/dist/agents/types.js.map +1 -1
- package/dist/commands/approval_pending.d.ts +9 -0
- package/dist/commands/approval_pending.js +23 -0
- package/dist/commands/approval_pending.js.map +1 -0
- package/dist/commands/auth.d.ts +7 -0
- package/dist/commands/auth.js +52 -11
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/backfill.d.ts +160 -0
- package/dist/commands/backfill.js +748 -0
- package/dist/commands/backfill.js.map +1 -0
- package/dist/commands/browser_credential.d.ts +6 -2
- package/dist/commands/browser_credential.js +34 -14
- package/dist/commands/browser_credential.js.map +1 -1
- package/dist/commands/connect.d.ts +146 -0
- package/dist/commands/connect.js +216 -117
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/connect_account.d.ts +135 -0
- package/dist/commands/connect_account.js +321 -0
- package/dist/commands/connect_account.js.map +1 -0
- package/dist/commands/connect_picker.d.ts +27 -0
- package/dist/commands/connect_picker.js +549 -0
- package/dist/commands/connect_picker.js.map +1 -0
- package/dist/commands/context.d.ts +16 -0
- package/dist/commands/context.js +27 -0
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/credential_problem.d.ts +15 -4
- package/dist/commands/credential_problem.js +29 -22
- package/dist/commands/credential_problem.js.map +1 -1
- package/dist/commands/device_credential.d.ts +5 -1
- package/dist/commands/device_credential.js +17 -14
- package/dist/commands/device_credential.js.map +1 -1
- package/dist/commands/finalize.d.ts +95 -0
- package/dist/commands/finalize.js +249 -74
- package/dist/commands/finalize.js.map +1 -1
- package/dist/commands/finish_mint.d.ts +30 -6
- package/dist/commands/finish_mint.js +26 -6
- package/dist/commands/finish_mint.js.map +1 -1
- package/dist/commands/report.js +38 -30
- package/dist/commands/report.js.map +1 -1
- package/dist/commands/scan.d.ts +162 -0
- package/dist/commands/scan.js +507 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/status.d.ts +19 -1
- package/dist/commands/status.js +232 -26
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/status_hooks.d.ts +14 -0
- package/dist/commands/status_hooks.js +81 -0
- package/dist/commands/status_hooks.js.map +1 -0
- package/dist/commands/status_machine.d.ts +27 -0
- package/dist/commands/status_machine.js +210 -0
- package/dist/commands/status_machine.js.map +1 -0
- package/dist/commands/sync_reason.d.ts +17 -0
- package/dist/commands/sync_reason.js +25 -0
- package/dist/commands/sync_reason.js.map +1 -0
- package/dist/commands/why.js +44 -31
- package/dist/commands/why.js.map +1 -1
- package/dist/consent.d.ts +217 -0
- package/dist/consent.js +400 -1
- package/dist/consent.js.map +1 -1
- package/dist/copy/error_codes.d.ts +222 -0
- package/dist/copy/error_codes.js +105 -0
- package/dist/copy/error_codes.js.map +1 -0
- package/dist/copy/index.d.ts +2 -2
- package/dist/copy/index.js +2 -2
- package/dist/copy/index.js.map +1 -1
- package/dist/copy/strings.d.ts +617 -30
- package/dist/copy/strings.js +741 -36
- package/dist/copy/strings.js.map +1 -1
- package/dist/copy/time.d.ts +11 -0
- package/dist/copy/time.js +18 -0
- package/dist/copy/time.js.map +1 -1
- package/dist/credential.d.ts +5 -1
- package/dist/credential.js +2 -2
- package/dist/credential.js.map +1 -1
- package/dist/git.d.ts +43 -0
- package/dist/git.js +95 -0
- package/dist/git.js.map +1 -1
- package/dist/grants_sync.d.ts +165 -0
- package/dist/grants_sync.js +289 -0
- package/dist/grants_sync.js.map +1 -0
- package/dist/grants_verb.d.ts +70 -0
- package/dist/grants_verb.js +121 -0
- package/dist/grants_verb.js.map +1 -0
- package/dist/hooks/entry.d.ts +18 -1
- package/dist/hooks/entry.js +117 -14
- package/dist/hooks/entry.js.map +1 -1
- package/dist/hooks/post_commit.js +3 -3
- package/dist/hooks/post_commit.js.map +1 -1
- package/dist/hooks/post_rewrite.js +6 -4
- package/dist/hooks/post_rewrite.js.map +1 -1
- package/dist/hooks/session_grants.d.ts +59 -0
- package/dist/hooks/session_grants.js +91 -0
- package/dist/hooks/session_grants.js.map +1 -0
- package/dist/hooks/session_start.d.ts +114 -0
- package/dist/hooks/session_start.js +243 -0
- package/dist/hooks/session_start.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +76 -11
- package/dist/index.js.map +1 -1
- package/dist/install.d.ts +57 -0
- package/dist/install.js +54 -20
- package/dist/install.js.map +1 -1
- package/dist/json.d.ts +32 -9
- package/dist/json.js +35 -8
- package/dist/json.js.map +1 -1
- package/dist/machine_state.d.ts +52 -0
- package/dist/machine_state.js +63 -0
- package/dist/machine_state.js.map +1 -0
- package/dist/oauth/retire.d.ts +10 -0
- package/dist/oauth/retire.js +13 -0
- package/dist/oauth/retire.js.map +1 -1
- package/dist/oauth/session.d.ts +1 -0
- package/dist/org_pending.d.ts +52 -0
- package/dist/org_pending.js +109 -0
- package/dist/org_pending.js.map +1 -0
- package/dist/paths.d.ts +70 -0
- package/dist/paths.js +90 -0
- package/dist/paths.js.map +1 -1
- package/dist/pending_revoke.d.ts +117 -0
- package/dist/pending_revoke.js +233 -0
- package/dist/pending_revoke.js.map +1 -0
- package/dist/pending_revoke_file.d.ts +55 -0
- package/dist/pending_revoke_file.js +74 -0
- package/dist/pending_revoke_file.js.map +1 -0
- package/dist/picker.d.ts +208 -0
- package/dist/picker.js +526 -0
- package/dist/picker.js.map +1 -0
- package/dist/picker_checklist.d.ts +141 -0
- package/dist/picker_checklist.js +411 -0
- package/dist/picker_checklist.js.map +1 -0
- package/dist/policy.d.ts +70 -1
- package/dist/policy.js +68 -2
- package/dist/policy.js.map +1 -1
- package/dist/post.d.ts +91 -18
- package/dist/post.js +259 -27
- package/dist/post.js.map +1 -1
- package/dist/state.d.ts +6 -0
- package/dist/state.js +38 -1
- package/dist/state.js.map +1 -1
- package/dist/system_message.d.ts +32 -2
- package/dist/system_message.js +47 -5
- package/dist/system_message.js.map +1 -1
- package/dist/term.d.ts +28 -2
- package/dist/term.js +23 -2
- package/dist/term.js.map +1 -1
- package/dist/verb_help.d.ts +13 -0
- package/dist/verb_help.js +40 -0
- package/dist/verb_help.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -9
package/dist/agents/cursor.js
CHANGED
|
@@ -17,7 +17,9 @@
|
|
|
17
17
|
* ⚠ **What is DATA here and what is MEASURED is not the same set**, and the
|
|
18
18
|
* difference is marked on each member below rather than left to a reader.
|
|
19
19
|
*/
|
|
20
|
+
import { join } from "node:path";
|
|
20
21
|
import { cursorHooksPath, cursorTranscriptRoot, isCursorTranscript } from "../paths.js";
|
|
22
|
+
import { readEntries, sessionFile, subdirectories } from "./session_listing.js";
|
|
21
23
|
/**
|
|
22
24
|
* ⭐ **MEASURED. `D5`'s SECOND HALF IS ANSWERED (`TODOS[130]`), AND THE ANSWER IS
|
|
23
25
|
* THAT CURSOR IMPOSES NO NUMBER OF ITS OWN.**
|
|
@@ -79,6 +81,97 @@ import { cursorHooksPath, cursorTranscriptRoot, isCursorTranscript } from "../pa
|
|
|
79
81
|
*/
|
|
80
82
|
const CURSOR_REGISTERED_MS = 3_000;
|
|
81
83
|
const CURSOR_CLIENT_MS = 2_200;
|
|
84
|
+
/**
|
|
85
|
+
* The longest folder name Cursor writes verbatim.
|
|
86
|
+
*
|
|
87
|
+
* ⛔ **MEASURED (Open Question 3, VS, 2026-10-24), with `cursor-agent`, which
|
|
88
|
+
* creates the project folder even when it is not logged in.** A name up to 61
|
|
89
|
+
* characters is kept; a longer one becomes its first 53 characters, `-`, and 7
|
|
90
|
+
* hex characters (the hash varied with the input, so it is not a constant).
|
|
91
|
+
*/
|
|
92
|
+
export const CURSOR_NAME_LIMIT = 61;
|
|
93
|
+
/**
|
|
94
|
+
* The folder name Cursor gives a workspace path — Open Question 3.
|
|
95
|
+
*
|
|
96
|
+
* ⛔ **THIS IS NOT CLAUDE CODE'S RULE, and the difference is the measurement.**
|
|
97
|
+
* Every RUN of non-alphanumerics, a literal `--` included, collapses to ONE `-`;
|
|
98
|
+
* leading and trailing separators are dropped (so `/tmp/q/trail.` is `…-trail`
|
|
99
|
+
* and there is no leading `-`); case is kept. An emoji and `é'` each gave a single
|
|
100
|
+
* dash where Claude Code gives two.
|
|
101
|
+
*
|
|
102
|
+
* ⚠ **Which makes Cursor's names collide far more:** `a-b`, `a.b`, `a--b` and
|
|
103
|
+
* `a.b.` are all one folder. The scan's walk encodes every directory entry with
|
|
104
|
+
* THIS function and matches on the result, so the collapsing applies to every
|
|
105
|
+
* candidate rather than being assumed away.
|
|
106
|
+
*/
|
|
107
|
+
export function encodeProjectPath(path) {
|
|
108
|
+
return path.replace(/[^A-Za-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Is this folder name one Cursor cut and hashed? See `CURSOR_NAME_LIMIT`.
|
|
112
|
+
*
|
|
113
|
+
* ⚠ **A hashed name is always exactly 61 long, and so is a verbatim name that
|
|
114
|
+
* happens to be 61 long** — length alone cannot tell them apart. The test is the
|
|
115
|
+
* hash's shape too (`-` and 7 hex). A verbatim 61-character name that ends the
|
|
116
|
+
* same way is read as hashed, which is the safe error: it is skipped and
|
|
117
|
+
* counted, never matched to a path it might not be.
|
|
118
|
+
*/
|
|
119
|
+
export function isHashedProjectName(name) {
|
|
120
|
+
return name.length === CURSOR_NAME_LIMIT && /-[0-9a-f]{7}$/.test(name);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The folder Cursor uses for a window with no workspace open. It is not a path,
|
|
124
|
+
* so it can never resolve to one and is not worth a "folder no longer exists"
|
|
125
|
+
* count. Seen by NAME on the author's machine; nothing in it was read.
|
|
126
|
+
*/
|
|
127
|
+
const CURSOR_NO_WORKSPACE = "empty-window";
|
|
128
|
+
/**
|
|
129
|
+
* Cursor's sessions, by folder — `D11 · A`, `scan`.
|
|
130
|
+
*
|
|
131
|
+
* `<root>/<encoded workspace>/agent-transcripts/<cid>/<cid>.jsonl`. Only the
|
|
132
|
+
* conversation's own file counts: `<cid>/subagents/…` is delegated work that
|
|
133
|
+
* belongs to the parent (`delegatedTranscripts: "announce"`). A flat
|
|
134
|
+
* `agent-transcripts/<cid>.jsonl` is accepted too, because `isCursorTranscript`
|
|
135
|
+
* admits any `.jsonl` at that depth and a count that disagreed with the gate
|
|
136
|
+
* would be a count of nothing.
|
|
137
|
+
*
|
|
138
|
+
* ⛔ Names and times only; see `claudeSessionSource` for the rule and its reason.
|
|
139
|
+
*/
|
|
140
|
+
function cursorSessionSource(home) {
|
|
141
|
+
const root = cursorTranscriptRoot(home);
|
|
142
|
+
const folders = [];
|
|
143
|
+
try {
|
|
144
|
+
for (const folder of subdirectories(root)) {
|
|
145
|
+
if (folder.name === CURSOR_NO_WORKSPACE)
|
|
146
|
+
continue;
|
|
147
|
+
const transcripts = join(folder.path, "agent-transcripts");
|
|
148
|
+
const sessions = [];
|
|
149
|
+
for (const entry of readEntries(transcripts) ?? []) {
|
|
150
|
+
const file = entry.isDirectory()
|
|
151
|
+
? join(transcripts, entry.name, `${entry.name}.jsonl`)
|
|
152
|
+
: entry.name.endsWith(".jsonl")
|
|
153
|
+
? join(transcripts, entry.name)
|
|
154
|
+
: null;
|
|
155
|
+
const session = file === null ? null : sessionFile(file);
|
|
156
|
+
if (session !== null)
|
|
157
|
+
sessions.push(session);
|
|
158
|
+
}
|
|
159
|
+
if (sessions.length > 0)
|
|
160
|
+
folders.push({ name: folder.name, sessions });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return { kind: "unreadable", reason: "unreadable" };
|
|
165
|
+
}
|
|
166
|
+
// Cursor keeps no record of the folders it has run in, so there is no second signal.
|
|
167
|
+
return {
|
|
168
|
+
kind: "folders",
|
|
169
|
+
folders,
|
|
170
|
+
encode: encodeProjectPath,
|
|
171
|
+
isHashed: isHashedProjectName,
|
|
172
|
+
recorded: [],
|
|
173
|
+
};
|
|
174
|
+
}
|
|
82
175
|
export const CURSOR = {
|
|
83
176
|
id: "cursor",
|
|
84
177
|
transport: "ndjson",
|
|
@@ -103,7 +196,22 @@ export const CURSOR = {
|
|
|
103
196
|
// ⛔ Against the client's `SessionEnd`, `sessionEnd` matched NOTHING — not an
|
|
104
197
|
// error, a `false` — so a Cursor session took no settle on its final turn and
|
|
105
198
|
// lost it permanently. That is the whole of the bug.
|
|
106
|
-
|
|
199
|
+
//
|
|
200
|
+
// ⛔ **`sessionStart` is STATIC, NOT OBSERVED** (Open Question 1, 2026-10-24).
|
|
201
|
+
// `cursor-agent` was not logged in, so no session ran; the spelling and the
|
|
202
|
+
// payload (`conversation_id`, `generation_id`, `model`, `is_background_agent`,
|
|
203
|
+
// `composer_mode`) come from the CLI bundle 2026.08.11-e8db854, which runs it
|
|
204
|
+
// for a NEW chat before the first turn. It reads `env` and `additional_context`
|
|
205
|
+
// from the output — and `additional_context` goes into the agent's context and
|
|
206
|
+
// is NOT shown to the user. There is no user-visible field, so this client
|
|
207
|
+
// never uses it to carry a notice: Cursor users learn the state from `status`,
|
|
208
|
+
// and the first-`Stop` install is kept as the fallback for exactly this reason.
|
|
209
|
+
eventNames: {
|
|
210
|
+
Stop: "stop",
|
|
211
|
+
PreCompact: "preCompact",
|
|
212
|
+
SessionEnd: "sessionEnd",
|
|
213
|
+
SessionStart: "sessionStart",
|
|
214
|
+
},
|
|
107
215
|
// ⛔ `~/.cursor/hooks.json`, and the `cursor` SHAPE — the two are a pair.
|
|
108
216
|
// Read out of the shipped bundle's validator: a top-level numeric `version`
|
|
109
217
|
// is REQUIRED, entries are a FLAT array of hook scripts each needing a
|
|
@@ -125,6 +233,11 @@ export const CURSOR = {
|
|
|
125
233
|
registeredTimeoutMs: CURSOR_REGISTERED_MS,
|
|
126
234
|
clientBudgetMs: CURSOR_CLIENT_MS,
|
|
127
235
|
},
|
|
236
|
+
// gstack-shortcut(dec-e650afbf): `Stop`'s clocks, no fast path (eng review D7 · B).
|
|
237
|
+
SessionStart: {
|
|
238
|
+
registeredTimeoutMs: CURSOR_REGISTERED_MS, // gstack-shortcut(dec-e650afbf)
|
|
239
|
+
clientBudgetMs: CURSOR_CLIENT_MS, // gstack-shortcut(dec-e650afbf)
|
|
240
|
+
},
|
|
128
241
|
},
|
|
129
242
|
// ⛔ DECLARED EXPLICITLY, and it is load-bearing now in a way it was not
|
|
130
243
|
// before (D177 §2, §9). `subagentsDir()` would compute
|
|
@@ -142,6 +255,7 @@ export const CURSOR = {
|
|
|
142
255
|
// measurement, not as a fact about the agent. Measuring it is a change to this
|
|
143
256
|
// line and nothing else.
|
|
144
257
|
sessionIdFromEnv: () => null,
|
|
258
|
+
sessionSource: (home) => cursorSessionSource(home),
|
|
145
259
|
// ⛔ NO `locateCurrentTranscript`, and the omission is the decision — the same
|
|
146
260
|
// one Codex made. Finding the conversation for THIS project would mean munging
|
|
147
261
|
// a workspace path this client is never told, or reading a `cwd` out of the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/agents/cursor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/agents/cursor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Listing a folder-keyed agent's sessions — `D11 · A`, `scan`.
|
|
3
|
+
*
|
|
4
|
+
* The two folder-keyed dialects (Claude Code, Cursor) differ in how a path is
|
|
5
|
+
* encoded and where a session sits under the folder, and agree on everything
|
|
6
|
+
* this file does: read directory ENTRIES, `lstat` a candidate for its time, and
|
|
7
|
+
* never open it.
|
|
8
|
+
*
|
|
9
|
+
* ⛔ **`lstat`, REGULAR FILES ONLY.** A symlink named `x.jsonl` is not a session
|
|
10
|
+
* (Cursor's gate resolves for exactly that reason), and a FIFO or socket under a
|
|
11
|
+
* root this client does not own is a thing a later `readFileSync` hangs on or
|
|
12
|
+
* throws on. A listing that only ever `lstat`s cannot be made to block, which is
|
|
13
|
+
* the property `scan` needs from the one command that walks the whole root.
|
|
14
|
+
*/
|
|
15
|
+
import { type Dirent } from "node:fs";
|
|
16
|
+
import type { SourceSession } from "./types.js";
|
|
17
|
+
/**
|
|
18
|
+
* The entries of a directory, `null` when it is simply not there, and a THROW
|
|
19
|
+
* for anything else (permissions, I/O) — the caller turns a throw into the
|
|
20
|
+
* agent's `unreadable` answer, so an absent store and a broken one stay apart.
|
|
21
|
+
*/
|
|
22
|
+
export declare function readEntries(dir: string): Dirent[] | null;
|
|
23
|
+
/** One regular file's times, or null for anything that is not one. Never opens it. */
|
|
24
|
+
export declare function sessionFile(path: string): SourceSession | null;
|
|
25
|
+
/** `<dir>/<name>` for each entry that is a real directory (a symlink is not one). */
|
|
26
|
+
export declare function subdirectories(dir: string): {
|
|
27
|
+
name: string;
|
|
28
|
+
path: string;
|
|
29
|
+
}[];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Listing a folder-keyed agent's sessions — `D11 · A`, `scan`.
|
|
3
|
+
*
|
|
4
|
+
* The two folder-keyed dialects (Claude Code, Cursor) differ in how a path is
|
|
5
|
+
* encoded and where a session sits under the folder, and agree on everything
|
|
6
|
+
* this file does: read directory ENTRIES, `lstat` a candidate for its time, and
|
|
7
|
+
* never open it.
|
|
8
|
+
*
|
|
9
|
+
* ⛔ **`lstat`, REGULAR FILES ONLY.** A symlink named `x.jsonl` is not a session
|
|
10
|
+
* (Cursor's gate resolves for exactly that reason), and a FIFO or socket under a
|
|
11
|
+
* root this client does not own is a thing a later `readFileSync` hangs on or
|
|
12
|
+
* throws on. A listing that only ever `lstat`s cannot be made to block, which is
|
|
13
|
+
* the property `scan` needs from the one command that walks the whole root.
|
|
14
|
+
*/
|
|
15
|
+
import { lstatSync, readdirSync } from "node:fs";
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
/**
|
|
18
|
+
* The entries of a directory, `null` when it is simply not there, and a THROW
|
|
19
|
+
* for anything else (permissions, I/O) — the caller turns a throw into the
|
|
20
|
+
* agent's `unreadable` answer, so an absent store and a broken one stay apart.
|
|
21
|
+
*/
|
|
22
|
+
export function readEntries(dir) {
|
|
23
|
+
try {
|
|
24
|
+
return readdirSync(dir, { withFileTypes: true });
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
const code = err.code;
|
|
28
|
+
if (code === "ENOENT" || code === "ENOTDIR")
|
|
29
|
+
return null;
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** One regular file's times, or null for anything that is not one. Never opens it. */
|
|
34
|
+
export function sessionFile(path) {
|
|
35
|
+
try {
|
|
36
|
+
const stat = lstatSync(path);
|
|
37
|
+
return stat.isFile() ? { path, mtimeMs: stat.mtimeMs } : null;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** `<dir>/<name>` for each entry that is a real directory (a symlink is not one). */
|
|
44
|
+
export function subdirectories(dir) {
|
|
45
|
+
return (readEntries(dir) ?? [])
|
|
46
|
+
.filter((entry) => entry.isDirectory())
|
|
47
|
+
.map((entry) => ({ name: entry.name, path: join(dir, entry.name) }));
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=session_listing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session_listing.js","sourceRoot":"","sources":["../../src/agents/session_listing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,SAAS,EAAE,WAAW,EAAe,MAAM,SAAS,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjC;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,GAAI,GAA6B,CAAC,IAAI,CAAC;QACjD,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QACzD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;QAC7B,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;SAC5B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;SACtC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;AACzE,CAAC"}
|
package/dist/agents/types.d.ts
CHANGED
|
@@ -137,13 +137,27 @@ export type RedactionProfile = "claude-code" | "codex-cli" | "cursor";
|
|
|
137
137
|
*/
|
|
138
138
|
export type DelegatedTranscripts = "discover" | "announce";
|
|
139
139
|
/**
|
|
140
|
-
* The
|
|
140
|
+
* The four hook events this client registers.
|
|
141
141
|
*
|
|
142
|
-
*
|
|
142
|
+
* Every member of this lane fires the same four; that is exactly why
|
|
143
143
|
* `hook_event_name` was REJECTED as an adapter selector (`CR-183`) — the events
|
|
144
144
|
* are shared, so they discriminate nothing.
|
|
145
|
+
*
|
|
146
|
+
* ⛔ **`SessionStart` is the fourth, and it is the one that never uploads** (0.7.0,
|
|
147
|
+
* eng review D3 · A). `Stop`, `PreCompact` and `SessionEnd` flow into the code
|
|
148
|
+
* that reads a transcript and sends it; `SessionStart` must only install git
|
|
149
|
+
* hooks and say so, so `hooks/entry.ts` resolves the canonical event FIRST and
|
|
150
|
+
* branches to its own handler before the consent gate and before anything reads
|
|
151
|
+
* `transcript_path`. It sits in this list, and not in a table of its own, so the
|
|
152
|
+
* compiler makes each dialect declare its budget and its spelling.
|
|
153
|
+
*
|
|
154
|
+
* event Claude Code Codex Cursor reads a transcript
|
|
155
|
+
* Stop Stop Stop stop yes
|
|
156
|
+
* PreCompact PreCompact PreCompact preCompact yes
|
|
157
|
+
* SessionEnd SessionEnd SessionEnd sessionEnd yes
|
|
158
|
+
* SessionStart SessionStart SessionStart sessionStart NEVER
|
|
145
159
|
*/
|
|
146
|
-
export declare const HOOK_EVENTS: readonly ["Stop", "PreCompact", "SessionEnd"];
|
|
160
|
+
export declare const HOOK_EVENTS: readonly ["Stop", "PreCompact", "SessionEnd", "SessionStart"];
|
|
147
161
|
export type HookEventName = (typeof HOOK_EVENTS)[number];
|
|
148
162
|
/**
|
|
149
163
|
* One event's two clocks, and the relation between them is the point.
|
|
@@ -173,6 +187,77 @@ export interface AgentHookConfig {
|
|
|
173
187
|
readonly path: (home: string, env: NodeJS.ProcessEnv) => string;
|
|
174
188
|
readonly shape: HookDocShape;
|
|
175
189
|
}
|
|
190
|
+
/**
|
|
191
|
+
* One transcript file a source adapter saw — `D11 · A`, `scan`.
|
|
192
|
+
*
|
|
193
|
+
* ⛔ **`stat`ed for its times and NEVER OPENED.** Everything the picker, the
|
|
194
|
+
* offer and the backfill count comes from names and times, because opening a
|
|
195
|
+
* transcript of a repo nobody granted is the read the consent design forbids. A
|
|
196
|
+
* field that could only be filled by reading the file does not belong here.
|
|
197
|
+
*/
|
|
198
|
+
export interface SourceSession {
|
|
199
|
+
readonly path: string;
|
|
200
|
+
readonly mtimeMs: number;
|
|
201
|
+
/**
|
|
202
|
+
* When the agent itself says the session began, where it keeps that. Codex's
|
|
203
|
+
* index does (`created_at`); the folder-keyed agents do not, and for them the
|
|
204
|
+
* file's mtime stands in everywhere a date is shown.
|
|
205
|
+
*/
|
|
206
|
+
readonly startedMs?: number;
|
|
207
|
+
}
|
|
208
|
+
/** One agent project folder: its encoded name, and the sessions found in it. */
|
|
209
|
+
export interface SourceFolder {
|
|
210
|
+
readonly name: string;
|
|
211
|
+
readonly sessions: readonly SourceSession[];
|
|
212
|
+
}
|
|
213
|
+
/** One index row: a session whose agent already recorded the folder it ran in. */
|
|
214
|
+
export interface SourceRow {
|
|
215
|
+
readonly cwd: string;
|
|
216
|
+
readonly origin: string | null;
|
|
217
|
+
readonly session: SourceSession;
|
|
218
|
+
}
|
|
219
|
+
/** Why a store could not be listed. Each is a different line in `Not listed here`. */
|
|
220
|
+
export type UnreadableReason = "unrecognised index" | "index locked" | "unreadable";
|
|
221
|
+
/**
|
|
222
|
+
* What one agent's store yields, without a transcript being opened.
|
|
223
|
+
*
|
|
224
|
+
* The two shapes are the two ways the three agents keep sessions, and they are
|
|
225
|
+
* not interchangeable: a FOLDER-keyed agent (Claude Code, Cursor) names the
|
|
226
|
+
* folder by lossy encoding, so the engine has to resolve it against the
|
|
227
|
+
* filesystem and can fail to (`encode` and `isHashed` are what that walk needs);
|
|
228
|
+
* an INDEXED agent (Codex) records the real `cwd`, so it is never ambiguous.
|
|
229
|
+
*
|
|
230
|
+
* ⛔ **`unreadable` is an answer, not an exception.** One agent's store failing
|
|
231
|
+
* hides that agent's rows and nothing else's, and `scan` reports it; a thrown
|
|
232
|
+
* error here would take the other two agents' rows down with it.
|
|
233
|
+
*/
|
|
234
|
+
export type SessionSource = {
|
|
235
|
+
readonly kind: "folders";
|
|
236
|
+
readonly folders: readonly SourceFolder[];
|
|
237
|
+
/** This dialect's `encodeProjectPath`, handed over so the walk uses the same one. */
|
|
238
|
+
readonly encode: (path: string) => string;
|
|
239
|
+
/** Is this folder name a truncated-and-hashed one, which cannot be inverted? */
|
|
240
|
+
readonly isHashed: (name: string) => boolean;
|
|
241
|
+
/**
|
|
242
|
+
* Paths the agent ITSELF recorded having run in — a second skip signal. A
|
|
243
|
+
* folder whose name encodes to a recorded path that no longer exists is one
|
|
244
|
+
* whose sessions may belong to that deleted path, whatever else now matches.
|
|
245
|
+
* Empty for an agent that keeps no such record.
|
|
246
|
+
*/
|
|
247
|
+
readonly recorded: readonly string[];
|
|
248
|
+
} | {
|
|
249
|
+
readonly kind: "rows";
|
|
250
|
+
readonly rows: readonly SourceRow[];
|
|
251
|
+
/**
|
|
252
|
+
* Rollout files on disk that no index row names — counted, never opened. Only
|
|
253
|
+
* the index's own agent has the notion; a folder-keyed agent has no index to
|
|
254
|
+
* be missing from.
|
|
255
|
+
*/
|
|
256
|
+
readonly unindexed: number;
|
|
257
|
+
} | {
|
|
258
|
+
readonly kind: "unreadable";
|
|
259
|
+
readonly reason: UnreadableReason;
|
|
260
|
+
};
|
|
176
261
|
/** Where `connect` found the session running right now. */
|
|
177
262
|
export interface LocatedTranscript {
|
|
178
263
|
readonly path: string;
|
|
@@ -260,11 +345,14 @@ export interface TranscriptDialect {
|
|
|
260
345
|
* (lost permanently — that event has no next invocation) and no gap-stamp
|
|
261
346
|
* reserve. Nothing raises and the hook still exits 0.
|
|
262
347
|
*
|
|
263
|
-
* ⚠ **Widening `HookEventName` was the
|
|
264
|
-
* The three map 1:1, so a
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
348
|
+
* ⚠ **Widening `HookEventName` was the wrong fix for a RENAME, and it is the right
|
|
349
|
+
* one for a new event** (0.7.0, eng review D3 · A). The first three map 1:1, so a
|
|
350
|
+
* rename belongs in a per-dialect table. `SessionStart` is a new event that ALL
|
|
351
|
+
* THREE agents deliver (Claude Code and Codex measured live, Cursor read out of
|
|
352
|
+
* the shipped bundle — see each dialect), so the argument that stopped the
|
|
353
|
+
* widening — "force every dialect to declare budgets for events it can never
|
|
354
|
+
* receive" — no longer holds, and the compiler is now the thing that makes a
|
|
355
|
+
* dialect added later declare its `SessionStart` spelling and budget.
|
|
268
356
|
*
|
|
269
357
|
* ## ⚠ MEASURED per dialect, and the two measurements differ in kind
|
|
270
358
|
*
|
|
@@ -304,6 +392,28 @@ export interface TranscriptDialect {
|
|
|
304
392
|
* is exactly the cap Codex silently clamps to — measured, not inferred.
|
|
305
393
|
*/
|
|
306
394
|
readonly hookConfig: AgentHookConfig;
|
|
395
|
+
/**
|
|
396
|
+
* ⛔ **WHAT SESSIONS DOES THIS AGENT HAVE ON DISK — without opening one.**
|
|
397
|
+
* `D11 · A`, `scan`, the picker and the backfill offer.
|
|
398
|
+
*
|
|
399
|
+
* Listed by NAME and TIME only (`SourceSession`). The consent design is that no
|
|
400
|
+
* path opens a transcript of an ungranted repo, so the thing that decides what
|
|
401
|
+
* to offer cannot be the thing that reads the transcripts. ⚠ Each adapter fails
|
|
402
|
+
* closed on its own: an unreadable store is `{ kind: "unreadable" }` and hides
|
|
403
|
+
* that agent's rows only.
|
|
404
|
+
*
|
|
405
|
+
* ⛔ **REQUIRED, not optional, and that is `admitsFile`'s device.** A dialect
|
|
406
|
+
* added later must ANSWER how its sessions are found, and the compiler is what
|
|
407
|
+
* asks; an agent that cannot answer without opening a transcript returns
|
|
408
|
+
* `unreadable` and says so, which is a decision a reviewer sees.
|
|
409
|
+
*
|
|
410
|
+
* ⚠ A MEASURED requirement and not a speculative one: Open Question 3 found the
|
|
411
|
+
* two folder-keyed agents encode a path DIFFERENTLY (Claude Code per UTF-16
|
|
412
|
+
* unit with no collapsing; Cursor collapsing runs and trimming the ends), and
|
|
413
|
+
* the shipped `connect` looked in the wrong folder for any repo path holding an
|
|
414
|
+
* `_` or a space because it assumed one rule.
|
|
415
|
+
*/
|
|
416
|
+
readonly sessionSource: (home: string, env: NodeJS.ProcessEnv) => SessionSource;
|
|
307
417
|
/** Per-event clocks. See `EventBudget` for the relation that binds them. */
|
|
308
418
|
readonly events: Readonly<Record<HookEventName, EventBudget>>;
|
|
309
419
|
readonly delegatedTranscripts: DelegatedTranscripts;
|
package/dist/agents/types.js
CHANGED
|
@@ -57,11 +57,25 @@
|
|
|
57
57
|
*/
|
|
58
58
|
export const UNKNOWN_AGENT_ID = "unknown";
|
|
59
59
|
/**
|
|
60
|
-
* The
|
|
60
|
+
* The four hook events this client registers.
|
|
61
61
|
*
|
|
62
|
-
*
|
|
62
|
+
* Every member of this lane fires the same four; that is exactly why
|
|
63
63
|
* `hook_event_name` was REJECTED as an adapter selector (`CR-183`) — the events
|
|
64
64
|
* are shared, so they discriminate nothing.
|
|
65
|
+
*
|
|
66
|
+
* ⛔ **`SessionStart` is the fourth, and it is the one that never uploads** (0.7.0,
|
|
67
|
+
* eng review D3 · A). `Stop`, `PreCompact` and `SessionEnd` flow into the code
|
|
68
|
+
* that reads a transcript and sends it; `SessionStart` must only install git
|
|
69
|
+
* hooks and say so, so `hooks/entry.ts` resolves the canonical event FIRST and
|
|
70
|
+
* branches to its own handler before the consent gate and before anything reads
|
|
71
|
+
* `transcript_path`. It sits in this list, and not in a table of its own, so the
|
|
72
|
+
* compiler makes each dialect declare its budget and its spelling.
|
|
73
|
+
*
|
|
74
|
+
* event Claude Code Codex Cursor reads a transcript
|
|
75
|
+
* Stop Stop Stop stop yes
|
|
76
|
+
* PreCompact PreCompact PreCompact preCompact yes
|
|
77
|
+
* SessionEnd SessionEnd SessionEnd sessionEnd yes
|
|
78
|
+
* SessionStart SessionStart SessionStart sessionStart NEVER
|
|
65
79
|
*/
|
|
66
|
-
export const HOOK_EVENTS = ["Stop", "PreCompact", "SessionEnd"];
|
|
80
|
+
export const HOOK_EVENTS = ["Stop", "PreCompact", "SessionEnd", "SessionStart"];
|
|
67
81
|
//# sourceMappingURL=types.js.map
|
package/dist/agents/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/agents/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AA4BH;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,SAAS,CAAC;AA6D1C
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/agents/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AA4BH;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,SAAS,CAAC;AA6D1C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,CAAU,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The org-approval-pending block — style guide §10.2's "held" beat, D65 §DR6, D81. One definition for the two places that
|
|
3
|
+
* print it: `connect`, on the 403 its own test capture got, and `status`, for a repo whose last upload got that same 403.
|
|
4
|
+
*
|
|
5
|
+
* ⚠ EXISTING COPY ONLY. `status` adds no sentence of its own; it prints what `connect` prints, so the two cannot drift
|
|
6
|
+
* (`org_pending.ts` records that the 403 happened). The shared clause renders on EVERY branch and `owners_notified` `> 0`
|
|
7
|
+
* ADDS to it (D81); `null` and `0` never claim a notification.
|
|
8
|
+
*/
|
|
9
|
+
export declare function approvalPendingBlock(colour: boolean, repoSlug: string, ownersNotified: number | null): string[];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The org-approval-pending block — style guide §10.2's "held" beat, D65 §DR6, D81. One definition for the two places that
|
|
3
|
+
* print it: `connect`, on the 403 its own test capture got, and `status`, for a repo whose last upload got that same 403.
|
|
4
|
+
*
|
|
5
|
+
* ⚠ EXISTING COPY ONLY. `status` adds no sentence of its own; it prints what `connect` prints, so the two cannot drift
|
|
6
|
+
* (`org_pending.ts` records that the 403 happened). The shared clause renders on EVERY branch and `owners_notified` `> 0`
|
|
7
|
+
* ADDS to it (D81); `null` and `0` never claim a notification.
|
|
8
|
+
*/
|
|
9
|
+
import { CONNECT, URLS } from "../copy/index.js";
|
|
10
|
+
import { renderErrorBlock } from "../term.js";
|
|
11
|
+
export function approvalPendingBlock(colour, repoSlug, ownersNotified) {
|
|
12
|
+
const why = ownersNotified !== null && ownersNotified > 0
|
|
13
|
+
? `${CONNECT.pendingRecorded} ${CONNECT.pendingNotified(ownersNotified)}`
|
|
14
|
+
: CONNECT.pendingRecorded;
|
|
15
|
+
return renderErrorBlock({
|
|
16
|
+
kind: "warn",
|
|
17
|
+
what: CONNECT.pendingWhat,
|
|
18
|
+
why: [CONNECT.pendingWhy(repoSlug), why, CONNECT.pendingBuffered],
|
|
19
|
+
fixLabel: CONNECT.pendingTrackLabel,
|
|
20
|
+
fixes: [URLS.captureAccess],
|
|
21
|
+
}, colour);
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=approval_pending.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval_pending.js","sourceRoot":"","sources":["../../src/commands/approval_pending.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,UAAU,oBAAoB,CAAC,MAAe,EAAE,QAAgB,EAAE,cAA6B;IACnG,MAAM,GAAG,GACP,cAAc,KAAK,IAAI,IAAI,cAAc,GAAG,CAAC;QAC3C,CAAC,CAAC,GAAG,OAAO,CAAC,eAAe,IAAI,OAAO,CAAC,eAAe,CAAC,cAAc,CAAC,EAAE;QACzE,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC;IAC9B,OAAO,gBAAgB,CACrB;QACE,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,OAAO,CAAC,WAAW;QACzB,GAAG,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,eAAe,CAAC;QACjE,QAAQ,EAAE,OAAO,CAAC,iBAAiB;QACnC,KAAK,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC;KAC5B,EACD,MAAM,CACP,CAAC;AACJ,CAAC"}
|
package/dist/commands/auth.d.ts
CHANGED
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
*/
|
|
76
76
|
import type { CommandContext } from "./context.js";
|
|
77
77
|
import type { BrowserCredential, BrowserOptions, Retry } from "./browser_credential.js";
|
|
78
|
+
import type { VerbSyncDeps } from "../grants_verb.js";
|
|
78
79
|
export interface AuthDeps {
|
|
79
80
|
/**
|
|
80
81
|
* Read the secret from a terminal WITHOUT ECHOING IT. Injected for the same
|
|
@@ -101,5 +102,11 @@ export interface AuthDeps {
|
|
|
101
102
|
* `src/index.ts` passes one that carries NO `browser` (never a second sign-in).
|
|
102
103
|
*/
|
|
103
104
|
readonly connect?: (ctx: CommandContext) => Promise<number>;
|
|
105
|
+
/**
|
|
106
|
+
* Slice 2, R3-7. The account's network, injected like `connect`: a credential this verb saves on a machine that has synced before
|
|
107
|
+
* is synced before the verb returns, so the new credential row is synced from its first upload. Left out, the verb makes no
|
|
108
|
+
* request, as before. `src/index.ts` passes the real `fetch`.
|
|
109
|
+
*/
|
|
110
|
+
readonly account?: VerbSyncDeps;
|
|
104
111
|
}
|
|
105
112
|
export declare function auth(ctx: CommandContext, argv: readonly string[], deps?: AuthDeps): Promise<number>;
|
package/dist/commands/auth.js
CHANGED
|
@@ -73,11 +73,14 @@
|
|
|
73
73
|
* @provenance vibecommit-mcp src/transport/server.ts — POST /oauth/ingest-credential is the mint route, verified
|
|
74
74
|
* @provenance vibecommit-web app/api/ingest-credentials/route.ts — the dashboard producer the paste path still serves, verified
|
|
75
75
|
*/
|
|
76
|
-
import { AUTH,
|
|
77
|
-
import {
|
|
76
|
+
import { AUTH, DEVICE, PICKER, URLS } from "../copy/index.js";
|
|
77
|
+
import { renderVerbHelp } from "../verb_help.js";
|
|
78
|
+
import { authCommandFor, channelCommand } from "../install.js";
|
|
78
79
|
import { writeLines } from "./context.js";
|
|
79
80
|
import { EXIT } from "../exit.js";
|
|
80
81
|
import { INGEST_TOKEN_PREFIX, IngestCredential, saveCredential } from "../credential.js";
|
|
82
|
+
import { readGrants } from "../consent.js";
|
|
83
|
+
import { machineCapture, machineSummary } from "../machine_state.js";
|
|
81
84
|
import { credentialsPath } from "../paths.js";
|
|
82
85
|
import { resolveProjectKeys } from "../project.js";
|
|
83
86
|
import { paint, renderErrorBlock, tildePath, wrap } from "../term.js";
|
|
@@ -98,10 +101,11 @@ export async function auth(ctx, argv, deps = {}) {
|
|
|
98
101
|
if (positional.length > 0) {
|
|
99
102
|
writeLines(ctx.stderr, renderErrorBlock({
|
|
100
103
|
kind: "bad",
|
|
104
|
+
code: "CREDENTIAL_IN_ARGV",
|
|
101
105
|
what: AUTH.argvWhat,
|
|
102
106
|
why: [AUTH.argvWhy],
|
|
103
107
|
fixLabel: AUTH.argvFix,
|
|
104
|
-
fixes: [AUTH.argvFixPrompt, AUTH.argvFixStdin],
|
|
108
|
+
fixes: [channelCommand(ctx.env, ctx.selfPath, AUTH.argvFixPrompt), AUTH.argvFixStdin(authCommandFor(ctx.env, ctx.selfPath))],
|
|
105
109
|
}, ctx.colour));
|
|
106
110
|
return EXIT.usage;
|
|
107
111
|
}
|
|
@@ -110,7 +114,8 @@ export async function auth(ctx, argv, deps = {}) {
|
|
|
110
114
|
// exactly that a usage error) runs first. By the time this is reached, `positional.length === 0`, so
|
|
111
115
|
// `--help` still has no side effects — nothing below has read or written anything yet either way.
|
|
112
116
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
113
|
-
|
|
117
|
+
const helpVerb = "auth"; // an identifier, not a literal, at the writer: `copy-inline-literals` follows argument positions
|
|
118
|
+
writeLines(ctx.stdout, renderVerbHelp(helpVerb));
|
|
114
119
|
return EXIT.ok;
|
|
115
120
|
}
|
|
116
121
|
const piped = argv.includes(STDIN_FLAG) || !ctx.stdinIsTty;
|
|
@@ -119,6 +124,7 @@ export async function auth(ctx, argv, deps = {}) {
|
|
|
119
124
|
writeLines(ctx.stderr, renderErrorBlock(piped
|
|
120
125
|
? {
|
|
121
126
|
kind: "bad",
|
|
127
|
+
code: "SIGNIN_CODE_NEEDS_TTY",
|
|
122
128
|
what: DEVICE.needsTerminalWhat,
|
|
123
129
|
why: [DEVICE.needsTerminalWhy],
|
|
124
130
|
fixLabel: DEVICE.needsTerminalFix,
|
|
@@ -126,10 +132,11 @@ export async function auth(ctx, argv, deps = {}) {
|
|
|
126
132
|
}
|
|
127
133
|
: {
|
|
128
134
|
kind: "bad",
|
|
135
|
+
code: "AUTH_FLAGS_CONFLICT",
|
|
129
136
|
what: DEVICE.conflictWhat,
|
|
130
137
|
why: [DEVICE.conflictWhy],
|
|
131
138
|
fixLabel: DEVICE.conflictFix,
|
|
132
|
-
fixes: [
|
|
139
|
+
fixes: [authCommandFor(ctx.env, ctx.selfPath), `${authCommandFor(ctx.env, ctx.selfPath)} ${PASTE_FLAG}`],
|
|
133
140
|
}, ctx.colour));
|
|
134
141
|
return EXIT.usage;
|
|
135
142
|
}
|
|
@@ -161,10 +168,11 @@ export async function auth(ctx, argv, deps = {}) {
|
|
|
161
168
|
if (secret === "") {
|
|
162
169
|
writeLines(ctx.stderr, renderErrorBlock({
|
|
163
170
|
kind: "bad",
|
|
171
|
+
code: "CREDENTIAL_EMPTY",
|
|
164
172
|
what: AUTH.emptyWhat,
|
|
165
173
|
why: [AUTH.emptyWhy],
|
|
166
|
-
fixLabel: AUTH.
|
|
167
|
-
fixes:
|
|
174
|
+
fixLabel: AUTH.mintFix(URLS.settings),
|
|
175
|
+
fixes: AUTH.mintCommands(authCommandFor(ctx.env, ctx.selfPath)),
|
|
168
176
|
}, ctx.colour));
|
|
169
177
|
return EXIT.failure;
|
|
170
178
|
}
|
|
@@ -175,10 +183,11 @@ export async function auth(ctx, argv, deps = {}) {
|
|
|
175
183
|
if (!secret.startsWith(INGEST_TOKEN_PREFIX)) {
|
|
176
184
|
writeLines(ctx.stderr, renderErrorBlock({
|
|
177
185
|
kind: "bad",
|
|
186
|
+
code: "CREDENTIAL_NOT_INGEST",
|
|
178
187
|
what: AUTH.wrongClassWhat,
|
|
179
188
|
why: [AUTH.wrongClassWhy],
|
|
180
|
-
fixLabel: AUTH.
|
|
181
|
-
fixes:
|
|
189
|
+
fixLabel: AUTH.mintFix(URLS.settings),
|
|
190
|
+
fixes: AUTH.mintCommands(authCommandFor(ctx.env, ctx.selfPath)),
|
|
182
191
|
}, ctx.colour));
|
|
183
192
|
return EXIT.failure;
|
|
184
193
|
}
|
|
@@ -194,17 +203,49 @@ export async function auth(ctx, argv, deps = {}) {
|
|
|
194
203
|
* block and the exit are exactly what they were.
|
|
195
204
|
*/
|
|
196
205
|
async function saved(ctx, deps, piped, notes = []) {
|
|
197
|
-
|
|
206
|
+
// 0.7.0, D32 · 26A (supersedes only L6's "outside a repository" clause). Inside a work tree the chain is unchanged (L2).
|
|
207
|
+
// Outside one, the continuation is the picker flow, and only on a machine that has nothing captured yet; a machine that
|
|
208
|
+
// already captures prints one line and no picker. ⛔ BOTH of 0.6.0's other guards stay: `piped` (`--stdin`, or no
|
|
209
|
+
// terminal) NEVER continues, so a CI runner with no grant still saves the credential and exits 0 instead of reaching the
|
|
210
|
+
// non-terminal refusal and exiting 2; and the continuation gets no `browser` (`index.ts` hands `connect` none), so a
|
|
211
|
+
// second sign-in cannot start.
|
|
212
|
+
const inRepo = resolveProjectKeys(ctx.cwd) !== null;
|
|
213
|
+
const capture = inRepo || piped || deps.connect === undefined ? null : machineCapture(ctx.home);
|
|
214
|
+
const shouldConnect = !piped && deps.connect !== undefined && (inRepo || capture?.kind === "none");
|
|
215
|
+
const staysOn = capture?.kind === "on";
|
|
198
216
|
writeLines(ctx.stdout, [
|
|
199
217
|
...renderErrorBlock({
|
|
200
218
|
kind: "ok",
|
|
201
219
|
what: AUTH.savedWhat,
|
|
202
220
|
why: [AUTH.savedWhy(tildePath(credentialsPath(ctx.home), ctx.home)), ...notes],
|
|
203
|
-
...(shouldConnect
|
|
221
|
+
...(shouldConnect || staysOn
|
|
222
|
+
? {}
|
|
223
|
+
: { fixLabel: AUTH.nextLabel, fixes: [channelCommand(ctx.env, ctx.selfPath, AUTH.nextCommand)] }),
|
|
204
224
|
}, ctx.colour),
|
|
225
|
+
...(capture?.kind === "on" ? ["", ...wrap(PICKER.authStays(machineSummary(capture)), 2)] : []),
|
|
205
226
|
]);
|
|
206
227
|
if (shouldConnect && deps.connect !== undefined)
|
|
207
228
|
return await deps.connect(ctx);
|
|
229
|
+
// Not chaining (the chained `connect` syncs for itself): this is the only other place a credential is saved, so it is the other
|
|
230
|
+
// place a new credential row is synced. ⛔ It makes no prompt and prints nothing, so a piped or no-terminal `auth` (which never
|
|
231
|
+
// continues, D32 guard 1) is as quiet as before; a failure is silent and the exit stays what it was.
|
|
232
|
+
// ⛔ A piped, `--stdin` or no-terminal `auth` does NOT sync (L10: "with `--stdin`, or with no terminal, it only saves the credential"): a
|
|
233
|
+
// sync sends this machine's grants, which is more than saving. R3-7 is for a person at a terminal.
|
|
234
|
+
if (!piped)
|
|
235
|
+
await syncNewCredential(ctx, deps);
|
|
208
236
|
return EXIT.ok;
|
|
209
237
|
}
|
|
238
|
+
/**
|
|
239
|
+
* R3-7, for a TERMINAL `auth` only (the caller skips a piped one). Only a machine that HAS synced (`grants.json`'s `synced_at`) syncs here: a machine that never has would be making a FIRST
|
|
240
|
+
* sync, which prints what it is saving and belongs to `connect`/`status`/`off`, not to a verb whose job is to save a credential.
|
|
241
|
+
* Bounded at 3 s and silent on failure (`syncForVerb` never throws).
|
|
242
|
+
*/
|
|
243
|
+
async function syncNewCredential(ctx, deps) {
|
|
244
|
+
if (deps.account === undefined)
|
|
245
|
+
return;
|
|
246
|
+
const read = readGrants(ctx.home);
|
|
247
|
+
if (read.kind !== "ok" || read.grants.synced_at === null)
|
|
248
|
+
return;
|
|
249
|
+
await (await import("../grants_verb.js")).syncForVerb(ctx, false, deps.account);
|
|
250
|
+
}
|
|
210
251
|
//# sourceMappingURL=auth.js.map
|