@vibe-commit/capture 0.7.0 → 0.9.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 (152) hide show
  1. package/README.md +238 -7
  2. package/dist/account_not_here.d.ts +15 -0
  3. package/dist/account_not_here.js +25 -0
  4. package/dist/account_not_here.js.map +1 -0
  5. package/dist/agents/claude_code.js +13 -0
  6. package/dist/agents/claude_code.js.map +1 -1
  7. package/dist/agents/codex_cli.js +11 -0
  8. package/dist/agents/codex_cli.js.map +1 -1
  9. package/dist/agents/cursor.js +7 -1
  10. package/dist/agents/cursor.js.map +1 -1
  11. package/dist/agents/registry.d.ts +7 -0
  12. package/dist/agents/registry.js +9 -0
  13. package/dist/agents/registry.js.map +1 -1
  14. package/dist/agents/types.d.ts +32 -0
  15. package/dist/agents/types.js.map +1 -1
  16. package/dist/bounded_id.d.ts +29 -0
  17. package/dist/bounded_id.js +42 -0
  18. package/dist/bounded_id.js.map +1 -0
  19. package/dist/codex_folders.d.ts +340 -0
  20. package/dist/codex_folders.js +1448 -0
  21. package/dist/codex_folders.js.map +1 -0
  22. package/dist/commands/auth.d.ts +7 -0
  23. package/dist/commands/auth.js +21 -0
  24. package/dist/commands/auth.js.map +1 -1
  25. package/dist/commands/backfill.d.ts +29 -7
  26. package/dist/commands/backfill.js +387 -109
  27. package/dist/commands/backfill.js.map +1 -1
  28. package/dist/commands/browser_credential.d.ts +4 -2
  29. package/dist/commands/browser_credential.js +5 -2
  30. package/dist/commands/browser_credential.js.map +1 -1
  31. package/dist/commands/connect.d.ts +10 -1
  32. package/dist/commands/connect.js +14 -9
  33. package/dist/commands/connect.js.map +1 -1
  34. package/dist/commands/connect_account.d.ts +135 -0
  35. package/dist/commands/connect_account.js +321 -0
  36. package/dist/commands/connect_account.js.map +1 -0
  37. package/dist/commands/connect_picker.js +204 -29
  38. package/dist/commands/connect_picker.js.map +1 -1
  39. package/dist/commands/device_credential.d.ts +3 -1
  40. package/dist/commands/device_credential.js +1 -0
  41. package/dist/commands/device_credential.js.map +1 -1
  42. package/dist/commands/finalize.d.ts +67 -0
  43. package/dist/commands/finalize.js +450 -114
  44. package/dist/commands/finalize.js.map +1 -1
  45. package/dist/commands/finish_mint.d.ts +28 -0
  46. package/dist/commands/finish_mint.js +16 -4
  47. package/dist/commands/finish_mint.js.map +1 -1
  48. package/dist/commands/scan.d.ts +28 -0
  49. package/dist/commands/scan.js +11 -0
  50. package/dist/commands/scan.js.map +1 -1
  51. package/dist/commands/status.d.ts +19 -1
  52. package/dist/commands/status.js +123 -14
  53. package/dist/commands/status.js.map +1 -1
  54. package/dist/commands/status_machine.d.ts +12 -3
  55. package/dist/commands/status_machine.js +111 -13
  56. package/dist/commands/status_machine.js.map +1 -1
  57. package/dist/commands/sync_reason.d.ts +17 -0
  58. package/dist/commands/sync_reason.js +25 -0
  59. package/dist/commands/sync_reason.js.map +1 -0
  60. package/dist/consent.d.ts +131 -3
  61. package/dist/consent.js +248 -14
  62. package/dist/consent.js.map +1 -1
  63. package/dist/copy/strings.d.ts +165 -4
  64. package/dist/copy/strings.js +190 -7
  65. package/dist/copy/strings.js.map +1 -1
  66. package/dist/folder_grants.d.ts +44 -0
  67. package/dist/folder_grants.js +101 -0
  68. package/dist/folder_grants.js.map +1 -0
  69. package/dist/git.d.ts +26 -4
  70. package/dist/git.js +82 -11
  71. package/dist/git.js.map +1 -1
  72. package/dist/grants_sync.d.ts +165 -0
  73. package/dist/grants_sync.js +289 -0
  74. package/dist/grants_sync.js.map +1 -0
  75. package/dist/grants_verb.d.ts +70 -0
  76. package/dist/grants_verb.js +121 -0
  77. package/dist/grants_verb.js.map +1 -0
  78. package/dist/hooks/entry.d.ts +30 -50
  79. package/dist/hooks/entry.js +601 -561
  80. package/dist/hooks/entry.js.map +1 -1
  81. package/dist/hooks/fire_lock.d.ts +31 -0
  82. package/dist/hooks/fire_lock.js +120 -12
  83. package/dist/hooks/fire_lock.js.map +1 -1
  84. package/dist/hooks/session_grants.d.ts +59 -0
  85. package/dist/hooks/session_grants.js +91 -0
  86. package/dist/hooks/session_grants.js.map +1 -0
  87. package/dist/hooks/session_start.d.ts +28 -7
  88. package/dist/hooks/session_start.js +55 -11
  89. package/dist/hooks/session_start.js.map +1 -1
  90. package/dist/index.js +22 -5
  91. package/dist/index.js.map +1 -1
  92. package/dist/machine_state.d.ts +44 -1
  93. package/dist/machine_state.js +75 -12
  94. package/dist/machine_state.js.map +1 -1
  95. package/dist/oauth/discovery.js +2 -1
  96. package/dist/oauth/discovery.js.map +1 -1
  97. package/dist/oauth/pkce.js +2 -1
  98. package/dist/oauth/pkce.js.map +1 -1
  99. package/dist/oauth/retire.d.ts +10 -0
  100. package/dist/oauth/retire.js +13 -0
  101. package/dist/oauth/retire.js.map +1 -1
  102. package/dist/paths.d.ts +54 -1
  103. package/dist/paths.js +88 -12
  104. package/dist/paths.js.map +1 -1
  105. package/dist/pending_revoke.d.ts +117 -0
  106. package/dist/pending_revoke.js +233 -0
  107. package/dist/pending_revoke.js.map +1 -0
  108. package/dist/pending_revoke_file.d.ts +55 -0
  109. package/dist/pending_revoke_file.js +74 -0
  110. package/dist/pending_revoke_file.js.map +1 -0
  111. package/dist/physical_path.d.ts +46 -0
  112. package/dist/physical_path.js +151 -0
  113. package/dist/physical_path.js.map +1 -0
  114. package/dist/picker.d.ts +82 -5
  115. package/dist/picker.js +183 -10
  116. package/dist/picker.js.map +1 -1
  117. package/dist/picker_checklist.d.ts +27 -3
  118. package/dist/picker_checklist.js +81 -6
  119. package/dist/picker_checklist.js.map +1 -1
  120. package/dist/policy.d.ts +24 -1
  121. package/dist/policy.js +30 -2
  122. package/dist/policy.js.map +1 -1
  123. package/dist/post.d.ts +55 -2
  124. package/dist/post.js +72 -22
  125. package/dist/post.js.map +1 -1
  126. package/dist/project.d.ts +89 -0
  127. package/dist/project.js +123 -0
  128. package/dist/project.js.map +1 -1
  129. package/dist/redact.d.ts +77 -2
  130. package/dist/redact.js +713 -47
  131. package/dist/redact.js.map +1 -1
  132. package/dist/session_targets.d.ts +713 -0
  133. package/dist/session_targets.js +2602 -0
  134. package/dist/session_targets.js.map +1 -0
  135. package/dist/spool.d.ts +12 -0
  136. package/dist/spool.js +29 -1
  137. package/dist/spool.js.map +1 -1
  138. package/dist/spool_union.d.ts +51 -0
  139. package/dist/spool_union.js +57 -0
  140. package/dist/spool_union.js.map +1 -0
  141. package/dist/state.d.ts +5 -0
  142. package/dist/state.js +19 -1
  143. package/dist/state.js.map +1 -1
  144. package/dist/system_message.d.ts +25 -2
  145. package/dist/system_message.js +29 -5
  146. package/dist/system_message.js.map +1 -1
  147. package/dist/text_tail.d.ts +8 -0
  148. package/dist/text_tail.js +19 -0
  149. package/dist/text_tail.js.map +1 -0
  150. package/dist/version.d.ts +1 -1
  151. package/dist/version.js +1 -1
  152. package/package.json +3 -3
@@ -0,0 +1,233 @@
1
+ /**
2
+ * Pending revokes (slice 2, S4, R3-11): the WRITE-AHEAD record of a credential `g` has made and not yet been told to keep.
3
+ *
4
+ * ```
5
+ * addPendingRevoke(home, credential, endpoint, now) at the mint, BEFORE anything else: refuses (never overwrites) when it cannot
6
+ * removePendingRevoke(home, token) after the revoke, or after the credential is kept: re-reads, removes ONE row
7
+ * revokeHeld(home, token, fetch) one best-effort revoke (3 s) of a row, to ITS recorded origin
8
+ * retryPendingRevokes(home, env, fetch, now) every user-facing verb, once, at its start: one 3 s bound across all rows
9
+ * readPendingRevokes(home) how many are waiting (for `status`'s `pending` count)
10
+ * ```
11
+ *
12
+ * ## ⛔ WHY IT IS WRITE-AHEAD
13
+ *
14
+ * `g` is the one place a credential is made ahead of consent (exception 3). So the credential lives in MEMORY until the yes:
15
+ * `credentials.json` does not exist before it, and the row below is written the instant the server hands the token over. A kill, a
16
+ * second Ctrl+C, a SIGHUP at the y/N or an unexpected throw therefore leaves a ROW (which the next verb revokes), never a live
17
+ * credential on disk that nothing remembers. The yes commits in this order: the grants, then `credentials.json`, then the row's
18
+ * deletion; a retry skips and drops any row whose token IS the current credential, so a kill between the last two never revokes what
19
+ * the user kept.
20
+ *
21
+ * ## ⛔ THE FILE HOLDS A BEARER TOKEN — the same exposure class as `credentials.json`, held to the same rules
22
+ * - written mode 0600 under the 0700 directory, temp + rename;
23
+ * - READ through `readSecretFile`: a file or folder others can read is refused, and an insecure or unreadable file is NEVER
24
+ * overwritten (the writer refuses, and `g` fails closed);
25
+ * - never printed, logged or returned: `status` counts the rows and shows nothing else, `--json` included;
26
+ * - a hook never touches it (only the CLI verbs import this module; `test/connect-account.test.ts` walks the hooks' import graph).
27
+ *
28
+ * ## ⛔ A ROW IS NEVER DROPPED UNREVOKED, except by the clock
29
+ * - it leaves on a 2xx, a 401 (the server does not accept it: already dead) or a 404 (a server that cannot revoke);
30
+ * - or at `minted_at` + 91 days: a token that was never on disk was never used, and an ingest credential ends 90 days unused;
31
+ * - there is no row cap that drops one: at the cap `addPendingRevoke` REFUSES, and `g` fails closed.
32
+ *
33
+ * ## ⛔ THE ORIGIN IS THE ROW'S
34
+ *
35
+ * A row records the issuing server's origin and a retry goes ONLY there (https, or http on loopback: `isAllowedIngestUrl`), never to
36
+ * wherever `VIBECOMMIT_MCP_URL` points today.
37
+ */
38
+ import { chmodSync, mkdirSync, readFileSync, unlinkSync } from "node:fs";
39
+ import { writeFileAtomic } from "./atomic_write.js";
40
+ import { IngestCredential, INGEST_TOKEN_PREFIX } from "./credential.js";
41
+ import { LEASE_MS, leased, readPendingRevokes, readQueue } from "./pending_revoke_file.js";
42
+ import { REVOKE_TIMEOUT_MS, selfRevokeOutcome } from "./oauth/retire.js";
43
+ import { credentialsPath, pendingRevokePath, rootDir } from "./paths.js";
44
+ import { isAllowedIngestUrl } from "./post.js";
45
+ export { readPendingRevokes } from "./pending_revoke_file.js";
46
+ /** More rows than `g` can produce in earnest: past it `g` refuses rather than drop one. */
47
+ export const MAX_ROWS = 8;
48
+ /** A token never used ends 90 days after it is made; one day of slack. */
49
+ const MAX_AGE_MS = 91 * 24 * 60 * 60 * 1000;
50
+ /** One best-effort revoke, and the bound on a verb's whole retry. */
51
+ export { REVOKE_TIMEOUT_MS };
52
+ function writeRows(home, rows) {
53
+ if (rows.length === 0) {
54
+ try {
55
+ unlinkSync(pendingRevokePath(home));
56
+ }
57
+ catch {
58
+ /* already gone */
59
+ }
60
+ return;
61
+ }
62
+ mkdirSync(rootDir(home), { recursive: true, mode: 0o700 });
63
+ chmodSync(rootDir(home), 0o700);
64
+ writeFileAtomic(pendingRevokePath(home), `${JSON.stringify({ revokes: rows }, null, 2)}\n`, 0o600);
65
+ }
66
+ /**
67
+ * Write the row for a credential that has just been made. Never overwrites a file it cannot read, never drops a row.
68
+ * `lease: true` is `g` holding the credential at its y/N: the row carries `{ pid, lease_until }` (30 minutes) so another verb's retry
69
+ * leaves it alone while it is kept; every other writer (a test, an orphaned row) has no lease and is retried at once.
70
+ */
71
+ export function addPendingRevoke(home, credential, endpoint, now, options = {}) {
72
+ try {
73
+ const token = credential.expose();
74
+ if (!token.startsWith(INGEST_TOKEN_PREFIX))
75
+ return { kind: "failed" };
76
+ const queue = readQueue(home);
77
+ if (queue.kind === "refused")
78
+ return { kind: "refused", read: queue.read };
79
+ const rows = queue.kind === "ok" ? queue.rows : [];
80
+ if (rows.some((row) => row.token === token))
81
+ return { kind: "written" };
82
+ if (rows.length >= MAX_ROWS)
83
+ return { kind: "full" };
84
+ const lease = options.lease === true ? { pid: process.pid, lease_until: new Date(now.getTime() + LEASE_MS).toISOString() } : null;
85
+ writeRows(home, [...rows, { token, minted_at: now.toISOString(), origin: new URL(endpoint).origin, lease }]);
86
+ return { kind: "written" };
87
+ }
88
+ catch {
89
+ return { kind: "failed" };
90
+ }
91
+ }
92
+ /** Remove ONE row, re-reading just before the write so a row queued in between survives. Deletes the file when it is empty. Never throws. */
93
+ export function removePendingRevoke(home, token) {
94
+ removeRows(home, new Set([token]));
95
+ }
96
+ /** Give the row up to the retry: `g` is no longer holding the credential, so the next verb may revoke it. Re-reads before it writes. */
97
+ export function releaseLease(home, token) {
98
+ try {
99
+ const queue = readQueue(home);
100
+ if (queue.kind !== "ok" || !queue.rows.some((row) => row.token === token && row.lease !== null))
101
+ return;
102
+ writeRows(home, queue.rows.map((row) => (row.token === token ? { ...row, lease: null } : row)));
103
+ }
104
+ catch {
105
+ /* the lease runs out by itself */
106
+ }
107
+ }
108
+ /**
109
+ * Is the row for `token` still there? `g` asks just before it commits, and the three answers are different things:
110
+ * - `held`: the row is in a queue this client can use;
111
+ * - `gone`: the queue is readable and the row is not in it — another verb settled (revoked) it;
112
+ * - `unusable`: the queue is insecure or unreadable. Nobody revoked anything; this client can neither keep nor settle the record.
113
+ */
114
+ export function holdsRow(home, token) {
115
+ const queue = readQueue(home);
116
+ if (queue.kind === "refused")
117
+ return "unusable";
118
+ return queue.kind === "ok" && queue.rows.some((row) => row.token === token) ? "held" : "gone";
119
+ }
120
+ function removeRows(home, tokens) {
121
+ try {
122
+ const queue = readQueue(home);
123
+ if (queue.kind !== "ok")
124
+ return;
125
+ const rows = queue.rows.filter((row) => !tokens.has(row.token));
126
+ if (rows.length !== queue.rows.length)
127
+ writeRows(home, rows);
128
+ }
129
+ catch {
130
+ /* the row stays; the next verb retries */
131
+ }
132
+ }
133
+ /** Revoke a token, with itself as the bearer, at `origin` — and only where a credential may be sent at all. */
134
+ async function revokeToken(origin, token, fetchImpl, timeoutMs) {
135
+ if (!isAllowedIngestUrl(origin))
136
+ return null;
137
+ const outcome = await selfRevokeOutcome(origin, new IngestCredential(token, "file"), { fetch: fetchImpl, timeoutMs });
138
+ return outcome.kind === "retired" || outcome.kind === "gone" ? "revoked" : outcome.kind === "unknown" ? "unrevocable" : null;
139
+ }
140
+ /** The tokens this machine is using right now: `credentials.json`'s, read RAW (a mode problem does not hide it) and the environment's. */
141
+ function currentTokens(home, env) {
142
+ const out = new Set();
143
+ try {
144
+ const token = JSON.parse(readFileSync(credentialsPath(home), "utf8")).token;
145
+ if (typeof token === "string" && token !== "")
146
+ out.add(token);
147
+ }
148
+ catch {
149
+ /* no file, or not ours: nothing to protect */
150
+ }
151
+ const envToken = env.VIBECOMMIT_TOKEN?.trim();
152
+ if (envToken !== undefined && envToken !== "")
153
+ out.add(envToken);
154
+ return out;
155
+ }
156
+ /**
157
+ * `g`'s every no-grant exit: one revoke within 3 s. With a readable row it goes to the row's recorded origin and removes the row on a
158
+ * 2xx, 401 or 404 (a failure gives the lease up so the next verb retries). With NO usable row — missing, or the queue refused — it
159
+ * revokes with the in-memory credential at the MINTED origin: a missing row is never taken for "already revoked".
160
+ */
161
+ export async function revokeHeld(home, credential, mintedOrigin, fetchImpl) {
162
+ const token = credential.expose();
163
+ let insecure = null;
164
+ try {
165
+ const queue = readQueue(home);
166
+ const row = queue.kind === "ok" ? queue.rows.find((r) => r.token === token) : undefined;
167
+ if (queue.kind === "refused" && queue.read.kind === "insecure-file")
168
+ insecure = queue.read;
169
+ if (row !== undefined) {
170
+ const ended = await revokeToken(row.origin, token, fetchImpl, REVOKE_TIMEOUT_MS);
171
+ if (ended !== null) {
172
+ removePendingRevoke(home, token);
173
+ return { kind: ended, insecure };
174
+ }
175
+ releaseLease(home, token);
176
+ return { kind: "waiting", insecure };
177
+ }
178
+ const ended = await revokeToken(mintedOrigin, token, fetchImpl, REVOKE_TIMEOUT_MS);
179
+ return { kind: ended ?? "orphan", insecure };
180
+ }
181
+ catch {
182
+ return { kind: "orphan", insecure };
183
+ }
184
+ }
185
+ /**
186
+ * Every user-facing verb, once, at its start: one attempt at each row, and ONE 3 s bound across all of them (a row gets what is
187
+ * left of the bound, and none is attempted once it is spent). Silent, and needs no credential.
188
+ *
189
+ * Before EACH request it re-reads the queue and the current credential (a credential committed, or a row settled, after the loop
190
+ * began is never revoked on a stale view), and it
191
+ * - skips and DROPS a row whose token is the current credential (the user kept it; the kill came between the credential's write
192
+ * and the row's deletion) — compared on the file's raw token, so a mode problem does not hide a kept credential;
193
+ * - skips (and keeps) a LEASED row: `g` is holding it at its y/N in another process;
194
+ * - drops a row past `minted_at` + 91 days.
195
+ * Removes each row it settles as it goes, re-reading just before it writes. Returns how many rows still wait. Never throws.
196
+ */
197
+ export async function retryPendingRevokes(home, env, fetchImpl, now, deadlineMs = REVOKE_TIMEOUT_MS) {
198
+ try {
199
+ const first = readQueue(home);
200
+ if (first.kind !== "ok")
201
+ return 0;
202
+ const started = Date.now();
203
+ for (const listed of first.rows) {
204
+ const fresh = readQueue(home);
205
+ if (fresh.kind !== "ok")
206
+ break;
207
+ const row = fresh.rows.find((r) => r.token === listed.token);
208
+ if (row === undefined)
209
+ continue;
210
+ if (currentTokens(home, env).has(row.token)) {
211
+ removePendingRevoke(home, row.token);
212
+ continue;
213
+ }
214
+ if (leased(row, now))
215
+ continue;
216
+ const age = now.getTime() - Date.parse(row.minted_at);
217
+ if (Number.isFinite(age) && age > MAX_AGE_MS) {
218
+ removePendingRevoke(home, row.token);
219
+ continue;
220
+ }
221
+ const left = deadlineMs - (Date.now() - started);
222
+ if (left <= 0)
223
+ break;
224
+ if ((await revokeToken(row.origin, row.token, fetchImpl, left)) !== null)
225
+ removePendingRevoke(home, row.token);
226
+ }
227
+ return readPendingRevokes(home).length;
228
+ }
229
+ catch {
230
+ return readPendingRevokes(home).length;
231
+ }
232
+ }
233
+ //# sourceMappingURL=pending_revoke.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pending_revoke.js","sourceRoot":"","sources":["../src/pending_revoke.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * The pending-revoke queue's FILE: its path's format, and reading it (slice 2, S4). Split from `pending_revoke.ts` so that what
3
+ * `status` needs — a COUNT — does not drag the writer and the network revoke onto every process's load path.
4
+ *
5
+ * Read through `readSecretFile` (the same mode checks as `credentials.json`): a file or folder others can read, a file that is not
6
+ * what this client wrote, or one with a row it cannot read all come back `refused`, and `pending_revoke.ts` never overwrites those.
7
+ * Nothing here writes, sends or prints a token; the only thing it hands `status` is how many rows wait.
8
+ */
9
+ import { type SecretFileRead } from "./credential.js";
10
+ export interface PendingRevoke {
11
+ readonly token: string;
12
+ /** ISO 8601, when the server made the credential. */
13
+ readonly minted_at: string;
14
+ /** The issuing server's origin (`https://api.vibecommit.ai`): the only place a retry sends this token. */
15
+ readonly origin: string;
16
+ /**
17
+ * While `g` is holding the credential (waiting at its y/N) the row carries the holder's lease: a retry run by ANOTHER verb in the
18
+ * meantime must not revoke a credential that is about to be kept. A row with no lease is an orphan. See `leased`.
19
+ */
20
+ readonly lease: {
21
+ readonly pid: number;
22
+ readonly lease_until: string;
23
+ } | null;
24
+ }
25
+ /** How long a holder's lease lasts: a `g` whose question stays open longer may be revoked by another verb, and its commit then refuses. */
26
+ export declare const LEASE_MS: number;
27
+ /**
28
+ * Is the row leased at `now`? BOTH must hold: the holder's pid is alive (`kill(pid, 0)`: EPERM counts as alive, ESRCH as dead) and
29
+ * `now` is before the EARLIER of the row's own `lease_until` and `minted_at` + 30 minutes. The lease is capped at its mint: a
30
+ * `lease_until` written far in the future (a bug, or an edit) does not keep a live pid's row out of the retry for ever. A dead holder or
31
+ * an expired lease is an orphan, which any verb's retry revokes.
32
+ */
33
+ export declare function leased(row: PendingRevoke, now: Date): boolean;
34
+ export type Queue = {
35
+ readonly kind: "ok";
36
+ readonly rows: PendingRevoke[];
37
+ } | {
38
+ readonly kind: "absent";
39
+ }
40
+ /** Present but not safe to read or write: never read, never overwritten. */
41
+ | {
42
+ readonly kind: "refused";
43
+ readonly read: Exclude<SecretFileRead, {
44
+ kind: "ok";
45
+ } | {
46
+ kind: "absent";
47
+ }> | {
48
+ readonly kind: "unreadable";
49
+ };
50
+ };
51
+ export declare function readQueue(home: string): Queue;
52
+ /** The rows waiting, for a count. An absent, insecure or unreadable file reads as none (it is never sent from). */
53
+ export declare function readPendingRevokes(home: string): PendingRevoke[];
54
+ /** How many rows wait: all `status` shows of the queue. */
55
+ export declare function countPendingRevokes(home: string): number;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * The pending-revoke queue's FILE: its path's format, and reading it (slice 2, S4). Split from `pending_revoke.ts` so that what
3
+ * `status` needs — a COUNT — does not drag the writer and the network revoke onto every process's load path.
4
+ *
5
+ * Read through `readSecretFile` (the same mode checks as `credentials.json`): a file or folder others can read, a file that is not
6
+ * what this client wrote, or one with a row it cannot read all come back `refused`, and `pending_revoke.ts` never overwrites those.
7
+ * Nothing here writes, sends or prints a token; the only thing it hands `status` is how many rows wait.
8
+ */
9
+ import { INGEST_TOKEN_PREFIX, readSecretFile } from "./credential.js";
10
+ import { pendingRevokePath } from "./paths.js";
11
+ /** How long a holder's lease lasts: a `g` whose question stays open longer may be revoked by another verb, and its commit then refuses. */
12
+ export const LEASE_MS = 30 * 60 * 1000;
13
+ /**
14
+ * Is the row leased at `now`? BOTH must hold: the holder's pid is alive (`kill(pid, 0)`: EPERM counts as alive, ESRCH as dead) and
15
+ * `now` is before the EARLIER of the row's own `lease_until` and `minted_at` + 30 minutes. The lease is capped at its mint: a
16
+ * `lease_until` written far in the future (a bug, or an edit) does not keep a live pid's row out of the retry for ever. A dead holder or
17
+ * an expired lease is an orphan, which any verb's retry revokes.
18
+ */
19
+ export function leased(row, now) {
20
+ if (row.lease === null)
21
+ return false;
22
+ const until = Math.min(Date.parse(row.lease.lease_until), Date.parse(row.minted_at) + LEASE_MS);
23
+ if (!(until > now.getTime()))
24
+ return false;
25
+ try {
26
+ process.kill(row.lease.pid, 0);
27
+ return true;
28
+ }
29
+ catch (error) {
30
+ return error.code === "EPERM";
31
+ }
32
+ }
33
+ export function readQueue(home) {
34
+ const read = readSecretFile(home, pendingRevokePath(home));
35
+ if (read.kind === "absent")
36
+ return { kind: "absent" };
37
+ if (read.kind !== "ok")
38
+ return { kind: "refused", read };
39
+ try {
40
+ const doc = JSON.parse(read.raw);
41
+ if (doc === null || typeof doc !== "object" || !Array.isArray(doc.revokes))
42
+ return { kind: "refused", read: { kind: "unreadable" } };
43
+ const rows = [];
44
+ for (const row of doc.revokes) {
45
+ if (row === null || typeof row !== "object")
46
+ return { kind: "refused", read: { kind: "unreadable" } };
47
+ const r = row;
48
+ if (typeof r.token !== "string" || !r.token.startsWith(INGEST_TOKEN_PREFIX) || typeof r.minted_at !== "string" || typeof r.origin !== "string") {
49
+ // A row this client cannot read is a row it must not lose: refuse the whole file rather than rewrite it without the row.
50
+ return { kind: "refused", read: { kind: "unreadable" } };
51
+ }
52
+ // A lease this client cannot read is NO lease: the row is an orphan, which errs toward revoking.
53
+ const l = r.lease;
54
+ const lease = l !== null && l !== undefined && typeof l === "object" && typeof l.pid === "number" && Number.isInteger(l.pid) && typeof l.lease_until === "string"
55
+ ? { pid: l.pid, lease_until: l.lease_until }
56
+ : null;
57
+ rows.push({ token: r.token, minted_at: r.minted_at, origin: r.origin, lease });
58
+ }
59
+ return { kind: "ok", rows };
60
+ }
61
+ catch {
62
+ return { kind: "refused", read: { kind: "unreadable" } };
63
+ }
64
+ }
65
+ /** The rows waiting, for a count. An absent, insecure or unreadable file reads as none (it is never sent from). */
66
+ export function readPendingRevokes(home) {
67
+ const queue = readQueue(home);
68
+ return queue.kind === "ok" ? queue.rows : [];
69
+ }
70
+ /** How many rows wait: all `status` shows of the queue. */
71
+ export function countPendingRevokes(home) {
72
+ return readPendingRevokes(home).length;
73
+ }
74
+ //# sourceMappingURL=pending_revoke_file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pending_revoke_file.js","sourceRoot":"","sources":["../src/pending_revoke_file.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * C70: ONE resolver for every path judgement.
3
+ *
4
+ * JavaScript's `realpathSync` runs `path.resolve` FIRST, which collapses `..` lexically: `A/shared/../secret.env`, with `A/shared` a link to `/repos/B/pkg`, is judged
5
+ * `A/secret.env`, while the kernel (and `realpathSync.native`) open `/repos/B/secret.env`. A call that runs in, or writes into, another repository through a link
6
+ * and a `..` was judged as this one. `physicalPath` walks the RAW components forward with a physical cursor, as the kernel does: `.` and empty components are
7
+ * skipped, `..` is the parent of the PHYSICAL cursor, and a component that is a link is replaced by its fully resolved target. Nothing is normalised before it.
8
+ *
9
+ * Fail closed: a component that exists but cannot be resolved (a dangling link, a loop, an unreadable folder) makes the whole path `null`.
10
+ */
11
+ /** The longest path, in bytes, that is resolved at all: at least the longest the kernel accepts (Linux 4,095 bytes, macOS 1,024), so nothing longer could have been opened. */
12
+ export declare const MAX_PATH_BYTES = 4096;
13
+ /** C74 (3): is this path (as a record wrote it) longer than any path the kernel could have opened? Such a path is unresolvable AT PLACEMENT: it is never stored. */
14
+ export declare function pathTooLong(path: string): boolean;
15
+ /**
16
+ * `base` + `relative`, the way the kernel reads them: a trailing separator is not doubled and `.` / empty components are dropped, but `..` is KEPT (it is only
17
+ * meaningful after the links before it are resolved, which is `physicalPath`'s job).
18
+ */
19
+ export declare function joinRaw(base: string, relative: string): string;
20
+ /** Where a walk ended: `path` is the physical cursor (with the `depth` components below the last place that exists appended, lexically). */
21
+ export interface PhysicalWalk {
22
+ readonly path: string;
23
+ /** How many trailing components of `path` do not exist (0: the whole path exists). Not counted: `..` that came back up through them. */
24
+ readonly depth: number;
25
+ }
26
+ /**
27
+ * The walk behind `physicalPath`. The kernel's own: a component that is a link is replaced, in place, by the components of its target (a relative target is read from
28
+ * the link's own physical parent, an absolute one from the root), so a `..` inside a target is also taken after the links before it. Only `lstat` and `readlink`
29
+ * are used. A link whose target is missing is dangling: `null` (it exists, so it is never "continued past"); so is a loop, a path longer than MAX_PATH_BYTES, and a
30
+ * component `lstat` cannot read (EACCES, ENAMETOOLONG, EIO): the hook cannot tell whether it is a link, so a `..` after it is the same case as a `..` after a link, and
31
+ * when the physical answer cannot be known the rule fails closed (C49 (8)). C72 (1): Claude's `isInside` therefore equals 0.8.0 on every path with no `..` after a link
32
+ * and no `..` after a component that cannot be read; on those paths it WITHHOLDS (over-withholding, never a leak).
33
+ *
34
+ * BELOW the last place that exists nothing can be a link and nothing needs a syscall: a missing component sets `depth` to 1, a name there is appended (depth + 1) and a
35
+ * `..` there takes the parent (depth - 1), all lexically; once `depth` is back at 0 the walk looks at the file system again. So a path with thousands of missing
36
+ * components costs no more than its existing part.
37
+ */
38
+ export declare function physicalWalk(raw: string): PhysicalWalk | null;
39
+ /**
40
+ * The physical place `raw` names. `missing`: `"extend"` (default) keeps a path whose last components are not there (a file that is to be created; the caller
41
+ * works up to the nearest place that exists); `"fail"`: the whole path must exist (`null` otherwise, as `realpathSync` threw). `..` is taken in both after the links
42
+ * before it. A relative `raw` is read from the working folder.
43
+ */
44
+ export declare function physicalPath(raw: string, missing?: "extend" | "fail"): string | null;
45
+ /** The nearest place that EXISTS at or above `raw`, physically; `null` for a dangling link or a loop, or when more than `maxMissing` components are missing. */
46
+ export declare function nearestExistingPhysical(raw: string, maxMissing?: number): string | null;
@@ -0,0 +1,151 @@
1
+ /**
2
+ * C70: ONE resolver for every path judgement.
3
+ *
4
+ * JavaScript's `realpathSync` runs `path.resolve` FIRST, which collapses `..` lexically: `A/shared/../secret.env`, with `A/shared` a link to `/repos/B/pkg`, is judged
5
+ * `A/secret.env`, while the kernel (and `realpathSync.native`) open `/repos/B/secret.env`. A call that runs in, or writes into, another repository through a link
6
+ * and a `..` was judged as this one. `physicalPath` walks the RAW components forward with a physical cursor, as the kernel does: `.` and empty components are
7
+ * skipped, `..` is the parent of the PHYSICAL cursor, and a component that is a link is replaced by its fully resolved target. Nothing is normalised before it.
8
+ *
9
+ * Fail closed: a component that exists but cannot be resolved (a dangling link, a loop, an unreadable folder) makes the whole path `null`.
10
+ */
11
+ import { lstatSync, readlinkSync } from "node:fs";
12
+ import { dirname, isAbsolute, parse, sep } from "node:path";
13
+ /** More components than any path the OS accepts (PATH_MAX is 4096): a longer one is not resolved. */
14
+ const MAX_COMPONENTS = 4096;
15
+ /** The longest path, in bytes, that is resolved at all: at least the longest the kernel accepts (Linux 4,095 bytes, macOS 1,024), so nothing longer could have been opened. */
16
+ export const MAX_PATH_BYTES = 4096;
17
+ /** C74 (3): is this path (as a record wrote it) longer than any path the kernel could have opened? Such a path is unresolvable AT PLACEMENT: it is never stored. */
18
+ export function pathTooLong(path) {
19
+ return Buffer.byteLength(path, "utf8") > MAX_PATH_BYTES;
20
+ }
21
+ /**
22
+ * `base` + `relative`, the way the kernel reads them: a trailing separator is not doubled and `.` / empty components are dropped, but `..` is KEPT (it is only
23
+ * meaningful after the links before it are resolved, which is `physicalPath`'s job).
24
+ */
25
+ export function joinRaw(base, relative) {
26
+ const parts = relative.split("/").filter((part) => part !== "" && part !== ".");
27
+ if (parts.length === 0)
28
+ return base;
29
+ return `${base.endsWith(sep) ? base : base + sep}${parts.join(sep)}`;
30
+ }
31
+ /** The kernel's own limit on links followed while resolving one path (MAXSYMLINKS is 32 on Linux, 40 elsewhere). */
32
+ const MAX_LINKS = 40;
33
+ /**
34
+ * The walk behind `physicalPath`. The kernel's own: a component that is a link is replaced, in place, by the components of its target (a relative target is read from
35
+ * the link's own physical parent, an absolute one from the root), so a `..` inside a target is also taken after the links before it. Only `lstat` and `readlink`
36
+ * are used. A link whose target is missing is dangling: `null` (it exists, so it is never "continued past"); so is a loop, a path longer than MAX_PATH_BYTES, and a
37
+ * component `lstat` cannot read (EACCES, ENAMETOOLONG, EIO): the hook cannot tell whether it is a link, so a `..` after it is the same case as a `..` after a link, and
38
+ * when the physical answer cannot be known the rule fails closed (C49 (8)). C72 (1): Claude's `isInside` therefore equals 0.8.0 on every path with no `..` after a link
39
+ * and no `..` after a component that cannot be read; on those paths it WITHHOLDS (over-withholding, never a leak).
40
+ *
41
+ * BELOW the last place that exists nothing can be a link and nothing needs a syscall: a missing component sets `depth` to 1, a name there is appended (depth + 1) and a
42
+ * `..` there takes the parent (depth - 1), all lexically; once `depth` is back at 0 the walk looks at the file system again. So a path with thousands of missing
43
+ * components costs no more than its existing part.
44
+ */
45
+ export function physicalWalk(raw) {
46
+ // C72 (2): a bound at least as large as the longest path the kernel accepts (Linux PATH_MAX counts the NUL, so 4,095 bytes; macOS 1,024): a longer path is refused with
47
+ // ENAMETOOLONG before any lookup, so nothing could have been opened through it. Checked before any split, so one crafted line cannot cost more than 4,096 bytes of
48
+ // walking (below a missing component the walk is a growing string). The absolute half rests on the agents handing the kernel the cwd-joined form (Codex joins first,
49
+ // mirrored by codex_folders.ts `joinRaw`; Claude's paths are absolute): a tool that passed a long relative string itself would be over-withheld, never leaked.
50
+ if (pathTooLong(raw))
51
+ return null;
52
+ const absolute = isAbsolute(raw) ? raw : joinRaw(process.cwd(), raw);
53
+ if (pathTooLong(absolute))
54
+ return null;
55
+ const root = parse(absolute).root;
56
+ const pending = [];
57
+ const parts = absolute.slice(root.length).split(sep);
58
+ if (parts.length > MAX_COMPONENTS)
59
+ return null;
60
+ for (let i = parts.length - 1; i >= 0; i -= 1)
61
+ pending.push({ part: parts[i], viaLink: false });
62
+ let current = root;
63
+ let depth = 0;
64
+ let links = 0;
65
+ let steps = 0;
66
+ for (let item = pending.pop(); item !== undefined; item = pending.pop()) {
67
+ steps += 1;
68
+ if (steps > 4 * MAX_COMPONENTS)
69
+ return null;
70
+ const { part, viaLink } = item;
71
+ if (part === "" || part === ".")
72
+ continue;
73
+ if (part === "..") {
74
+ current = dirname(current);
75
+ if (depth > 0)
76
+ depth -= 1;
77
+ continue;
78
+ }
79
+ const next = current.endsWith(sep) ? current + part : current + sep + part;
80
+ if (depth > 0) {
81
+ current = next;
82
+ depth += 1;
83
+ continue;
84
+ }
85
+ let isLink;
86
+ try {
87
+ isLink = lstatSync(next).isSymbolicLink();
88
+ }
89
+ catch (error) {
90
+ const code = error.code;
91
+ if (code !== "ENOENT" && code !== "ENOTDIR")
92
+ return null;
93
+ // A component the target of a link names that is not there makes the link dangling: it exists, so it is never continued past.
94
+ if (viaLink)
95
+ return null;
96
+ current = next;
97
+ depth = 1;
98
+ continue;
99
+ }
100
+ if (!isLink) {
101
+ current = next;
102
+ continue;
103
+ }
104
+ links += 1;
105
+ if (links > MAX_LINKS)
106
+ return null;
107
+ let target;
108
+ try {
109
+ target = readlinkSync(next);
110
+ }
111
+ catch {
112
+ return null;
113
+ }
114
+ if (target === "")
115
+ return null;
116
+ let rest = target;
117
+ if (isAbsolute(target)) {
118
+ const targetRoot = parse(target).root;
119
+ current = targetRoot;
120
+ rest = target.slice(targetRoot.length);
121
+ }
122
+ const targetParts = rest.split(sep);
123
+ if (pending.length + targetParts.length > 4 * MAX_COMPONENTS)
124
+ return null;
125
+ for (let i = targetParts.length - 1; i >= 0; i -= 1)
126
+ pending.push({ part: targetParts[i], viaLink: true });
127
+ }
128
+ return { path: current, depth };
129
+ }
130
+ /**
131
+ * The physical place `raw` names. `missing`: `"extend"` (default) keeps a path whose last components are not there (a file that is to be created; the caller
132
+ * works up to the nearest place that exists); `"fail"`: the whole path must exist (`null` otherwise, as `realpathSync` threw). `..` is taken in both after the links
133
+ * before it. A relative `raw` is read from the working folder.
134
+ */
135
+ export function physicalPath(raw, missing = "extend") {
136
+ const walk = physicalWalk(raw);
137
+ if (walk === null)
138
+ return null;
139
+ return missing === "fail" && walk.depth > 0 ? null : walk.path;
140
+ }
141
+ /** The nearest place that EXISTS at or above `raw`, physically; `null` for a dangling link or a loop, or when more than `maxMissing` components are missing. */
142
+ export function nearestExistingPhysical(raw, maxMissing = 64) {
143
+ const walk = physicalWalk(raw);
144
+ if (walk === null || walk.depth > maxMissing)
145
+ return null;
146
+ let current = walk.path;
147
+ for (let i = 0; i < walk.depth; i += 1)
148
+ current = dirname(current);
149
+ return current;
150
+ }
151
+ //# sourceMappingURL=physical_path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"physical_path.js","sourceRoot":"","sources":["../src/physical_path.ts"],"names":[],"mappings":"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"}