@primitive.ai/prim 0.1.0-alpha.90 → 0.1.0-alpha.91
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 +20 -12
- package/SKILL.md +6 -6
- package/dist/{chunk-LMRYZL3D.js → chunk-5AFRMOPX.js} +1 -1
- package/dist/{chunk-RO3F6JYV.js → chunk-5GYQ4J3D.js} +2 -2
- package/dist/{chunk-3EEFL6NJ.js → chunk-67VNLLBN.js} +261 -1
- package/dist/{chunk-LEMX3IOG.js → chunk-BTNNEH2W.js} +1 -1
- package/dist/chunk-EWZMDD44.js +1474 -0
- package/dist/{chunk-YWO7O6QG.js → chunk-FBAYCS3X.js} +2 -2
- package/dist/{chunk-R26A7TWY.js → chunk-FKXFUUWJ.js} +1 -1
- package/dist/{chunk-C7T7B45R.js → chunk-FOLKHXLJ.js} +150 -21
- package/dist/chunk-H4UNYFUC.js +196 -0
- package/dist/chunk-HU4PW6I6.js +264 -0
- package/dist/{chunk-CUE2LKLR.js → chunk-KFXF2V5G.js} +1 -1
- package/dist/{chunk-6IWNH776.js → chunk-LFFLHSQO.js} +4 -3
- package/dist/{chunk-MAZWKJGL.js → chunk-M6RIJVRE.js} +42 -232
- package/dist/{chunk-2JRX7ZR4.js → chunk-NAHHFSLH.js} +16 -5
- package/dist/{chunk-FI3LIRCB.js → chunk-OAAP2MY6.js} +2 -2
- package/dist/{chunk-Z7YPFWAZ.js → chunk-PCVGBE5B.js} +10 -268
- package/dist/{chunk-UZPBB7V4.js → chunk-VW3FDYRT.js} +60 -48
- package/dist/chunk-WX52U7Z2.js +112 -0
- package/dist/{chunk-PPQYQEIR.js → chunk-ZB4F75SZ.js} +28 -0
- package/dist/daemon/server.js +20 -20
- package/dist/hook-runtime/daemon/server.js +20 -20
- package/dist/hook-runtime/hooks/post-commit.js +20 -20
- package/dist/hook-runtime/hooks/post-rewrite.js +20 -20
- package/dist/hook-runtime/hooks/post-tool-use.js +651 -132
- package/dist/hook-runtime/hooks/pre-tool-use.js +644 -96
- package/dist/hook-runtime/hooks/prim-hook.js +612 -96
- package/dist/hook-runtime/hooks/session-end.js +610 -27
- package/dist/hook-runtime/hooks/session-start.js +1132 -533
- package/dist/hook-runtime/index.js +735 -264
- package/dist/hook-runtime/statusline-main.js +48 -4
- package/dist/hooks/post-commit.js +11 -9
- package/dist/hooks/post-rewrite.js +11 -9
- package/dist/hooks/post-tool-use.js +73 -48
- package/dist/hooks/pre-commit.js +4 -4
- package/dist/hooks/pre-tool-use.js +46 -44
- package/dist/hooks/prim-hook.js +47 -25
- package/dist/hooks/session-end.js +15 -26
- package/dist/hooks/session-start.js +59 -32
- package/dist/index.js +558 -1436
- package/dist/statusline-main.js +48 -4
- package/package.json +1 -1
- package/dist/chunk-PMRDE2DA.js +0 -310
- package/dist/chunk-WB7AUZ4C.js +0 -52
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
} from "./chunk-EZXEWFDG.js";
|
|
4
4
|
import {
|
|
5
5
|
scrubEnvironmentPaths
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-OAAP2MY6.js";
|
|
7
7
|
import {
|
|
8
8
|
isValidRepoSyncId
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-M6RIJVRE.js";
|
|
10
10
|
|
|
11
11
|
// src/hooks/prim-hook-core.ts
|
|
12
12
|
import { createHash, randomUUID } from "crypto";
|
|
@@ -45,6 +45,17 @@ function toolOutcomeFor(parsed, agent) {
|
|
|
45
45
|
if (agent === "codex") {
|
|
46
46
|
return event === "PostToolUse" ? "returned" : void 0;
|
|
47
47
|
}
|
|
48
|
+
if (agent === "cursor") {
|
|
49
|
+
if (event === "PostToolUse") return "succeeded";
|
|
50
|
+
if (event === "PostToolUseFailure") {
|
|
51
|
+
if (parsed.is_interrupt === true) return "interrupted";
|
|
52
|
+
const failureType = lowerString(parsed.failure_type);
|
|
53
|
+
if (failureType === "permission_denied") return "prevented";
|
|
54
|
+
if (failureType === "error" || failureType === "timeout") return "failed";
|
|
55
|
+
return "unknown";
|
|
56
|
+
}
|
|
57
|
+
return void 0;
|
|
58
|
+
}
|
|
48
59
|
const extra = parsed.extra;
|
|
49
60
|
if (event === "post_approval_response") {
|
|
50
61
|
const choice = lowerString(extra?.choice);
|
|
@@ -75,7 +86,7 @@ function toMove(parsed, cliVersion, agent = "claude_code", workspaceId, reposito
|
|
|
75
86
|
let moveId;
|
|
76
87
|
if (CORRELATED_TOOL_EVENTS.has(eventType) && sessionId && invocationId) {
|
|
77
88
|
moveId = `posttool:v1:${createHash("sha256").update(JSON.stringify([agent, sessionId, eventType, invocationId])).digest("hex")}`;
|
|
78
|
-
} else if (CORRELATED_TOOL_EVENTS.has(eventType) && sessionId) {
|
|
89
|
+
} else if (CORRELATED_TOOL_EVENTS.has(eventType) && sessionId && agent !== "cursor") {
|
|
79
90
|
moveId = `posttool:fallback:v1:${createHash("sha256").update(
|
|
80
91
|
JSON.stringify([
|
|
81
92
|
agent,
|
|
@@ -231,8 +242,8 @@ function toRewriteMove(rewrite, cliVersion, cwd, context) {
|
|
|
231
242
|
}
|
|
232
243
|
return moves;
|
|
233
244
|
}
|
|
234
|
-
function shouldFlushAfter(eventType) {
|
|
235
|
-
return eventType === "SessionEnd";
|
|
245
|
+
function shouldFlushAfter(eventType, agent = "claude_code") {
|
|
246
|
+
return eventType === "SessionEnd" || agent === "cursor" && eventType === "Stop";
|
|
236
247
|
}
|
|
237
248
|
|
|
238
249
|
export {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
boundedHealthError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-BTNNEH2W.js";
|
|
4
4
|
import {
|
|
5
5
|
terminalSafeLine
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-PCVGBE5B.js";
|
|
7
7
|
|
|
8
8
|
// src/hooks/redact.ts
|
|
9
9
|
import { existsSync, readFileSync, statSync } from "fs";
|
|
@@ -10,268 +10,16 @@ import {
|
|
|
10
10
|
TOKEN_EXPIRES_PATH,
|
|
11
11
|
TOKEN_FILE_PATH,
|
|
12
12
|
WORKOS_CONNECT_RESPONSE_MAX_BYTES,
|
|
13
|
+
atomicWriteFile,
|
|
13
14
|
jwtExpiresAt,
|
|
14
15
|
parseWorkosConnectTokens,
|
|
15
16
|
readBoundedJson,
|
|
16
17
|
readCredentialMigrationState,
|
|
17
18
|
readStoredCredentialMetadata,
|
|
18
19
|
resolveAuthCredential,
|
|
20
|
+
withFileLock,
|
|
19
21
|
workosConnectTokenEndpoint
|
|
20
|
-
} from "./chunk-
|
|
21
|
-
|
|
22
|
-
// src/lib/atomic-file.ts
|
|
23
|
-
import { randomUUID } from "crypto";
|
|
24
|
-
import {
|
|
25
|
-
constants,
|
|
26
|
-
closeSync,
|
|
27
|
-
fchmodSync,
|
|
28
|
-
fsyncSync,
|
|
29
|
-
mkdirSync,
|
|
30
|
-
openSync,
|
|
31
|
-
renameSync,
|
|
32
|
-
rmSync,
|
|
33
|
-
writeFileSync
|
|
34
|
-
} from "fs";
|
|
35
|
-
import { dirname } from "path";
|
|
36
|
-
var DEFAULT_FILE_MODE = 438;
|
|
37
|
-
function syncDirectory(path) {
|
|
38
|
-
const fd = openSync(path, constants.O_RDONLY);
|
|
39
|
-
try {
|
|
40
|
-
fsyncSync(fd);
|
|
41
|
-
} finally {
|
|
42
|
-
closeSync(fd);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
function syncFile(path) {
|
|
46
|
-
const fd = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
47
|
-
try {
|
|
48
|
-
fsyncSync(fd);
|
|
49
|
-
} finally {
|
|
50
|
-
closeSync(fd);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
function syncCreatedParentChain(firstCreatedParent, parent) {
|
|
54
|
-
const createdDirectories = [];
|
|
55
|
-
let current = parent;
|
|
56
|
-
while (current !== firstCreatedParent) {
|
|
57
|
-
createdDirectories.push(current);
|
|
58
|
-
const next = dirname(current);
|
|
59
|
-
if (next === current) {
|
|
60
|
-
throw new Error("recursive mkdir returned a path outside the target parent chain");
|
|
61
|
-
}
|
|
62
|
-
current = next;
|
|
63
|
-
}
|
|
64
|
-
createdDirectories.push(firstCreatedParent);
|
|
65
|
-
syncDirectory(dirname(firstCreatedParent));
|
|
66
|
-
for (const directory of createdDirectories.reverse().slice(0, -1)) {
|
|
67
|
-
syncDirectory(directory);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
function ensureDurableDirectory(path, mode) {
|
|
71
|
-
const firstCreatedParent = mkdirSync(path, { recursive: true, mode });
|
|
72
|
-
if (firstCreatedParent) {
|
|
73
|
-
syncCreatedParentChain(firstCreatedParent, path);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
function atomicWriteFile(target, content, options = {}) {
|
|
77
|
-
const parent = dirname(target);
|
|
78
|
-
if (options.ensureParent) {
|
|
79
|
-
ensureDurableDirectory(parent);
|
|
80
|
-
}
|
|
81
|
-
const temporaryPath = `${target}.${randomUUID()}.tmp`;
|
|
82
|
-
let temporaryCreated = false;
|
|
83
|
-
try {
|
|
84
|
-
const flags = constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW;
|
|
85
|
-
const fd = openSync(temporaryPath, flags, options.mode ?? DEFAULT_FILE_MODE);
|
|
86
|
-
temporaryCreated = true;
|
|
87
|
-
try {
|
|
88
|
-
if (options.mode !== void 0) fchmodSync(fd, options.mode);
|
|
89
|
-
writeFileSync(fd, content);
|
|
90
|
-
fsyncSync(fd);
|
|
91
|
-
} finally {
|
|
92
|
-
closeSync(fd);
|
|
93
|
-
}
|
|
94
|
-
options.validate?.(temporaryPath);
|
|
95
|
-
renameSync(temporaryPath, target);
|
|
96
|
-
syncDirectory(parent);
|
|
97
|
-
} catch (error) {
|
|
98
|
-
if (temporaryCreated) {
|
|
99
|
-
try {
|
|
100
|
-
rmSync(temporaryPath, { force: true });
|
|
101
|
-
} catch {
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
throw error;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// src/lib/process-liveness.ts
|
|
109
|
-
function processIsAlive(pid, probe = process.kill) {
|
|
110
|
-
if (!Number.isSafeInteger(pid) || pid <= 0) return false;
|
|
111
|
-
try {
|
|
112
|
-
probe(pid, 0);
|
|
113
|
-
return true;
|
|
114
|
-
} catch (error) {
|
|
115
|
-
return error.code === "EPERM";
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// src/lib/file-lock.ts
|
|
120
|
-
import { randomBytes } from "crypto";
|
|
121
|
-
import { existsSync, mkdirSync as mkdirSync2, readFileSync, rmSync as rmSync2, statSync, writeFileSync as writeFileSync2 } from "fs";
|
|
122
|
-
import { dirname as dirname2, join } from "path";
|
|
123
|
-
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
124
|
-
var DEFAULT_POLL_MS = 50;
|
|
125
|
-
var DEFAULT_INIT_GRACE_MS = 2e3;
|
|
126
|
-
var DIRECTORY_MODE = 448;
|
|
127
|
-
var FILE_MODE = 384;
|
|
128
|
-
function ownerPath(lockDir) {
|
|
129
|
-
return join(lockDir, "owner.json");
|
|
130
|
-
}
|
|
131
|
-
function readOwner(lockDir) {
|
|
132
|
-
try {
|
|
133
|
-
const owner = JSON.parse(readFileSync(ownerPath(lockDir), "utf8"));
|
|
134
|
-
if (!Number.isInteger(owner.pid) || owner.pid <= 0 || typeof owner.nonce !== "string" || typeof owner.createdAt !== "number") {
|
|
135
|
-
return null;
|
|
136
|
-
}
|
|
137
|
-
return owner;
|
|
138
|
-
} catch {
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
function sameOwner(a, b) {
|
|
143
|
-
return a?.pid === b.pid && a.nonce === b.nonce && a.createdAt === b.createdAt;
|
|
144
|
-
}
|
|
145
|
-
function tryTakeLock(lockDir, now) {
|
|
146
|
-
try {
|
|
147
|
-
mkdirSync2(lockDir, { mode: DIRECTORY_MODE });
|
|
148
|
-
} catch (error) {
|
|
149
|
-
if (error.code === "EEXIST") return null;
|
|
150
|
-
throw error;
|
|
151
|
-
}
|
|
152
|
-
const owner = {
|
|
153
|
-
pid: process.pid,
|
|
154
|
-
nonce: randomBytes(16).toString("hex"),
|
|
155
|
-
createdAt: now
|
|
156
|
-
};
|
|
157
|
-
try {
|
|
158
|
-
writeFileSync2(ownerPath(lockDir), `${JSON.stringify(owner)}
|
|
159
|
-
`, {
|
|
160
|
-
encoding: "utf8",
|
|
161
|
-
mode: FILE_MODE,
|
|
162
|
-
flag: "wx"
|
|
163
|
-
});
|
|
164
|
-
return owner;
|
|
165
|
-
} catch (error) {
|
|
166
|
-
rmSync2(lockDir, { recursive: true, force: true });
|
|
167
|
-
throw error;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
function recoverStaleLock(lockDir, now, alive, initGraceMs) {
|
|
171
|
-
const owner = readOwner(lockDir);
|
|
172
|
-
if (owner) {
|
|
173
|
-
if (alive(owner.pid)) return false;
|
|
174
|
-
if (!sameOwner(readOwner(lockDir), owner)) return false;
|
|
175
|
-
rmSync2(lockDir, { recursive: true, force: true });
|
|
176
|
-
return true;
|
|
177
|
-
}
|
|
178
|
-
try {
|
|
179
|
-
const before = statSync(lockDir).mtimeMs;
|
|
180
|
-
if (now - before < initGraceMs) return false;
|
|
181
|
-
if (readOwner(lockDir)) return false;
|
|
182
|
-
if (statSync(lockDir).mtimeMs !== before) return false;
|
|
183
|
-
rmSync2(lockDir, { recursive: true, force: true });
|
|
184
|
-
return true;
|
|
185
|
-
} catch {
|
|
186
|
-
return true;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
function abortError(signal) {
|
|
190
|
-
if (signal.reason instanceof Error) return signal.reason;
|
|
191
|
-
const error = new Error("The operation was aborted");
|
|
192
|
-
error.name = "AbortError";
|
|
193
|
-
return error;
|
|
194
|
-
}
|
|
195
|
-
function throwIfAborted(signal) {
|
|
196
|
-
if (signal?.aborted) throw abortError(signal);
|
|
197
|
-
}
|
|
198
|
-
async function defaultSleep(ms, signal) {
|
|
199
|
-
if (!signal) {
|
|
200
|
-
await new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
throwIfAborted(signal);
|
|
204
|
-
await new Promise((resolve2, reject) => {
|
|
205
|
-
const timer = setTimeout(() => {
|
|
206
|
-
signal.removeEventListener("abort", onAbort);
|
|
207
|
-
resolve2();
|
|
208
|
-
}, ms);
|
|
209
|
-
const onAbort = () => {
|
|
210
|
-
clearTimeout(timer);
|
|
211
|
-
reject(abortError(signal));
|
|
212
|
-
};
|
|
213
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
var syncSleeper = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
|
|
217
|
-
function defaultSleepSync(ms) {
|
|
218
|
-
Atomics.wait(syncSleeper, 0, 0, ms);
|
|
219
|
-
}
|
|
220
|
-
function withFileLockSync(lockDir, operation, options = {}) {
|
|
221
|
-
mkdirSync2(dirname2(lockDir), { recursive: true, mode: DIRECTORY_MODE });
|
|
222
|
-
const nowMs = options.nowMs ?? Date.now;
|
|
223
|
-
const alive = options.processAlive ?? processIsAlive;
|
|
224
|
-
const deadline = nowMs() + (options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
225
|
-
let owner = null;
|
|
226
|
-
while (!owner) {
|
|
227
|
-
const now = nowMs();
|
|
228
|
-
owner = tryTakeLock(lockDir, now);
|
|
229
|
-
if (owner) break;
|
|
230
|
-
recoverStaleLock(lockDir, now, alive, options.initGraceMs ?? DEFAULT_INIT_GRACE_MS);
|
|
231
|
-
if (now >= deadline) {
|
|
232
|
-
throw new Error(`timed out waiting for file lock ${lockDir}`);
|
|
233
|
-
}
|
|
234
|
-
(options.sleep ?? defaultSleepSync)(options.pollMs ?? DEFAULT_POLL_MS);
|
|
235
|
-
}
|
|
236
|
-
try {
|
|
237
|
-
return operation();
|
|
238
|
-
} finally {
|
|
239
|
-
if (sameOwner(readOwner(lockDir), owner)) {
|
|
240
|
-
rmSync2(lockDir, { recursive: true, force: true });
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
async function withFileLock(lockDir, operation, options = {}) {
|
|
245
|
-
mkdirSync2(dirname2(lockDir), { recursive: true, mode: DIRECTORY_MODE });
|
|
246
|
-
const nowMs = options.nowMs ?? Date.now;
|
|
247
|
-
const alive = options.processAlive ?? processIsAlive;
|
|
248
|
-
const deadline = nowMs() + (options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
249
|
-
let owner = null;
|
|
250
|
-
while (!owner) {
|
|
251
|
-
throwIfAborted(options.signal);
|
|
252
|
-
const now = nowMs();
|
|
253
|
-
owner = tryTakeLock(lockDir, now);
|
|
254
|
-
if (owner) break;
|
|
255
|
-
recoverStaleLock(lockDir, now, alive, options.initGraceMs ?? DEFAULT_INIT_GRACE_MS);
|
|
256
|
-
if (now >= deadline) {
|
|
257
|
-
throw new Error(`timed out waiting for file lock ${lockDir}`);
|
|
258
|
-
}
|
|
259
|
-
if (options.sleep) {
|
|
260
|
-
await options.sleep(options.pollMs ?? DEFAULT_POLL_MS);
|
|
261
|
-
throwIfAborted(options.signal);
|
|
262
|
-
} else {
|
|
263
|
-
await defaultSleep(options.pollMs ?? DEFAULT_POLL_MS, options.signal);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
try {
|
|
267
|
-
throwIfAborted(options.signal);
|
|
268
|
-
return await operation();
|
|
269
|
-
} finally {
|
|
270
|
-
if (sameOwner(readOwner(lockDir), owner)) {
|
|
271
|
-
rmSync2(lockDir, { recursive: true, force: true });
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
22
|
+
} from "./chunk-67VNLLBN.js";
|
|
275
23
|
|
|
276
24
|
// src/lib/terminal-safe.ts
|
|
277
25
|
function replaceIsolatedSurrogates(value) {
|
|
@@ -320,8 +68,8 @@ var stripControlChars = terminalSafeText;
|
|
|
320
68
|
|
|
321
69
|
// src/client.ts
|
|
322
70
|
import { createHash } from "crypto";
|
|
323
|
-
import { chmodSync, existsSync
|
|
324
|
-
import { dirname
|
|
71
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync } from "fs";
|
|
72
|
+
import { dirname } from "path";
|
|
325
73
|
var CONFIG_DIR_MODE = 448;
|
|
326
74
|
var CREDENTIAL_FILE_MODE = 384;
|
|
327
75
|
var REFRESH_THRESHOLD_MS = 6e4;
|
|
@@ -329,7 +77,7 @@ var DEFAULT_API_URL = "https://api.getprimitive.ai";
|
|
|
329
77
|
var AUTH_EXPIRED_MESSAGE = "Authentication expired. Run `prim auth login` to re-authenticate.";
|
|
330
78
|
function readTrimmed(path) {
|
|
331
79
|
try {
|
|
332
|
-
const value =
|
|
80
|
+
const value = readFileSync(path, "utf8").trim();
|
|
333
81
|
return value || void 0;
|
|
334
82
|
} catch {
|
|
335
83
|
return void 0;
|
|
@@ -345,8 +93,8 @@ function expiresAtFor(token, expiresIn) {
|
|
|
345
93
|
return expiresIn === void 0 ? jwtExpiresAt(token) : Date.now() + expiresIn * 1e3;
|
|
346
94
|
}
|
|
347
95
|
function ensureConfigDirectory() {
|
|
348
|
-
const directory =
|
|
349
|
-
|
|
96
|
+
const directory = dirname(TOKEN_FILE_PATH);
|
|
97
|
+
mkdirSync(directory, { recursive: true, mode: CONFIG_DIR_MODE });
|
|
350
98
|
chmodSync(directory, CONFIG_DIR_MODE);
|
|
351
99
|
}
|
|
352
100
|
function atomicWrite(path, content) {
|
|
@@ -354,8 +102,8 @@ function atomicWrite(path, content) {
|
|
|
354
102
|
atomicWriteFile(path, content, { mode: CREDENTIAL_FILE_MODE });
|
|
355
103
|
}
|
|
356
104
|
function removeCredentialFile(path) {
|
|
357
|
-
if (!
|
|
358
|
-
|
|
105
|
+
if (!existsSync(path)) return false;
|
|
106
|
+
rmSync(path, { force: true });
|
|
359
107
|
return true;
|
|
360
108
|
}
|
|
361
109
|
function getTokenExpiresAt() {
|
|
@@ -892,12 +640,6 @@ async function getPinnedManagementClient(options = {}) {
|
|
|
892
640
|
}
|
|
893
641
|
|
|
894
642
|
export {
|
|
895
|
-
syncDirectory,
|
|
896
|
-
syncFile,
|
|
897
|
-
atomicWriteFile,
|
|
898
|
-
processIsAlive,
|
|
899
|
-
withFileLockSync,
|
|
900
|
-
withFileLock,
|
|
901
643
|
terminalSafeText,
|
|
902
644
|
terminalSafeLine,
|
|
903
645
|
isTerminalSafeText,
|
|
@@ -1,26 +1,30 @@
|
|
|
1
1
|
import {
|
|
2
|
-
formatStatusline
|
|
3
|
-
|
|
4
|
-
} from "./chunk-PMRDE2DA.js";
|
|
2
|
+
formatStatusline
|
|
3
|
+
} from "./chunk-WX52U7Z2.js";
|
|
5
4
|
import {
|
|
6
5
|
decisionIngestionStatus,
|
|
7
|
-
gitToplevel,
|
|
8
6
|
repositoryBindingState
|
|
9
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-M6RIJVRE.js";
|
|
10
8
|
import {
|
|
11
9
|
getSiteUrl,
|
|
12
10
|
isSessionEnded,
|
|
13
|
-
terminalSafeText
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
terminalSafeText
|
|
12
|
+
} from "./chunk-PCVGBE5B.js";
|
|
13
|
+
import {
|
|
14
|
+
packageVersion
|
|
15
|
+
} from "./chunk-EWZMDD44.js";
|
|
16
|
+
import {
|
|
17
|
+
gitToplevel
|
|
18
|
+
} from "./chunk-H4UNYFUC.js";
|
|
16
19
|
import {
|
|
17
20
|
daemonPrincipalsMatch,
|
|
18
21
|
daemonRequest,
|
|
19
22
|
resolveDaemonPrincipal
|
|
20
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-KFXF2V5G.js";
|
|
21
24
|
import {
|
|
22
|
-
primConfigDirectory
|
|
23
|
-
|
|
25
|
+
primConfigDirectory,
|
|
26
|
+
withFileLock
|
|
27
|
+
} from "./chunk-67VNLLBN.js";
|
|
24
28
|
|
|
25
29
|
// src/hooks/codex-context.ts
|
|
26
30
|
import { createHash, randomBytes as randomBytes2 } from "crypto";
|
|
@@ -61,9 +65,7 @@ var CODEX_DRAFT_DIGEST_MAX_SEEN_IDS = 4096;
|
|
|
61
65
|
var CODEX_DIGEST_STATE_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
62
66
|
var CODEX_DIGEST_STATE_MAX_FILES = 256;
|
|
63
67
|
var STATE_VERSION = 1;
|
|
64
|
-
var STATE_DIRECTORY = ["codex", "decision-digests"];
|
|
65
68
|
var DRAFT_STATE_VERSION = 2;
|
|
66
|
-
var DRAFT_STATE_DIRECTORY = ["codex", "decision-draft-deliveries"];
|
|
67
69
|
var NO_CURSOR = -1;
|
|
68
70
|
function hasVisibleCodexMessage(output) {
|
|
69
71
|
if (typeof output !== "object" || output === null || Array.isArray(output)) return false;
|
|
@@ -95,11 +97,11 @@ function appendCodexContext(output, context) {
|
|
|
95
97
|
}
|
|
96
98
|
return next;
|
|
97
99
|
}
|
|
98
|
-
function stateRoot() {
|
|
99
|
-
return join(primConfigDirectory(),
|
|
100
|
+
function stateRoot(namespace) {
|
|
101
|
+
return join(primConfigDirectory(), namespace, "decision-digests");
|
|
100
102
|
}
|
|
101
|
-
function draftStateRoot() {
|
|
102
|
-
return join(primConfigDirectory(),
|
|
103
|
+
function draftStateRoot(namespace) {
|
|
104
|
+
return join(primConfigDirectory(), namespace, "decision-draft-deliveries");
|
|
103
105
|
}
|
|
104
106
|
function workspaceFor(cwd) {
|
|
105
107
|
try {
|
|
@@ -111,12 +113,12 @@ function workspaceFor(cwd) {
|
|
|
111
113
|
function statePath(args) {
|
|
112
114
|
const identity = `${args.siteUrl}\0${workspaceFor(args.cwd)}\0${args.sessionId}`;
|
|
113
115
|
const key = createHash("sha256").update(identity).digest("hex");
|
|
114
|
-
return join(stateRoot(), `${key}.json`);
|
|
116
|
+
return join(stateRoot(args.namespace), `${key}.json`);
|
|
115
117
|
}
|
|
116
118
|
function draftStatePath(args) {
|
|
117
119
|
const identity = `${args.siteUrl}\0${workspaceFor(args.cwd)}\0${args.sessionId}\0${args.principalId}\0${args.organizationId}\0${args.credentialFingerprint}`;
|
|
118
120
|
const key = createHash("sha256").update(identity).digest("hex");
|
|
119
|
-
return join(draftStateRoot(), `${key}.json`);
|
|
121
|
+
return join(draftStateRoot(args.namespace), `${key}.json`);
|
|
120
122
|
}
|
|
121
123
|
function parseState(value) {
|
|
122
124
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
@@ -307,14 +309,14 @@ function mergeState(latest, args) {
|
|
|
307
309
|
updatedAt: args.now
|
|
308
310
|
};
|
|
309
311
|
}
|
|
310
|
-
async function commitState(path, args) {
|
|
312
|
+
async function commitState(path, args, namespace) {
|
|
311
313
|
try {
|
|
312
314
|
await withFileLock(
|
|
313
315
|
`${path}.lock`,
|
|
314
316
|
() => {
|
|
315
317
|
const latest = readState(path);
|
|
316
|
-
writeStateFile(path, stateRoot(), mergeState(latest, args));
|
|
317
|
-
cleanupStateFiles(stateRoot(), args.now);
|
|
318
|
+
writeStateFile(path, stateRoot(namespace), mergeState(latest, args));
|
|
319
|
+
cleanupStateFiles(stateRoot(namespace), args.now);
|
|
318
320
|
},
|
|
319
321
|
{ timeoutMs: 50, pollMs: 10 }
|
|
320
322
|
);
|
|
@@ -336,7 +338,7 @@ function mergeDraftState(latest, args) {
|
|
|
336
338
|
updatedAt: args.now
|
|
337
339
|
};
|
|
338
340
|
}
|
|
339
|
-
async function commitDraftState(path, args) {
|
|
341
|
+
async function commitDraftState(path, args, namespace) {
|
|
340
342
|
try {
|
|
341
343
|
let next;
|
|
342
344
|
await withFileLock(
|
|
@@ -344,8 +346,8 @@ async function commitDraftState(path, args) {
|
|
|
344
346
|
() => {
|
|
345
347
|
const latest = readDraftState(path);
|
|
346
348
|
next = mergeDraftState(latest, args);
|
|
347
|
-
writeStateFile(path, draftStateRoot(), next);
|
|
348
|
-
cleanupStateFiles(draftStateRoot(), args.now);
|
|
349
|
+
writeStateFile(path, draftStateRoot(namespace), next);
|
|
350
|
+
cleanupStateFiles(draftStateRoot(namespace), args.now);
|
|
349
351
|
},
|
|
350
352
|
{ timeoutMs: 50, pollMs: 10 }
|
|
351
353
|
);
|
|
@@ -355,9 +357,10 @@ async function commitDraftState(path, args) {
|
|
|
355
357
|
}
|
|
356
358
|
}
|
|
357
359
|
async function prepareCodexContext(options) {
|
|
360
|
+
const namespace = options.namespace ?? "codex";
|
|
358
361
|
const siteUrl = getSiteUrl();
|
|
359
362
|
const workspace = workspaceFor(options.cwd);
|
|
360
|
-
const path = statePath({ cwd: options.cwd, sessionId: options.sessionId, siteUrl });
|
|
363
|
+
const path = statePath({ cwd: options.cwd, sessionId: options.sessionId, siteUrl, namespace });
|
|
361
364
|
const previous = readState(path);
|
|
362
365
|
const principal = resolveDaemonPrincipal();
|
|
363
366
|
const privatePath = principal ? draftStatePath({
|
|
@@ -366,7 +369,8 @@ async function prepareCodexContext(options) {
|
|
|
366
369
|
siteUrl,
|
|
367
370
|
principalId: principal.principalId,
|
|
368
371
|
organizationId: principal.organizationId,
|
|
369
|
-
credentialFingerprint: principal.credentialFingerprint
|
|
372
|
+
credentialFingerprint: principal.credentialFingerprint,
|
|
373
|
+
namespace
|
|
370
374
|
}) : void 0;
|
|
371
375
|
const previousDraft = privatePath ? readDraftState(privatePath) : void 0;
|
|
372
376
|
const startup = options.startup === true;
|
|
@@ -456,32 +460,40 @@ async function prepareCodexContext(options) {
|
|
|
456
460
|
acknowledge: async (handedOff) => {
|
|
457
461
|
if (!handedOff || acknowledged) return;
|
|
458
462
|
acknowledged = true;
|
|
459
|
-
await commitState(
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
463
|
+
await commitState(
|
|
464
|
+
path,
|
|
465
|
+
{
|
|
466
|
+
sessionId: options.sessionId,
|
|
467
|
+
siteUrl,
|
|
468
|
+
workspace,
|
|
469
|
+
startedAt,
|
|
470
|
+
watermarkMs: pageWatermark,
|
|
471
|
+
seenIds: freshRows.map((row) => row.id),
|
|
472
|
+
report,
|
|
473
|
+
feedAvailable,
|
|
474
|
+
now: Date.now()
|
|
475
|
+
},
|
|
476
|
+
namespace
|
|
477
|
+
);
|
|
470
478
|
if (privatePath === void 0 || principal === void 0 || draftPageToken === void 0) {
|
|
471
479
|
return;
|
|
472
480
|
}
|
|
473
481
|
let persistedDraft = previousDraft;
|
|
474
482
|
if (draftDigest?.deliveredIds.length) {
|
|
475
|
-
persistedDraft = await commitDraftState(
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
483
|
+
persistedDraft = await commitDraftState(
|
|
484
|
+
privatePath,
|
|
485
|
+
{
|
|
486
|
+
sessionId: options.sessionId,
|
|
487
|
+
siteUrl,
|
|
488
|
+
workspace,
|
|
489
|
+
principalId: principal.principalId,
|
|
490
|
+
organizationId: principal.organizationId,
|
|
491
|
+
credentialFingerprint: principal.credentialFingerprint,
|
|
492
|
+
deliveredIds: draftDigest.deliveredIds,
|
|
493
|
+
now: Date.now()
|
|
494
|
+
},
|
|
495
|
+
namespace
|
|
496
|
+
);
|
|
485
497
|
}
|
|
486
498
|
const seenDrafts = new Set(persistedDraft?.seenDraftIds ?? []);
|
|
487
499
|
const deliveredOnThisPage = draftPageActionableIds.filter((id) => seenDrafts.has(id));
|