@yansigit/opencodex 2.36.1-dev.20260829.32 → 2.36.1-dev.20260829.34
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/gui/dist/assets/{ApiKeys-nJ-qJbkG.js → ApiKeys-BthcYAks.js} +1 -1
- package/gui/dist/assets/{Claude-Br34aNO2.js → Claude-D1uQ1rsr.js} +1 -1
- package/gui/dist/assets/{CodexSet-Bo2Vel9f.js → CodexSet-BUNKAStQ.js} +1 -1
- package/gui/dist/assets/{FileIntegrationPage-8JuhUwT7.js → FileIntegrationPage-CyrJ_kz1.js} +1 -1
- package/gui/dist/assets/{Grok-pTPUqi3L.js → Grok-CeSYaWba.js} +1 -1
- package/gui/dist/assets/{Integrations-N-s59k0n.js → Integrations-BkE9NLJW.js} +2 -2
- package/gui/dist/assets/{IntegrationsOverview-C6Mkt8OU.js → IntegrationsOverview-D-nDl5t5.js} +1 -1
- package/gui/dist/assets/{Logs-BzYAbD9a.js → Logs-uExIBMZr.js} +1 -1
- package/gui/dist/assets/{Models-8o4TdPzd.js → Models-CNpjs-ld.js} +1 -1
- package/gui/dist/assets/{NumberStepper-y1MLMfxu.js → NumberStepper-CKVXkKzE.js} +1 -1
- package/gui/dist/assets/{Providers-oTh_-wqc.js → Providers-D72-5Egl.js} +1 -1
- package/gui/dist/assets/{RestoreDialog-BnPA60nS.js → RestoreDialog-2MVe4MQR.js} +1 -1
- package/gui/dist/assets/{Startup-eM7zbmU_.js → Startup-ClZlpU_Z.js} +1 -1
- package/gui/dist/assets/{Storage-Cuf1wakW.js → Storage-ChC2WGMp.js} +1 -1
- package/gui/dist/assets/{Subagents-DEnwUJJx.js → Subagents-BpkXAXLZ.js} +1 -1
- package/gui/dist/assets/{Usage-BMBOTm6l.js → Usage-BkEOitI3.js} +1 -1
- package/gui/dist/assets/{data-surface-Cq2fDI2w.js → data-surface-BzhqUIJ_.js} +1 -1
- package/gui/dist/assets/{index-CEcmjvjC.js → index-DE4QGJSt.js} +2 -2
- package/gui/dist/assets/{model-display-CTtXLqIv.js → model-display-CCqxubZd.js} +1 -1
- package/gui/dist/assets/{provider-payload-Bs3Sdqv7.js → provider-payload-CI2ayvqv.js} +1 -1
- package/gui/dist/index.html +1 -1
- package/package.json +1 -1
- package/src/adapters/command-code-project-context.ts +24 -19
- package/src/codex/catalog/sync.ts +7 -2
- package/src/generated/compatibility-version.json +11 -11
- package/src/oauth/google-antigravity.ts +18 -5
- package/src/storage/cleanup.ts +11 -0
- package/src/storage/policy-job.ts +29 -3
- package/src/storage/policy-worker.ts +8 -1
- package/src/storage/policy.ts +3 -0
- package/src/storage/restore-job.ts +5 -0
- package/src/storage/restore-worker.ts +7 -1
- package/src/storage/storage-mutation-coordinator.ts +6 -0
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
"path": "package.json",
|
|
13
|
-
"sha256": "
|
|
13
|
+
"sha256": "3d5021c8d2ed4fd75d71843af16e27288d71580386a8fb5b6f9d2c2ea2851fb6"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"path": "scripts/model-metadata.source.json",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
56
|
"path": "src/adapters/command-code-project-context.ts",
|
|
57
|
-
"sha256": "
|
|
57
|
+
"sha256": "44c4f2ce44231fb19d7068363b6a13f38c8d7fb7ffd7e049f3c47da967abb7ab"
|
|
58
58
|
},
|
|
59
59
|
{
|
|
60
60
|
"path": "src/adapters/command-code.ts",
|
|
@@ -814,7 +814,7 @@
|
|
|
814
814
|
},
|
|
815
815
|
{
|
|
816
816
|
"path": "src/codex/catalog/sync.ts",
|
|
817
|
-
"sha256": "
|
|
817
|
+
"sha256": "f03b4c28e4be29f9df3784d1cb58bc83ca8a58e646d91894b9a90fa339508be4"
|
|
818
818
|
},
|
|
819
819
|
{
|
|
820
820
|
"path": "src/codex/codex-write-lock.ts",
|
|
@@ -2166,7 +2166,7 @@
|
|
|
2166
2166
|
},
|
|
2167
2167
|
{
|
|
2168
2168
|
"path": "src/oauth/google-antigravity.ts",
|
|
2169
|
-
"sha256": "
|
|
2169
|
+
"sha256": "a5546286e647862c4b6be097495e10ec75ba0026f4c20ab8cb277c1d2697893d"
|
|
2170
2170
|
},
|
|
2171
2171
|
{
|
|
2172
2172
|
"path": "src/oauth/health.ts",
|
|
@@ -3074,11 +3074,11 @@
|
|
|
3074
3074
|
},
|
|
3075
3075
|
{
|
|
3076
3076
|
"path": "src/storage/cleanup.ts",
|
|
3077
|
-
"sha256": "
|
|
3077
|
+
"sha256": "2a90e83983c7da9bdb2ac0134870dd7ba85d4469237fae798c60b34985c8e554"
|
|
3078
3078
|
},
|
|
3079
3079
|
{
|
|
3080
3080
|
"path": "src/storage/policy-job.ts",
|
|
3081
|
-
"sha256": "
|
|
3081
|
+
"sha256": "4fa9f2297feb1a9404af6173adbf875c19f5e79f4b1aca0056744e83d503ad8a"
|
|
3082
3082
|
},
|
|
3083
3083
|
{
|
|
3084
3084
|
"path": "src/storage/policy-scheduler.ts",
|
|
@@ -3086,19 +3086,19 @@
|
|
|
3086
3086
|
},
|
|
3087
3087
|
{
|
|
3088
3088
|
"path": "src/storage/policy-worker.ts",
|
|
3089
|
-
"sha256": "
|
|
3089
|
+
"sha256": "c33d74fe319fc9103bd1937a81c8f0b23f319d7f634bea87a691da845ebec282"
|
|
3090
3090
|
},
|
|
3091
3091
|
{
|
|
3092
3092
|
"path": "src/storage/policy.ts",
|
|
3093
|
-
"sha256": "
|
|
3093
|
+
"sha256": "93050c0be848f0df9c7114d903272738fb817b62fb0cdd9d7a59e55b9b267601"
|
|
3094
3094
|
},
|
|
3095
3095
|
{
|
|
3096
3096
|
"path": "src/storage/restore-job.ts",
|
|
3097
|
-
"sha256": "
|
|
3097
|
+
"sha256": "216a1a1c3610f5d3fbf50952855b792583f851bc989294f986208c93fd9eefaa"
|
|
3098
3098
|
},
|
|
3099
3099
|
{
|
|
3100
3100
|
"path": "src/storage/restore-worker.ts",
|
|
3101
|
-
"sha256": "
|
|
3101
|
+
"sha256": "4ff17868a3e04fd28db86269c17d2e5600cf6e0222374fc3032854cb0990c766"
|
|
3102
3102
|
},
|
|
3103
3103
|
{
|
|
3104
3104
|
"path": "src/storage/scanner.ts",
|
|
@@ -3106,7 +3106,7 @@
|
|
|
3106
3106
|
},
|
|
3107
3107
|
{
|
|
3108
3108
|
"path": "src/storage/storage-mutation-coordinator.ts",
|
|
3109
|
-
"sha256": "
|
|
3109
|
+
"sha256": "65d0560e25f73d6b4bbfb64168cfd37bd796d74781368819207339be254df51b"
|
|
3110
3110
|
},
|
|
3111
3111
|
{
|
|
3112
3112
|
"path": "src/storage/worker-lifecycle.ts",
|
|
@@ -37,7 +37,13 @@ const CALLBACK_PATH = "/callback";
|
|
|
37
37
|
const REFRESH_SKEW_MS = 5 * 60 * 1000;
|
|
38
38
|
const REQUEST_TIMEOUT_MS = 30_000;
|
|
39
39
|
const ONBOARD_ATTEMPTS = 5;
|
|
40
|
-
const
|
|
40
|
+
const ANTIGRAVITY_ONBOARD_POLL_MS = 2_000;
|
|
41
|
+
const defaultSleep = (ms: number): Promise<void> => new Promise(resolve => setTimeout(resolve, ms));
|
|
42
|
+
let sleepForTests: ((ms: number) => Promise<void>) | undefined;
|
|
43
|
+
|
|
44
|
+
export function setAntigravityOnboardSleepForTests(sleep: ((ms: number) => Promise<void>) | undefined): void {
|
|
45
|
+
sleepForTests = sleep;
|
|
46
|
+
}
|
|
41
47
|
|
|
42
48
|
function requestSignal(signal: AbortSignal | undefined): AbortSignal {
|
|
43
49
|
const timeout = AbortSignal.timeout(REQUEST_TIMEOUT_MS);
|
|
@@ -136,7 +142,11 @@ async function loadCodeAssistProject(accessToken: string, signal?: AbortSignal):
|
|
|
136
142
|
return extractProjectId((await response.json().catch(() => undefined)) as Record<string, unknown> | undefined);
|
|
137
143
|
}
|
|
138
144
|
|
|
139
|
-
async function onboardProject(
|
|
145
|
+
async function onboardProject(
|
|
146
|
+
accessToken: string,
|
|
147
|
+
signal?: AbortSignal,
|
|
148
|
+
): Promise<string | undefined> {
|
|
149
|
+
const sleep = sleepForTests ?? defaultSleep;
|
|
140
150
|
for (let attempt = 0; attempt < ONBOARD_ATTEMPTS; attempt++) {
|
|
141
151
|
if (signal?.aborted) throw signal.reason ?? new Error("Antigravity onboarding aborted");
|
|
142
152
|
const response = await fetch(`${DAILY_API}/${API_VERSION}:onboardUser`, {
|
|
@@ -153,7 +163,7 @@ async function onboardProject(accessToken: string, signal?: AbortSignal): Promis
|
|
|
153
163
|
if (!response.ok) {
|
|
154
164
|
// Transient (429/5xx): keep polling within the attempt budget. Hard 4xx: give up now.
|
|
155
165
|
if (response.status === 429 || response.status >= 500) {
|
|
156
|
-
await
|
|
166
|
+
await sleep(ANTIGRAVITY_ONBOARD_POLL_MS);
|
|
157
167
|
continue;
|
|
158
168
|
}
|
|
159
169
|
return undefined;
|
|
@@ -162,13 +172,16 @@ async function onboardProject(accessToken: string, signal?: AbortSignal): Promis
|
|
|
162
172
|
if (data.done === true) {
|
|
163
173
|
return extractProjectId(data.response as Record<string, unknown> | undefined);
|
|
164
174
|
}
|
|
165
|
-
await
|
|
175
|
+
await sleep(ANTIGRAVITY_ONBOARD_POLL_MS);
|
|
166
176
|
}
|
|
167
177
|
return undefined;
|
|
168
178
|
}
|
|
169
179
|
|
|
170
180
|
/** Discover the CCA project for an access token (loadCodeAssist → onboardUser fallback). */
|
|
171
|
-
export async function discoverAntigravityProject(
|
|
181
|
+
export async function discoverAntigravityProject(
|
|
182
|
+
accessToken: string,
|
|
183
|
+
signal?: AbortSignal,
|
|
184
|
+
): Promise<string | undefined> {
|
|
172
185
|
return (await loadCodeAssistProject(accessToken, signal)) ?? (await onboardProject(accessToken, signal));
|
|
173
186
|
}
|
|
174
187
|
|
package/src/storage/cleanup.ts
CHANGED
|
@@ -2578,6 +2578,10 @@ export interface RestoreTestHooks {
|
|
|
2578
2578
|
* reconcile, so cleanup can race an in-flight restore.
|
|
2579
2579
|
*/
|
|
2580
2580
|
holdAfterFileMovesMs?: number;
|
|
2581
|
+
/** Internal test seam: pause after moves until the owner releases it. */
|
|
2582
|
+
waitAfterFileMoves?: SharedArrayBuffer;
|
|
2583
|
+
/** Internal worker-only notification after rollout moves complete. */
|
|
2584
|
+
notifyAfterFileMoves?: boolean;
|
|
2581
2585
|
}
|
|
2582
2586
|
|
|
2583
2587
|
/**
|
|
@@ -2924,6 +2928,13 @@ export function restoreTrashEntry(
|
|
|
2924
2928
|
while (Date.now() < deadline) { /* test-only spin wait */ }
|
|
2925
2929
|
}
|
|
2926
2930
|
}
|
|
2931
|
+
if (hooks?.waitAfterFileMoves) {
|
|
2932
|
+
if (hooks.notifyAfterFileMoves) {
|
|
2933
|
+
Atomics.store(new Int32Array(hooks.waitAfterFileMoves), 1, 1);
|
|
2934
|
+
Atomics.notify(new Int32Array(hooks.waitAfterFileMoves), 1);
|
|
2935
|
+
}
|
|
2936
|
+
Atomics.wait(new Int32Array(hooks.waitAfterFileMoves), 0, 0);
|
|
2937
|
+
}
|
|
2927
2938
|
|
|
2928
2939
|
if (hooks?.failAfterFileMoves) {
|
|
2929
2940
|
return abortAfterMoves("fs_failed");
|
|
@@ -62,6 +62,14 @@ export interface PolicyJobTestHooks {
|
|
|
62
62
|
* policy load, so concurrent PUTs can race completion metadata writes.
|
|
63
63
|
*/
|
|
64
64
|
blockMs?: number;
|
|
65
|
+
/** Notify race tests once the shared mutation lease is acquired. */
|
|
66
|
+
onAcquired?: () => void;
|
|
67
|
+
/** Notify after the worker has loaded its policy snapshot. */
|
|
68
|
+
onPolicyLoaded?: () => void;
|
|
69
|
+
/** Test-only worker gate after the policy snapshot, before metadata save. */
|
|
70
|
+
waitAfterPolicyLoadedBuffer?: SharedArrayBuffer;
|
|
71
|
+
/** Keep the policy job at the lease boundary until a competing request is issued. */
|
|
72
|
+
waitForRelease?: Promise<void>;
|
|
65
73
|
/**
|
|
66
74
|
* When true, run on the main thread via `queueMicrotask` + optional sleep.
|
|
67
75
|
* Used only for unit tests that cannot spawn workers; responsiveness tests
|
|
@@ -292,13 +300,14 @@ function applyMutationBusy(): void {
|
|
|
292
300
|
};
|
|
293
301
|
}
|
|
294
302
|
|
|
295
|
-
function runInWorker(opts: RequestPolicyRunOptions & { blockMs?: number }): Promise<PolicyRunResult> {
|
|
303
|
+
function runInWorker(opts: RequestPolicyRunOptions & { blockMs?: number; notifyLoaded?: () => void; waitAfterPolicyLoadedBuffer?: SharedArrayBuffer }): Promise<PolicyRunResult> {
|
|
296
304
|
const reservation = tryReserveStorageWorker();
|
|
297
305
|
if (!reservation) return Promise.reject(new StorageWorkerAdmissionBusyError());
|
|
298
306
|
return withStorageWorkerSpawnGate(() => new Promise<PolicyRunResult>((resolve, reject) => {
|
|
299
307
|
const requestId = crypto.randomUUID();
|
|
300
308
|
let settled = false;
|
|
301
309
|
let worker: Worker;
|
|
310
|
+
const loadedRelease = opts.waitAfterPolicyLoadedBuffer;
|
|
302
311
|
try {
|
|
303
312
|
worker = new Worker(new URL("./policy-worker.ts", import.meta.url).href);
|
|
304
313
|
reservation.bind(worker);
|
|
@@ -335,6 +344,10 @@ function runInWorker(opts: RequestPolicyRunOptions & { blockMs?: number }): Prom
|
|
|
335
344
|
if (!data || typeof data !== "object") return;
|
|
336
345
|
const msg = data as Record<string, unknown>;
|
|
337
346
|
if (msg.requestId !== requestId) return;
|
|
347
|
+
if (msg.type === "loaded") {
|
|
348
|
+
opts.notifyLoaded?.();
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
338
351
|
if (msg.type === "done" && msg.result && typeof msg.result === "object") {
|
|
339
352
|
finish(() => resolve(msg.result as PolicyRunResult));
|
|
340
353
|
return;
|
|
@@ -357,6 +370,9 @@ function runInWorker(opts: RequestPolicyRunOptions & { blockMs?: number }): Prom
|
|
|
357
370
|
...(opts.codexHome ? { codexHome: opts.codexHome } : {}),
|
|
358
371
|
...(opts.busyTimeoutMs !== undefined ? { busyTimeoutMs: opts.busyTimeoutMs } : {}),
|
|
359
372
|
...(opts.blockMs !== undefined ? { blockMs: opts.blockMs } : {}),
|
|
373
|
+
...(opts.notifyLoaded ? { notifyLoaded: true } : {}),
|
|
374
|
+
...(loadedRelease ? { waitForLoadedRelease: true } : {}),
|
|
375
|
+
...(loadedRelease ? { loadedRelease } : {}),
|
|
360
376
|
env: {
|
|
361
377
|
...(process.env.CODEX_HOME ? { CODEX_HOME: process.env.CODEX_HOME } : {}),
|
|
362
378
|
...(process.env.OPENCODEX_HOME ? { OPENCODEX_HOME: process.env.OPENCODEX_HOME } : {}),
|
|
@@ -378,8 +394,12 @@ async function executeJob(opts: RequestPolicyRunOptions): Promise<void> {
|
|
|
378
394
|
}
|
|
379
395
|
return;
|
|
380
396
|
}
|
|
381
|
-
|
|
397
|
+
const lease = gate.lease;
|
|
398
|
+
heldMutationLease = lease;
|
|
382
399
|
try {
|
|
400
|
+
testHooks?.onAcquired?.();
|
|
401
|
+
await testHooks?.waitForRelease;
|
|
402
|
+
if (generation !== runGeneration) return;
|
|
383
403
|
const blockMs = testHooks?.blockMs;
|
|
384
404
|
let result: PolicyRunResult;
|
|
385
405
|
|
|
@@ -392,12 +412,15 @@ async function executeJob(opts: RequestPolicyRunOptions): Promise<void> {
|
|
|
392
412
|
codexHome,
|
|
393
413
|
...(opts.busyTimeoutMs !== undefined ? { busyTimeoutMs: opts.busyTimeoutMs } : {}),
|
|
394
414
|
...(typeof blockMs === "number" && blockMs > 0 ? { holdAfterLoadMs: blockMs } : {}),
|
|
415
|
+
...(testHooks?.onPolicyLoaded ? { onPolicyLoaded: testHooks.onPolicyLoaded } : {}),
|
|
395
416
|
});
|
|
396
417
|
} else {
|
|
397
418
|
result = await runInWorker({
|
|
398
419
|
...opts,
|
|
399
420
|
codexHome,
|
|
400
421
|
...(typeof blockMs === "number" && blockMs > 0 ? { blockMs } : {}),
|
|
422
|
+
...(testHooks?.onPolicyLoaded ? { notifyLoaded: testHooks.onPolicyLoaded } : {}),
|
|
423
|
+
...(testHooks?.waitAfterPolicyLoadedBuffer ? { waitAfterPolicyLoadedBuffer: testHooks.waitAfterPolicyLoadedBuffer } : {}),
|
|
401
424
|
});
|
|
402
425
|
}
|
|
403
426
|
|
|
@@ -408,7 +431,10 @@ async function executeJob(opts: RequestPolicyRunOptions): Promise<void> {
|
|
|
408
431
|
if (err instanceof StorageWorkerAdmissionBusyError) applyMutationBusy();
|
|
409
432
|
else applyFailed(err instanceof Error ? err.message : "worker_failed");
|
|
410
433
|
} finally {
|
|
411
|
-
|
|
434
|
+
// A reset may let a newer run acquire the global mirror before this stale
|
|
435
|
+
// continuation resumes. Release only the lease captured by this run.
|
|
436
|
+
lease.release();
|
|
437
|
+
if (heldMutationLease === lease) heldMutationLease = undefined;
|
|
412
438
|
}
|
|
413
439
|
}
|
|
414
440
|
|
|
@@ -13,6 +13,9 @@ interface RunMessage {
|
|
|
13
13
|
busyTimeoutMs?: number;
|
|
14
14
|
/** Test-only: block after loading the start-of-job policy (see holdAfterLoadMs). */
|
|
15
15
|
blockMs?: number;
|
|
16
|
+
notifyLoaded?: boolean;
|
|
17
|
+
waitForLoadedRelease?: boolean;
|
|
18
|
+
loadedRelease?: SharedArrayBuffer;
|
|
16
19
|
/** Env snapshot — Workers may not see parent mutations on all platforms. */
|
|
17
20
|
env?: { CODEX_HOME?: string; OPENCODEX_HOME?: string };
|
|
18
21
|
}
|
|
@@ -29,7 +32,7 @@ declare const self: Worker;
|
|
|
29
32
|
|
|
30
33
|
self.onmessage = (event: MessageEvent<unknown>) => {
|
|
31
34
|
if (!isRunMessage(event.data)) return;
|
|
32
|
-
const { requestId, reason, force, codexHome, busyTimeoutMs, blockMs, env } = event.data;
|
|
35
|
+
const { requestId, reason, force, codexHome, busyTimeoutMs, blockMs, notifyLoaded, waitForLoadedRelease, loadedRelease, env } = event.data;
|
|
33
36
|
try {
|
|
34
37
|
if (env?.CODEX_HOME) process.env.CODEX_HOME = env.CODEX_HOME;
|
|
35
38
|
if (env?.OPENCODEX_HOME) process.env.OPENCODEX_HOME = env.OPENCODEX_HOME;
|
|
@@ -38,6 +41,10 @@ self.onmessage = (event: MessageEvent<unknown>) => {
|
|
|
38
41
|
force: force === true,
|
|
39
42
|
...(codexHome ? { codexHome } : {}),
|
|
40
43
|
...(busyTimeoutMs !== undefined ? { busyTimeoutMs } : {}),
|
|
44
|
+
...(notifyLoaded ? { onPolicyLoaded: () => {
|
|
45
|
+
self.postMessage({ type: "loaded", requestId });
|
|
46
|
+
if (waitForLoadedRelease && loadedRelease) Atomics.wait(new Int32Array(loadedRelease), 0, 0);
|
|
47
|
+
} } : {}),
|
|
41
48
|
...(typeof blockMs === "number" && Number.isFinite(blockMs) && blockMs > 0
|
|
42
49
|
? { holdAfterLoadMs: Math.floor(blockMs) }
|
|
43
50
|
: {}),
|
package/src/storage/policy.ts
CHANGED
|
@@ -72,6 +72,8 @@ export interface PolicyRunDeps {
|
|
|
72
72
|
* land before completion merges run metadata.
|
|
73
73
|
*/
|
|
74
74
|
holdAfterLoadMs?: number;
|
|
75
|
+
/** Test-only synchronization seam; omitted in production. */
|
|
76
|
+
onPolicyLoaded?: () => void;
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
/** Canonical defaults — enabled is always false. */
|
|
@@ -436,6 +438,7 @@ export function runStorageCleanupPolicy(deps: PolicyRunDeps): PolicyRunResult {
|
|
|
436
438
|
const execute = deps.execute ?? executeArchivedCleanup;
|
|
437
439
|
|
|
438
440
|
const policy = normalizeStorageCleanupPolicy(load());
|
|
441
|
+
deps.onPolicyLoaded?.();
|
|
439
442
|
|
|
440
443
|
if (typeof deps.holdAfterLoadMs === "number" && Number.isFinite(deps.holdAfterLoadMs) && deps.holdAfterLoadMs > 0) {
|
|
441
444
|
Bun.sleepSync(Math.floor(deps.holdAfterLoadMs));
|
|
@@ -35,6 +35,8 @@ export interface RestoreJobTestHooks extends StorageMutationCoordinatorTestHooks
|
|
|
35
35
|
enableTestStream?: boolean;
|
|
36
36
|
/** Forwarded to restoreTrashEntry _test hooks. */
|
|
37
37
|
restoreTest?: RestoreTestHooks;
|
|
38
|
+
/** Notify responsiveness tests once the worker is about to block. */
|
|
39
|
+
workerReady?: SharedArrayBuffer;
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
const WORKER_TIMEOUT_MS = 10 * 60 * 1000;
|
|
@@ -159,6 +161,7 @@ function runInWorker(opts: {
|
|
|
159
161
|
busyTimeoutMs?: number;
|
|
160
162
|
blockMs?: number;
|
|
161
163
|
restoreTest?: RestoreTestHooks;
|
|
164
|
+
workerReady?: SharedArrayBuffer;
|
|
162
165
|
}): Promise<RestoreResult> {
|
|
163
166
|
const reservation = tryReserveStorageWorker();
|
|
164
167
|
if (!reservation) return Promise.reject(new StorageWorkerAdmissionBusyError());
|
|
@@ -222,6 +225,7 @@ function runInWorker(opts: {
|
|
|
222
225
|
...(opts.busyTimeoutMs !== undefined ? { busyTimeoutMs: opts.busyTimeoutMs } : {}),
|
|
223
226
|
...(opts.blockMs !== undefined ? { blockMs: opts.blockMs } : {}),
|
|
224
227
|
...(opts.restoreTest ? { restoreTest: opts.restoreTest } : {}),
|
|
228
|
+
...(opts.workerReady ? { workerReady: opts.workerReady } : {}),
|
|
225
229
|
env: {
|
|
226
230
|
...(process.env.CODEX_HOME ? { CODEX_HOME: process.env.CODEX_HOME } : {}),
|
|
227
231
|
...(process.env.OPENCODEX_HOME ? { OPENCODEX_HOME: process.env.OPENCODEX_HOME } : {}),
|
|
@@ -258,6 +262,7 @@ async function executeRestore(opts: {
|
|
|
258
262
|
...(opts.busyTimeoutMs !== undefined ? { busyTimeoutMs: opts.busyTimeoutMs } : {}),
|
|
259
263
|
...(typeof blockMs === "number" && blockMs > 0 ? { blockMs } : {}),
|
|
260
264
|
...(restoreTest ? { restoreTest } : {}),
|
|
265
|
+
...(testHooks?.workerReady ? { workerReady: testHooks.workerReady } : {}),
|
|
261
266
|
});
|
|
262
267
|
} catch (err) {
|
|
263
268
|
if (err instanceof StorageWorkerAdmissionBusyError) return busyRestoreResult();
|
|
@@ -13,6 +13,7 @@ interface RunMessage {
|
|
|
13
13
|
/** Test-only: block before restore so responsiveness tests can probe /healthz. */
|
|
14
14
|
blockMs?: number;
|
|
15
15
|
restoreTest?: RestoreTestHooks;
|
|
16
|
+
workerReady?: SharedArrayBuffer;
|
|
16
17
|
/** Env snapshot — Workers may not see parent mutations on all platforms. */
|
|
17
18
|
env?: { CODEX_HOME?: string; OPENCODEX_HOME?: string };
|
|
18
19
|
}
|
|
@@ -27,11 +28,16 @@ declare const self: Worker;
|
|
|
27
28
|
|
|
28
29
|
self.onmessage = async (event: MessageEvent<unknown>) => {
|
|
29
30
|
if (!isRunMessage(event.data)) return;
|
|
30
|
-
const { requestId, trashId, codexHome, busyTimeoutMs, blockMs, restoreTest, env } = event.data;
|
|
31
|
+
const { requestId, trashId, codexHome, busyTimeoutMs, blockMs, restoreTest, workerReady, env } = event.data;
|
|
31
32
|
try {
|
|
32
33
|
if (env?.CODEX_HOME) process.env.CODEX_HOME = env.CODEX_HOME;
|
|
33
34
|
if (env?.OPENCODEX_HOME) process.env.OPENCODEX_HOME = env.OPENCODEX_HOME;
|
|
34
35
|
if (typeof blockMs === "number" && Number.isFinite(blockMs) && blockMs > 0) {
|
|
36
|
+
if (workerReady) {
|
|
37
|
+
const ready = new Int32Array(workerReady);
|
|
38
|
+
Atomics.store(ready, 0, 1);
|
|
39
|
+
Atomics.notify(ready, 0);
|
|
40
|
+
}
|
|
35
41
|
await Bun.sleep(Math.floor(blockMs));
|
|
36
42
|
}
|
|
37
43
|
const result = restoreTrashEntry(trashId, {
|
|
@@ -16,6 +16,10 @@ export type StorageMutationBusyError = "storage_mutation_busy";
|
|
|
16
16
|
export interface StorageMutationCoordinatorTestHooks {
|
|
17
17
|
/** Block after acquiring the slot, before mutation work (race tests). */
|
|
18
18
|
blockMs?: number;
|
|
19
|
+
/** Notify race tests as soon as the per-home lease is owned. */
|
|
20
|
+
onAcquired?: () => void;
|
|
21
|
+
/** Keep the lease held until a race test has issued its competing operation. */
|
|
22
|
+
waitForRelease?: Promise<void>;
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
interface ActiveSlot {
|
|
@@ -79,6 +83,7 @@ export function tryBeginStorageMutation(
|
|
|
79
83
|
},
|
|
80
84
|
};
|
|
81
85
|
slots.set(key, { kind, startedAt: Date.now(), lease: ownerLease });
|
|
86
|
+
testHooks?.onAcquired?.();
|
|
82
87
|
return { acquired: true, lease: ownerLease };
|
|
83
88
|
}
|
|
84
89
|
|
|
@@ -94,6 +99,7 @@ export function endStorageMutation(codexHome?: string): void {
|
|
|
94
99
|
}
|
|
95
100
|
|
|
96
101
|
async function applyCoordinatorBlock(): Promise<void> {
|
|
102
|
+
await testHooks?.waitForRelease;
|
|
97
103
|
const blockMs = testHooks?.blockMs;
|
|
98
104
|
if (typeof blockMs === "number" && Number.isFinite(blockMs) && blockMs > 0) {
|
|
99
105
|
await Bun.sleep(Math.floor(blockMs));
|