@primitive.ai/prim 0.1.0-alpha.90 → 0.1.0-alpha.92

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 (45) hide show
  1. package/README.md +20 -12
  2. package/SKILL.md +6 -6
  3. package/dist/chunk-26P57LAR.js +1491 -0
  4. package/dist/{chunk-LMRYZL3D.js → chunk-5AFRMOPX.js} +1 -1
  5. package/dist/{chunk-RO3F6JYV.js → chunk-5GYQ4J3D.js} +2 -2
  6. package/dist/chunk-5HRR4AOR.js +264 -0
  7. package/dist/{chunk-3EEFL6NJ.js → chunk-67VNLLBN.js} +261 -1
  8. package/dist/{chunk-UZPBB7V4.js → chunk-ADUTW66W.js} +60 -48
  9. package/dist/{chunk-LEMX3IOG.js → chunk-BTNNEH2W.js} +1 -1
  10. package/dist/{chunk-YWO7O6QG.js → chunk-FBAYCS3X.js} +2 -2
  11. package/dist/{chunk-R26A7TWY.js → chunk-FKXFUUWJ.js} +1 -1
  12. package/dist/chunk-H4UNYFUC.js +196 -0
  13. package/dist/{chunk-CUE2LKLR.js → chunk-KFXF2V5G.js} +1 -1
  14. package/dist/{chunk-6IWNH776.js → chunk-LFFLHSQO.js} +4 -3
  15. package/dist/{chunk-C7T7B45R.js → chunk-LGGMJYY4.js} +150 -21
  16. package/dist/{chunk-MAZWKJGL.js → chunk-M6RIJVRE.js} +42 -232
  17. package/dist/{chunk-2JRX7ZR4.js → chunk-NAHHFSLH.js} +16 -5
  18. package/dist/{chunk-FI3LIRCB.js → chunk-OAAP2MY6.js} +2 -2
  19. package/dist/{chunk-Z7YPFWAZ.js → chunk-PCVGBE5B.js} +10 -268
  20. package/dist/chunk-WX52U7Z2.js +112 -0
  21. package/dist/{chunk-PPQYQEIR.js → chunk-ZB4F75SZ.js} +28 -0
  22. package/dist/daemon/server.js +30 -21
  23. package/dist/hook-runtime/daemon/server.js +30 -21
  24. package/dist/hook-runtime/hooks/post-commit.js +20 -20
  25. package/dist/hook-runtime/hooks/post-rewrite.js +20 -20
  26. package/dist/hook-runtime/hooks/post-tool-use.js +666 -147
  27. package/dist/hook-runtime/hooks/pre-tool-use.js +659 -111
  28. package/dist/hook-runtime/hooks/prim-hook.js +627 -111
  29. package/dist/hook-runtime/hooks/session-end.js +610 -27
  30. package/dist/hook-runtime/hooks/session-start.js +1132 -533
  31. package/dist/hook-runtime/index.js +764 -267
  32. package/dist/hook-runtime/statusline-main.js +48 -4
  33. package/dist/hooks/post-commit.js +11 -9
  34. package/dist/hooks/post-rewrite.js +11 -9
  35. package/dist/hooks/post-tool-use.js +73 -48
  36. package/dist/hooks/pre-commit.js +4 -4
  37. package/dist/hooks/pre-tool-use.js +46 -44
  38. package/dist/hooks/prim-hook.js +47 -25
  39. package/dist/hooks/session-end.js +15 -26
  40. package/dist/hooks/session-start.js +59 -32
  41. package/dist/index.js +561 -1430
  42. package/dist/statusline-main.js +48 -4
  43. package/package.json +1 -1
  44. package/dist/chunk-PMRDE2DA.js +0 -310
  45. package/dist/chunk-WB7AUZ4C.js +0 -52
@@ -3,7 +3,7 @@ import {
3
3
  isTerminalSafeText,
4
4
  terminalSafeLine,
5
5
  terminalSafeText
6
- } from "./chunk-Z7YPFWAZ.js";
6
+ } from "./chunk-PCVGBE5B.js";
7
7
 
8
8
  // src/decisions/feedback.ts
9
9
  var FEEDBACK_PROTOCOL_VERSION = 2;
@@ -2,10 +2,10 @@ import {
2
2
  getClient,
3
3
  getSiteUrl,
4
4
  terminalSafeLine
5
- } from "./chunk-Z7YPFWAZ.js";
5
+ } from "./chunk-PCVGBE5B.js";
6
6
  import {
7
7
  daemonRequest
8
- } from "./chunk-CUE2LKLR.js";
8
+ } from "./chunk-KFXF2V5G.js";
9
9
 
10
10
  // src/daemon/proxy.ts
11
11
  var DAEMON_HTTP_TIMEOUT_MS = 1e4;
@@ -0,0 +1,264 @@
1
+ import {
2
+ cursorHooksPath,
3
+ hasExactCursorHandler,
4
+ readCursorHooks
5
+ } from "./chunk-26P57LAR.js";
6
+ import {
7
+ resolveRepositoryContext
8
+ } from "./chunk-H4UNYFUC.js";
9
+
10
+ // src/hooks/agent.ts
11
+ function parseAgent(argv) {
12
+ const i = argv.indexOf("--agent");
13
+ const value = i !== -1 ? argv[i + 1] : void 0;
14
+ if (value === "codex" || value === "cursor" || value === "hermes") {
15
+ return value;
16
+ }
17
+ return "claude_code";
18
+ }
19
+
20
+ // src/hooks/cursor-coexistence.ts
21
+ var CURSOR_EVENTS = /* @__PURE__ */ new Set([
22
+ "sessionStart",
23
+ "beforeSubmitPrompt",
24
+ "preToolUse",
25
+ "postToolUse",
26
+ "postToolUseFailure",
27
+ "afterAgentResponse",
28
+ "subagentStop",
29
+ "stop",
30
+ "sessionEnd"
31
+ ]);
32
+ function cursorEvidence(parsed) {
33
+ return typeof parsed.cursor_version === "string" && parsed.cursor_version.length > 0 && typeof parsed.conversation_id === "string" && parsed.conversation_id.length > 0 && typeof parsed.generation_id === "string" && parsed.generation_id.length > 0 && Array.isArray(parsed.workspace_roots) && typeof parsed.hook_event_name === "string" && CURSOR_EVENTS.has(parsed.hook_event_name);
34
+ }
35
+ function uniqueProjectRoot(parsed) {
36
+ if (!Array.isArray(parsed.workspace_roots)) return;
37
+ const roots = new Set(
38
+ parsed.workspace_roots.filter((value) => typeof value === "string" && value.length > 0).map((value) => resolveRepositoryContext(value)?.repoRoot).filter((value) => !!value)
39
+ );
40
+ return roots.size === 1 ? [...roots][0] : void 0;
41
+ }
42
+ function shouldSuppressImportedCursorHandler(parsed, bin) {
43
+ if (!cursorEvidence(parsed)) return false;
44
+ const project = uniqueProjectRoot(parsed);
45
+ if (project) {
46
+ try {
47
+ if (hasExactCursorHandler(
48
+ readCursorHooks(cursorHooksPath("project", project)),
49
+ parsed.hook_event_name,
50
+ bin,
51
+ "project"
52
+ )) {
53
+ return true;
54
+ }
55
+ } catch {
56
+ }
57
+ }
58
+ try {
59
+ return hasExactCursorHandler(
60
+ readCursorHooks(cursorHooksPath("user")),
61
+ parsed.hook_event_name,
62
+ bin,
63
+ "user"
64
+ );
65
+ } catch {
66
+ return false;
67
+ }
68
+ }
69
+
70
+ // src/hooks/hook-stdin.ts
71
+ var DEFAULT_MAX_BYTES = 1024 * 1024;
72
+ function readHookStdin(timeoutMs = 1e3, maxBytes = DEFAULT_MAX_BYTES) {
73
+ return new Promise((resolve2, reject) => {
74
+ const chunks = [];
75
+ let bytes = 0;
76
+ let settled = false;
77
+ const finish = (error) => {
78
+ if (settled) return;
79
+ settled = true;
80
+ clearTimeout(timer);
81
+ process.stdin.removeAllListeners("data");
82
+ process.stdin.removeAllListeners("end");
83
+ process.stdin.removeAllListeners("error");
84
+ if (error) reject(error);
85
+ else resolve2(Buffer.concat(chunks, bytes).toString("utf-8"));
86
+ };
87
+ const timer = setTimeout(() => finish(new Error("stdin read timeout")), timeoutMs);
88
+ process.stdin.on("data", (chunk) => {
89
+ bytes += chunk.length;
90
+ if (bytes > maxBytes) {
91
+ finish(new Error("stdin envelope exceeds size limit"));
92
+ return;
93
+ }
94
+ chunks.push(chunk);
95
+ });
96
+ process.stdin.on("end", () => finish());
97
+ process.stdin.on("error", (error) => finish(error));
98
+ });
99
+ }
100
+
101
+ // src/hooks/normalize.ts
102
+ import { createHash } from "crypto";
103
+ import { realpathSync } from "fs";
104
+ import { isAbsolute, resolve } from "path";
105
+ var HERMES_EVENT_MAP = {
106
+ on_session_start: "SessionStart",
107
+ on_session_end: "SessionEnd",
108
+ pre_llm_call: "UserPromptSubmit",
109
+ post_llm_call: "Stop",
110
+ pre_tool_call: "PreToolUse",
111
+ post_tool_call: "PostToolUse",
112
+ subagent_stop: "SubagentStop"
113
+ };
114
+ var CURSOR_EVENT_MAP = {
115
+ sessionStart: "SessionStart",
116
+ beforeSubmitPrompt: "UserPromptSubmit",
117
+ preToolUse: "PreToolUse",
118
+ postToolUse: "PostToolUse",
119
+ postToolUseFailure: "PostToolUseFailure",
120
+ afterAgentResponse: "AssistantResponse",
121
+ subagentStop: "SubagentStop",
122
+ stop: "Stop",
123
+ sessionEnd: "SessionEnd"
124
+ };
125
+ var CURSOR_TOOL_EVENTS = /* @__PURE__ */ new Set(["PreToolUse", "PostToolUse", "PostToolUseFailure"]);
126
+ var SAFE_CORRELATION_ID_RE = /^[A-Za-z0-9._:-]{1,128}$/u;
127
+ var CURSOR_LOCAL_ONLY_FIELDS = /* @__PURE__ */ new Set([
128
+ "conversation_id",
129
+ "generation_id",
130
+ "user_email",
131
+ "account_email",
132
+ "transcript_path",
133
+ "transcript",
134
+ "thought",
135
+ "thoughts",
136
+ "thinking",
137
+ "tool_output"
138
+ ]);
139
+ function requiredId(value, field) {
140
+ if (typeof value !== "string" || value.length === 0) {
141
+ throw new TypeError(`Cursor hook envelope is missing ${field}`);
142
+ }
143
+ return value;
144
+ }
145
+ function cursorToolUseId(value) {
146
+ if (SAFE_CORRELATION_ID_RE.test(value)) return value;
147
+ return `cursor:tool:v1:${createHash("sha256").update(value).digest("hex")}`;
148
+ }
149
+ function workspaceCandidates(value) {
150
+ if (!Array.isArray(value)) return [];
151
+ const roots = value.filter((item) => typeof item === "string" && item.length > 0);
152
+ if (roots.some((root) => !isAbsolute(root))) {
153
+ throw new TypeError("Cursor workspace roots must be absolute");
154
+ }
155
+ return roots;
156
+ }
157
+ function pathIdentity(path) {
158
+ try {
159
+ return realpathSync.native(path);
160
+ } catch {
161
+ return resolve(path);
162
+ }
163
+ }
164
+ function resolveCursorCwd(parsed) {
165
+ const roots = workspaceCandidates(parsed.workspace_roots).map(pathIdentity);
166
+ const rootRepositories = roots.map((root) => resolveRepositoryContext(root)?.repoRoot);
167
+ const repositoryRoots = new Set(rootRepositories.filter((root) => !!root));
168
+ if (repositoryRoots.size > 1) {
169
+ throw new TypeError("Cursor hook envelope spans multiple repositories");
170
+ }
171
+ if (roots.length > 1 && rootRepositories.some((root) => root === void 0)) {
172
+ throw new TypeError("Cursor hook envelope has ambiguous workspace roots");
173
+ }
174
+ const expectedRepo = [...repositoryRoots][0];
175
+ const rawCwd = typeof parsed.cwd === "string" && parsed.cwd.length > 0 ? parsed.cwd : void 0;
176
+ if (rawCwd && !isAbsolute(rawCwd)) {
177
+ throw new TypeError("Cursor hook cwd must be absolute");
178
+ }
179
+ const envelopeCwd = rawCwd ? pathIdentity(rawCwd) : void 0;
180
+ const input = typeof parsed.tool_input === "object" && parsed.tool_input !== null && !Array.isArray(parsed.tool_input) ? parsed.tool_input : void 0;
181
+ const rawToolCwd = parsed.tool_name === "Shell" && typeof input?.working_directory === "string" ? input.working_directory : void 0;
182
+ const base = envelopeCwd ?? (roots.length === 1 ? roots[0] : expectedRepo);
183
+ if (rawToolCwd && !isAbsolute(rawToolCwd) && !base) {
184
+ throw new TypeError("relative Cursor tool cwd has no workspace base");
185
+ }
186
+ const toolCwd = rawToolCwd ? pathIdentity(isAbsolute(rawToolCwd) ? rawToolCwd : resolve(base, rawToolCwd)) : void 0;
187
+ const selected = toolCwd ?? envelopeCwd ?? (roots.length === 1 ? roots[0] : expectedRepo);
188
+ if (!selected) {
189
+ throw new TypeError("Cursor hook envelope has no unambiguous workspace");
190
+ }
191
+ const selectedRepo = resolveRepositoryContext(selected)?.repoRoot;
192
+ if (expectedRepo && selectedRepo !== expectedRepo) {
193
+ throw new TypeError("Cursor hook cwd conflicts with workspace repository");
194
+ }
195
+ return selected;
196
+ }
197
+ function normalizeCursorEnvelope(parsed) {
198
+ const rawEvent = requiredId(parsed.hook_event_name, "hook_event_name");
199
+ const event = CURSOR_EVENT_MAP[rawEvent];
200
+ if (!event) throw new TypeError(`unsupported Cursor hook event: ${rawEvent}`);
201
+ const sessionId = requiredId(parsed.conversation_id, "conversation_id");
202
+ const turnId = requiredId(parsed.generation_id, "generation_id");
203
+ if (parsed.session_id !== void 0 && parsed.session_id !== sessionId) {
204
+ throw new TypeError("Cursor conversation_id conflicts with session_id");
205
+ }
206
+ if (parsed.turn_id !== void 0 && parsed.turn_id !== turnId) {
207
+ throw new TypeError("Cursor generation_id conflicts with turn_id");
208
+ }
209
+ const rawToolUseId = CURSOR_TOOL_EVENTS.has(event) ? requiredId(parsed.tool_use_id, "tool_use_id") : void 0;
210
+ const normalized = {
211
+ ...Object.fromEntries(
212
+ Object.entries(parsed).filter(([key]) => !CURSOR_LOCAL_ONLY_FIELDS.has(key))
213
+ ),
214
+ hook_event_name: event,
215
+ session_id: sessionId,
216
+ turn_id: turnId,
217
+ cwd: resolveCursorCwd(parsed),
218
+ ...rawToolUseId === void 0 ? {} : { tool_use_id: cursorToolUseId(rawToolUseId) }
219
+ };
220
+ if (typeof parsed.tool_output === "string") {
221
+ try {
222
+ normalized.tool_response = JSON.parse(parsed.tool_output);
223
+ } catch {
224
+ normalized.tool_response = parsed.tool_output;
225
+ }
226
+ }
227
+ return normalized;
228
+ }
229
+ function normalizeEnvelope(parsed, agent) {
230
+ if (agent === "cursor") {
231
+ return normalizeCursorEnvelope(parsed);
232
+ }
233
+ if (agent === "codex") {
234
+ const event = parsed.hook_event_name;
235
+ if ((event === "PreToolUse" || event === "PostToolUse") && (typeof parsed.tool_use_id !== "string" || parsed.tool_use_id.length === 0)) {
236
+ const stableToolIdentity = JSON.stringify([
237
+ parsed.session_id ?? null,
238
+ parsed.turn_id ?? null,
239
+ parsed.tool_name ?? null,
240
+ parsed.tool_input ?? null
241
+ ]);
242
+ return {
243
+ ...parsed,
244
+ tool_use_id: `codex:fallback:v1:${createHash("sha256").update(stableToolIdentity).digest("hex")}`
245
+ };
246
+ }
247
+ return parsed;
248
+ }
249
+ if (agent !== "hermes") {
250
+ return parsed;
251
+ }
252
+ const raw = parsed.hook_event_name;
253
+ if (typeof raw === "string" && raw in HERMES_EVENT_MAP) {
254
+ return { ...parsed, hook_event_name: HERMES_EVENT_MAP[raw] };
255
+ }
256
+ return parsed;
257
+ }
258
+
259
+ export {
260
+ parseAgent,
261
+ shouldSuppressImportedCursorHandler,
262
+ readHookStdin,
263
+ normalizeEnvelope
264
+ };
@@ -498,6 +498,260 @@ function jwtOrganizationId(token) {
498
498
  return typeof orgId === "string" && orgId.length > 0 ? orgId : void 0;
499
499
  }
500
500
 
501
+ // src/lib/atomic-file.ts
502
+ import { randomUUID } from "crypto";
503
+ import {
504
+ constants,
505
+ closeSync,
506
+ fchmodSync,
507
+ fsyncSync,
508
+ mkdirSync,
509
+ openSync,
510
+ renameSync,
511
+ rmSync,
512
+ writeFileSync
513
+ } from "fs";
514
+ import { dirname } from "path";
515
+ var DEFAULT_FILE_MODE = 438;
516
+ function syncDirectory(path) {
517
+ const fd = openSync(path, constants.O_RDONLY);
518
+ try {
519
+ fsyncSync(fd);
520
+ } finally {
521
+ closeSync(fd);
522
+ }
523
+ }
524
+ function syncFile(path) {
525
+ const fd = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW);
526
+ try {
527
+ fsyncSync(fd);
528
+ } finally {
529
+ closeSync(fd);
530
+ }
531
+ }
532
+ function syncCreatedParentChain(firstCreatedParent, parent) {
533
+ const createdDirectories = [];
534
+ let current = parent;
535
+ while (current !== firstCreatedParent) {
536
+ createdDirectories.push(current);
537
+ const next = dirname(current);
538
+ if (next === current) {
539
+ throw new Error("recursive mkdir returned a path outside the target parent chain");
540
+ }
541
+ current = next;
542
+ }
543
+ createdDirectories.push(firstCreatedParent);
544
+ syncDirectory(dirname(firstCreatedParent));
545
+ for (const directory of createdDirectories.reverse().slice(0, -1)) {
546
+ syncDirectory(directory);
547
+ }
548
+ }
549
+ function ensureDurableDirectory(path, mode) {
550
+ const firstCreatedParent = mkdirSync(path, { recursive: true, mode });
551
+ if (firstCreatedParent) {
552
+ syncCreatedParentChain(firstCreatedParent, path);
553
+ }
554
+ }
555
+ function atomicWriteFile(target, content, options = {}) {
556
+ const parent = dirname(target);
557
+ if (options.ensureParent) {
558
+ ensureDurableDirectory(parent);
559
+ }
560
+ const temporaryPath = `${target}.${randomUUID()}.tmp`;
561
+ let temporaryCreated = false;
562
+ try {
563
+ const flags = constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW;
564
+ const fd = openSync(temporaryPath, flags, options.mode ?? DEFAULT_FILE_MODE);
565
+ temporaryCreated = true;
566
+ try {
567
+ if (options.mode !== void 0) fchmodSync(fd, options.mode);
568
+ writeFileSync(fd, content);
569
+ fsyncSync(fd);
570
+ } finally {
571
+ closeSync(fd);
572
+ }
573
+ options.validate?.(temporaryPath);
574
+ renameSync(temporaryPath, target);
575
+ syncDirectory(parent);
576
+ } catch (error) {
577
+ if (temporaryCreated) {
578
+ try {
579
+ rmSync(temporaryPath, { force: true });
580
+ } catch {
581
+ }
582
+ }
583
+ throw error;
584
+ }
585
+ }
586
+
587
+ // src/lib/process-liveness.ts
588
+ function processIsAlive(pid, probe = process.kill) {
589
+ if (!Number.isSafeInteger(pid) || pid <= 0) return false;
590
+ try {
591
+ probe(pid, 0);
592
+ return true;
593
+ } catch (error) {
594
+ return error.code === "EPERM";
595
+ }
596
+ }
597
+
598
+ // src/lib/file-lock.ts
599
+ import { randomBytes } from "crypto";
600
+ import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync as rmSync2, statSync, writeFileSync as writeFileSync2 } from "fs";
601
+ import { dirname as dirname2, join as join3 } from "path";
602
+ var DEFAULT_TIMEOUT_MS = 3e4;
603
+ var DEFAULT_POLL_MS = 50;
604
+ var DEFAULT_INIT_GRACE_MS = 2e3;
605
+ var DIRECTORY_MODE = 448;
606
+ var FILE_MODE = 384;
607
+ function ownerPath(lockDir) {
608
+ return join3(lockDir, "owner.json");
609
+ }
610
+ function readOwner(lockDir) {
611
+ try {
612
+ const owner = JSON.parse(readFileSync2(ownerPath(lockDir), "utf8"));
613
+ if (!Number.isInteger(owner.pid) || owner.pid <= 0 || typeof owner.nonce !== "string" || typeof owner.createdAt !== "number") {
614
+ return null;
615
+ }
616
+ return owner;
617
+ } catch {
618
+ return null;
619
+ }
620
+ }
621
+ function sameOwner(a, b) {
622
+ return a?.pid === b.pid && a.nonce === b.nonce && a.createdAt === b.createdAt;
623
+ }
624
+ function tryTakeLock(lockDir, now) {
625
+ try {
626
+ mkdirSync2(lockDir, { mode: DIRECTORY_MODE });
627
+ } catch (error) {
628
+ if (error.code === "EEXIST") return null;
629
+ throw error;
630
+ }
631
+ const owner = {
632
+ pid: process.pid,
633
+ nonce: randomBytes(16).toString("hex"),
634
+ createdAt: now
635
+ };
636
+ try {
637
+ writeFileSync2(ownerPath(lockDir), `${JSON.stringify(owner)}
638
+ `, {
639
+ encoding: "utf8",
640
+ mode: FILE_MODE,
641
+ flag: "wx"
642
+ });
643
+ return owner;
644
+ } catch (error) {
645
+ rmSync2(lockDir, { recursive: true, force: true });
646
+ throw error;
647
+ }
648
+ }
649
+ function recoverStaleLock(lockDir, now, alive, initGraceMs) {
650
+ const owner = readOwner(lockDir);
651
+ if (owner) {
652
+ if (alive(owner.pid)) return false;
653
+ if (!sameOwner(readOwner(lockDir), owner)) return false;
654
+ rmSync2(lockDir, { recursive: true, force: true });
655
+ return true;
656
+ }
657
+ try {
658
+ const before = statSync(lockDir).mtimeMs;
659
+ if (now - before < initGraceMs) return false;
660
+ if (readOwner(lockDir)) return false;
661
+ if (statSync(lockDir).mtimeMs !== before) return false;
662
+ rmSync2(lockDir, { recursive: true, force: true });
663
+ return true;
664
+ } catch {
665
+ return true;
666
+ }
667
+ }
668
+ function abortError(signal) {
669
+ if (signal.reason instanceof Error) return signal.reason;
670
+ const error = new Error("The operation was aborted");
671
+ error.name = "AbortError";
672
+ return error;
673
+ }
674
+ function throwIfAborted(signal) {
675
+ if (signal?.aborted) throw abortError(signal);
676
+ }
677
+ async function defaultSleep2(ms, signal) {
678
+ if (!signal) {
679
+ await new Promise((resolve) => setTimeout(resolve, ms));
680
+ return;
681
+ }
682
+ throwIfAborted(signal);
683
+ await new Promise((resolve, reject) => {
684
+ const timer = setTimeout(() => {
685
+ signal.removeEventListener("abort", onAbort);
686
+ resolve();
687
+ }, ms);
688
+ const onAbort = () => {
689
+ clearTimeout(timer);
690
+ reject(abortError(signal));
691
+ };
692
+ signal.addEventListener("abort", onAbort, { once: true });
693
+ });
694
+ }
695
+ var syncSleeper = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
696
+ function defaultSleepSync(ms) {
697
+ Atomics.wait(syncSleeper, 0, 0, ms);
698
+ }
699
+ function withFileLockSync(lockDir, operation, options = {}) {
700
+ mkdirSync2(dirname2(lockDir), { recursive: true, mode: DIRECTORY_MODE });
701
+ const nowMs = options.nowMs ?? Date.now;
702
+ const alive = options.processAlive ?? processIsAlive;
703
+ const deadline = nowMs() + (options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
704
+ let owner = null;
705
+ while (!owner) {
706
+ const now = nowMs();
707
+ owner = tryTakeLock(lockDir, now);
708
+ if (owner) break;
709
+ recoverStaleLock(lockDir, now, alive, options.initGraceMs ?? DEFAULT_INIT_GRACE_MS);
710
+ if (now >= deadline) {
711
+ throw new Error(`timed out waiting for file lock ${lockDir}`);
712
+ }
713
+ (options.sleep ?? defaultSleepSync)(options.pollMs ?? DEFAULT_POLL_MS);
714
+ }
715
+ try {
716
+ return operation();
717
+ } finally {
718
+ if (sameOwner(readOwner(lockDir), owner)) {
719
+ rmSync2(lockDir, { recursive: true, force: true });
720
+ }
721
+ }
722
+ }
723
+ async function withFileLock(lockDir, operation, options = {}) {
724
+ mkdirSync2(dirname2(lockDir), { recursive: true, mode: DIRECTORY_MODE });
725
+ const nowMs = options.nowMs ?? Date.now;
726
+ const alive = options.processAlive ?? processIsAlive;
727
+ const deadline = nowMs() + (options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
728
+ let owner = null;
729
+ while (!owner) {
730
+ throwIfAborted(options.signal);
731
+ const now = nowMs();
732
+ owner = tryTakeLock(lockDir, now);
733
+ if (owner) break;
734
+ recoverStaleLock(lockDir, now, alive, options.initGraceMs ?? DEFAULT_INIT_GRACE_MS);
735
+ if (now >= deadline) {
736
+ throw new Error(`timed out waiting for file lock ${lockDir}`);
737
+ }
738
+ if (options.sleep) {
739
+ await options.sleep(options.pollMs ?? DEFAULT_POLL_MS);
740
+ throwIfAborted(options.signal);
741
+ } else {
742
+ await defaultSleep2(options.pollMs ?? DEFAULT_POLL_MS, options.signal);
743
+ }
744
+ }
745
+ try {
746
+ throwIfAborted(options.signal);
747
+ return await operation();
748
+ } finally {
749
+ if (sameOwner(readOwner(lockDir), owner)) {
750
+ rmSync2(lockDir, { recursive: true, force: true });
751
+ }
752
+ }
753
+ }
754
+
501
755
  export {
502
756
  __commonJS,
503
757
  __toESM,
@@ -525,5 +779,11 @@ export {
525
779
  readStoredCredentialMetadata,
526
780
  decodeJwtPayload,
527
781
  jwtExpiresAt,
528
- jwtOrganizationId
782
+ jwtOrganizationId,
783
+ syncDirectory,
784
+ syncFile,
785
+ atomicWriteFile,
786
+ processIsAlive,
787
+ withFileLockSync,
788
+ withFileLock
529
789
  };