@nowcrew/daemon 0.5.26 → 0.5.28
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/package.json +1 -1
- package/dist/attachments.js +0 -196
- package/dist/bound-im-decision.js +0 -22
- package/dist/completion-retransmitter.js +0 -77
- package/dist/computer-cli.js +0 -274
- package/dist/computer-profile-lock.js +0 -395
- package/dist/computer-profile.js +0 -364
- package/dist/computer-service.js +0 -358
- package/dist/config.js +0 -82
- package/dist/console-collapse.js +0 -13
- package/dist/console-formatter.js +0 -77
- package/dist/console-payload.js +0 -73
- package/dist/console.js +0 -329
- package/dist/daemon-startup-error.js +0 -30
- package/dist/execution-backend.js +0 -44
- package/dist/execution-event-limit.js +0 -64
- package/dist/execution-journal-lock.js +0 -421
- package/dist/execution-journal.js +0 -716
- package/dist/execution-protocol.js +0 -342
- package/dist/execution-recovery.js +0 -95
- package/dist/execution-runner.js +0 -659
- package/dist/execution-supervisor-child.js +0 -236
- package/dist/execution-supervisor.js +0 -302
- package/dist/execution-telemetry-journal.js +0 -71
- package/dist/external-output.js +0 -114
- package/dist/i18n.js +0 -64
- package/dist/json-result.js +0 -27
- package/dist/list-models.js +0 -92
- package/dist/local-executor.js +0 -439
- package/dist/log-format.js +0 -10
- package/dist/machine-info.js +0 -124
- package/dist/main.js +0 -118
- package/dist/normalize.js +0 -170
- package/dist/origin-decision.js +0 -44
- package/dist/platform.js +0 -8
- package/dist/prompt.js +0 -307
- package/dist/provider-env.js +0 -90
- package/dist/runner.js +0 -234
- package/dist/runtime-cancellation.js +0 -74
- package/dist/runtime-capabilities.js +0 -43
- package/dist/runtime-path.js +0 -60
- package/dist/runtimes/claude.js +0 -51
- package/dist/runtimes/codex-app-server-runner.js +0 -340
- package/dist/runtimes/codex-deepseek-catalog.js +0 -7
- package/dist/runtimes/codex-deepseek-config.js +0 -50
- package/dist/runtimes/codex.js +0 -53
- package/dist/runtimes/kimi-acp-runner.js +0 -364
- package/dist/runtimes/kimi.js +0 -45
- package/dist/runtimes/progress-watchdog.js +0 -26
- package/dist/scheduled-report.js +0 -51
- package/dist/scheduled-run-report.js +0 -57
- package/dist/serve-lifecycle.js +0 -82
- package/dist/serve.js +0 -868
- package/dist/session.js +0 -82
- package/dist/shared-execution-slots.js +0 -68
- package/dist/shutdown-deadline.js +0 -32
- package/dist/skill-preview.js +0 -21
- package/dist/skills.js +0 -56
- package/dist/slog.js +0 -228
- package/dist/supervised-runtime.js +0 -104
- package/dist/token.js +0 -24
- package/dist/unified-diff.js +0 -84
- package/dist/websocket-shutdown.js +0 -53
- package/dist/win32-job-object.js +0 -193
- package/dist/workspace-fs.js +0 -80
- package/dist/workspace-import.js +0 -127
- package/dist/workspace.js +0 -148
|
@@ -1,421 +0,0 @@
|
|
|
1
|
-
import { mkdir, open, readFile, readdir, rename, rm, rmdir, stat, unlink, } from "node:fs/promises";
|
|
2
|
-
import { renameSync } from "node:fs";
|
|
3
|
-
import { randomUUID } from "node:crypto";
|
|
4
|
-
import { join, resolve } from "node:path";
|
|
5
|
-
import { z } from "zod";
|
|
6
|
-
const OwnerSchema = z.object({
|
|
7
|
-
pid: z.number().int().positive(),
|
|
8
|
-
processIdentity: z.string().min(1),
|
|
9
|
-
token: z.string().uuid(),
|
|
10
|
-
}).strict();
|
|
11
|
-
export class JournalLockedError extends Error {
|
|
12
|
-
journalPath;
|
|
13
|
-
ownerPid;
|
|
14
|
-
constructor(message, diagnostics = {}) {
|
|
15
|
-
super(message);
|
|
16
|
-
this.name = "JournalLockedError";
|
|
17
|
-
if (diagnostics.journalPath !== undefined)
|
|
18
|
-
this.journalPath = diagnostics.journalPath;
|
|
19
|
-
if (diagnostics.ownerPid !== undefined)
|
|
20
|
-
this.ownerPid = diagnostics.ownerPid;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
export class JournalLockCorruptionError extends Error {
|
|
24
|
-
constructor(path, cause) {
|
|
25
|
-
super(`Invalid execution journal lock: ${path}`, { cause });
|
|
26
|
-
this.name = "JournalLockCorruptionError";
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
export const defaultJournalLockFileSystem = {
|
|
30
|
-
mkdir,
|
|
31
|
-
readFile: (path) => readFile(path, "utf8"),
|
|
32
|
-
readdir,
|
|
33
|
-
rename,
|
|
34
|
-
renameSync,
|
|
35
|
-
rm,
|
|
36
|
-
rmdir,
|
|
37
|
-
stat,
|
|
38
|
-
unlink,
|
|
39
|
-
writeDurableFile: async (path, contents) => {
|
|
40
|
-
const handle = await open(path, "wx", 0o600);
|
|
41
|
-
try {
|
|
42
|
-
await handle.writeFile(contents, "utf8");
|
|
43
|
-
await handle.sync();
|
|
44
|
-
}
|
|
45
|
-
finally {
|
|
46
|
-
await handle.close();
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
};
|
|
50
|
-
const defaultRegistry = { leases: new Map() };
|
|
51
|
-
export function createJournalLeaseRegistry() {
|
|
52
|
-
return { leases: new Map() };
|
|
53
|
-
}
|
|
54
|
-
const codeOf = (error) => error instanceof Error && "code" in error ? error.code : undefined;
|
|
55
|
-
const ownerFileName = (token) => `owner.${token}.json`;
|
|
56
|
-
const releasedLockName = (token) => `.journal.released.${token}.lock`;
|
|
57
|
-
const RELEASED_LOCK_PATTERN = /^\.journal\.released\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.lock$/i;
|
|
58
|
-
const MAX_RELEASED_LOCK_CLEANUP = 8;
|
|
59
|
-
const throwIfAborted = (signal) => {
|
|
60
|
-
if (signal?.aborted)
|
|
61
|
-
throw signal.reason ?? new Error("Journal close aborted");
|
|
62
|
-
};
|
|
63
|
-
function awaitAbortable(operation, signal) {
|
|
64
|
-
if (signal === undefined)
|
|
65
|
-
return operation;
|
|
66
|
-
throwIfAborted(signal);
|
|
67
|
-
return new Promise((resolveOperation, rejectOperation) => {
|
|
68
|
-
let settled = false;
|
|
69
|
-
const settle = (continuation) => {
|
|
70
|
-
if (settled)
|
|
71
|
-
return;
|
|
72
|
-
settled = true;
|
|
73
|
-
signal.removeEventListener("abort", onAbort);
|
|
74
|
-
continuation();
|
|
75
|
-
};
|
|
76
|
-
const onAbort = () => settle(() => {
|
|
77
|
-
rejectOperation(signal.reason ?? new Error("Journal close aborted"));
|
|
78
|
-
});
|
|
79
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
80
|
-
if (signal.aborted)
|
|
81
|
-
onAbort();
|
|
82
|
-
operation.then((value) => settle(() => resolveOperation(value)), (error) => settle(() => rejectOperation(error)));
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
async function parseLockOwner(lockDirectory, fileName, fileSystem) {
|
|
86
|
-
const path = join(lockDirectory, fileName);
|
|
87
|
-
try {
|
|
88
|
-
const owner = OwnerSchema.parse(JSON.parse(await fileSystem.readFile(path)));
|
|
89
|
-
if (fileName !== ownerFileName(owner.token))
|
|
90
|
-
throw new Error("owner token does not match filename");
|
|
91
|
-
return owner;
|
|
92
|
-
}
|
|
93
|
-
catch (error) {
|
|
94
|
-
throw new JournalLockCorruptionError(path, error);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
export async function inspectJournalLock(options) {
|
|
98
|
-
const fileSystem = options.fileSystem ?? defaultJournalLockFileSystem;
|
|
99
|
-
const lockDirectory = join(options.directory, ".journal.lock");
|
|
100
|
-
const orphanGraceMs = options.orphanGraceMs ?? 30_000;
|
|
101
|
-
const now = options.now ?? (() => new Date());
|
|
102
|
-
const missingDuringOwnerRead = (error) => codeOf(error) === "ENOENT"
|
|
103
|
-
|| (error instanceof Error && codeOf(error.cause) === "ENOENT");
|
|
104
|
-
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
105
|
-
let names;
|
|
106
|
-
try {
|
|
107
|
-
names = (await fileSystem.readdir(lockDirectory)).map(String).sort();
|
|
108
|
-
}
|
|
109
|
-
catch (error) {
|
|
110
|
-
if (codeOf(error) === "ENOENT") {
|
|
111
|
-
return { status: "unlocked", detail: "lock directory does not exist" };
|
|
112
|
-
}
|
|
113
|
-
return { status: "corrupt", detail: `lock directory cannot be read: ${error.message}` };
|
|
114
|
-
}
|
|
115
|
-
if (names.length === 0) {
|
|
116
|
-
try {
|
|
117
|
-
const lockStat = await fileSystem.stat(lockDirectory);
|
|
118
|
-
const ageMs = now().valueOf() - lockStat.mtimeMs;
|
|
119
|
-
return ageMs < orphanGraceMs
|
|
120
|
-
? { status: "installing", detail: "lock owner installation is in progress" }
|
|
121
|
-
: { status: "stale", detail: "empty lock directory exceeded the owner installation grace period" };
|
|
122
|
-
}
|
|
123
|
-
catch (error) {
|
|
124
|
-
if (codeOf(error) === "ENOENT" && attempt === 0)
|
|
125
|
-
continue;
|
|
126
|
-
if (codeOf(error) === "ENOENT") {
|
|
127
|
-
return { status: "unlocked", detail: "lock directory disappeared during inspection" };
|
|
128
|
-
}
|
|
129
|
-
return { status: "corrupt", detail: `empty lock directory cannot be inspected: ${error.message}` };
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
if (names.length !== 1 || !/^owner\.[0-9a-f-]+\.json$/i.test(names[0])) {
|
|
133
|
-
return { status: "corrupt", detail: "lock directory must contain one owner" };
|
|
134
|
-
}
|
|
135
|
-
let owner;
|
|
136
|
-
try {
|
|
137
|
-
owner = await parseLockOwner(lockDirectory, names[0], fileSystem);
|
|
138
|
-
}
|
|
139
|
-
catch (error) {
|
|
140
|
-
if (attempt === 0 && missingDuringOwnerRead(error))
|
|
141
|
-
continue;
|
|
142
|
-
return { status: "corrupt", detail: error.message };
|
|
143
|
-
}
|
|
144
|
-
let inspection;
|
|
145
|
-
try {
|
|
146
|
-
const identity = await options.inspectIdentity(owner.pid);
|
|
147
|
-
inspection = identity === owner.processIdentity
|
|
148
|
-
? {
|
|
149
|
-
status: "owned",
|
|
150
|
-
ownerPid: owner.pid,
|
|
151
|
-
ownerAlive: true,
|
|
152
|
-
detail: `owned by live process ${owner.pid}`,
|
|
153
|
-
}
|
|
154
|
-
: {
|
|
155
|
-
status: "stale",
|
|
156
|
-
ownerPid: owner.pid,
|
|
157
|
-
ownerAlive: false,
|
|
158
|
-
detail: `owner process ${owner.pid} is absent or its identity changed`,
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
catch (error) {
|
|
162
|
-
inspection = {
|
|
163
|
-
status: "corrupt",
|
|
164
|
-
ownerPid: owner.pid,
|
|
165
|
-
detail: `owner process cannot be inspected: ${error.message}`,
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
try {
|
|
169
|
-
const confirmedOwner = await parseLockOwner(lockDirectory, names[0], fileSystem);
|
|
170
|
-
const confirmedNames = (await fileSystem.readdir(lockDirectory)).map(String).sort();
|
|
171
|
-
const unchanged = JSON.stringify(confirmedOwner) === JSON.stringify(owner)
|
|
172
|
-
&& JSON.stringify(confirmedNames) === JSON.stringify(names);
|
|
173
|
-
if (!unchanged && attempt === 0)
|
|
174
|
-
continue;
|
|
175
|
-
if (!unchanged)
|
|
176
|
-
return { status: "corrupt", detail: "lock snapshot changed during bounded inspection" };
|
|
177
|
-
return inspection;
|
|
178
|
-
}
|
|
179
|
-
catch (error) {
|
|
180
|
-
if (attempt === 0 && missingDuringOwnerRead(error))
|
|
181
|
-
continue;
|
|
182
|
-
if (missingDuringOwnerRead(error)) {
|
|
183
|
-
return { status: "corrupt", detail: "lock snapshot remained unstable after bounded inspection" };
|
|
184
|
-
}
|
|
185
|
-
return { status: "corrupt", detail: error.message };
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
return { status: "corrupt", detail: "lock snapshot remained unstable after bounded inspection" };
|
|
189
|
-
}
|
|
190
|
-
function processLeaseMap(registry) {
|
|
191
|
-
return registry.leases;
|
|
192
|
-
}
|
|
193
|
-
export function createJournalLease(options) {
|
|
194
|
-
const fileSystem = options.fileSystem ?? defaultJournalLockFileSystem;
|
|
195
|
-
const registry = processLeaseMap(options.registry ?? defaultRegistry);
|
|
196
|
-
const now = options.now ?? (() => new Date());
|
|
197
|
-
const orphanGraceMs = options.orphanGraceMs ?? 30_000;
|
|
198
|
-
const journalPath = resolve(options.directory);
|
|
199
|
-
const lockDirectory = join(options.directory, ".journal.lock");
|
|
200
|
-
let attached = false;
|
|
201
|
-
let status = "open";
|
|
202
|
-
const cleanupReleasedDirectory = async (path) => {
|
|
203
|
-
try {
|
|
204
|
-
await fileSystem.rm(path, { recursive: true, force: true });
|
|
205
|
-
await options.syncDirectory(options.directory);
|
|
206
|
-
}
|
|
207
|
-
catch { /* released tombstones never block ownership or reverse a committed close */ }
|
|
208
|
-
};
|
|
209
|
-
const cleanupReleasedDirectories = async () => {
|
|
210
|
-
let names;
|
|
211
|
-
try {
|
|
212
|
-
names = (await fileSystem.readdir(options.directory)).map(String).sort();
|
|
213
|
-
}
|
|
214
|
-
catch {
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
await Promise.all(names
|
|
218
|
-
.filter((name) => RELEASED_LOCK_PATTERN.test(name))
|
|
219
|
-
.slice(0, MAX_RELEASED_LOCK_CLEANUP)
|
|
220
|
-
.map((name) => cleanupReleasedDirectory(join(options.directory, name))));
|
|
221
|
-
};
|
|
222
|
-
const readOwner = async () => {
|
|
223
|
-
let names;
|
|
224
|
-
try {
|
|
225
|
-
names = (await fileSystem.readdir(lockDirectory)).map(String).sort();
|
|
226
|
-
}
|
|
227
|
-
catch (error) {
|
|
228
|
-
if (codeOf(error) === "ENOENT")
|
|
229
|
-
return null;
|
|
230
|
-
throw error;
|
|
231
|
-
}
|
|
232
|
-
if (names.length === 0) {
|
|
233
|
-
const lockStat = await fileSystem.stat(lockDirectory);
|
|
234
|
-
if (now().valueOf() - lockStat.mtimeMs < orphanGraceMs) {
|
|
235
|
-
throw new JournalLockedError("Execution journal lock owner installation is in progress", { journalPath });
|
|
236
|
-
}
|
|
237
|
-
try {
|
|
238
|
-
await fileSystem.rmdir(lockDirectory);
|
|
239
|
-
await options.syncDirectory(options.directory);
|
|
240
|
-
}
|
|
241
|
-
catch (error) {
|
|
242
|
-
if (codeOf(error) !== "ENOENT" && codeOf(error) !== "ENOTEMPTY")
|
|
243
|
-
throw error;
|
|
244
|
-
}
|
|
245
|
-
return null;
|
|
246
|
-
}
|
|
247
|
-
if (names.length !== 1 || !/^owner\.[0-9a-f-]+\.json$/i.test(names[0])) {
|
|
248
|
-
throw new JournalLockCorruptionError(lockDirectory, new Error("lock directory must contain one owner"));
|
|
249
|
-
}
|
|
250
|
-
return { owner: await parseLockOwner(lockDirectory, names[0], fileSystem), fileName: names[0] };
|
|
251
|
-
};
|
|
252
|
-
const validateInstalledOwner = async (lease) => {
|
|
253
|
-
const observed = await readOwner();
|
|
254
|
-
if (observed === null
|
|
255
|
-
|| observed.fileName !== lease.ownerFile
|
|
256
|
-
|| JSON.stringify(observed.owner) !== JSON.stringify(lease.owner)) {
|
|
257
|
-
throw new JournalLockCorruptionError(lockDirectory, new Error("installed owner changed"));
|
|
258
|
-
}
|
|
259
|
-
};
|
|
260
|
-
const finishInstall = async (lease) => {
|
|
261
|
-
if (!lease.ownerInstalled) {
|
|
262
|
-
try {
|
|
263
|
-
await fileSystem.rename(lease.ownerTemp, join(lockDirectory, lease.ownerFile));
|
|
264
|
-
}
|
|
265
|
-
catch (error) {
|
|
266
|
-
if (codeOf(error) !== "ENOENT")
|
|
267
|
-
throw error;
|
|
268
|
-
await validateInstalledOwner(lease);
|
|
269
|
-
}
|
|
270
|
-
lease.ownerInstalled = true;
|
|
271
|
-
}
|
|
272
|
-
await validateInstalledOwner(lease);
|
|
273
|
-
if (!lease.lockDirectorySynced) {
|
|
274
|
-
await options.syncDirectory(lockDirectory);
|
|
275
|
-
lease.lockDirectorySynced = true;
|
|
276
|
-
}
|
|
277
|
-
if (!lease.executionsDirectorySynced) {
|
|
278
|
-
await options.syncDirectory(options.directory);
|
|
279
|
-
lease.executionsDirectorySynced = true;
|
|
280
|
-
}
|
|
281
|
-
await fileSystem.rm(lease.ownerTemp, { force: true }).catch(() => undefined);
|
|
282
|
-
};
|
|
283
|
-
const install = async () => {
|
|
284
|
-
const token = randomUUID();
|
|
285
|
-
const owner = OwnerSchema.parse({
|
|
286
|
-
pid: options.currentPid,
|
|
287
|
-
processIdentity: await options.captureCurrentIdentity(),
|
|
288
|
-
token,
|
|
289
|
-
});
|
|
290
|
-
const ownerTemp = join(options.directory, `.journal-owner.${token}.tmp`);
|
|
291
|
-
await fileSystem.writeDurableFile(ownerTemp, `${JSON.stringify(owner)}\n`);
|
|
292
|
-
try {
|
|
293
|
-
for (;;) {
|
|
294
|
-
try {
|
|
295
|
-
await fileSystem.mkdir(lockDirectory);
|
|
296
|
-
const lease = {
|
|
297
|
-
owner,
|
|
298
|
-
ownerFile: ownerFileName(token),
|
|
299
|
-
ownerTemp,
|
|
300
|
-
refs: 1,
|
|
301
|
-
ownerInstalled: false,
|
|
302
|
-
lockDirectorySynced: false,
|
|
303
|
-
executionsDirectorySynced: false,
|
|
304
|
-
releasing: false,
|
|
305
|
-
};
|
|
306
|
-
registry.set(options.directory, lease);
|
|
307
|
-
attached = true;
|
|
308
|
-
await finishInstall(lease);
|
|
309
|
-
return lease;
|
|
310
|
-
}
|
|
311
|
-
catch (error) {
|
|
312
|
-
if (codeOf(error) !== "EEXIST")
|
|
313
|
-
throw error;
|
|
314
|
-
}
|
|
315
|
-
const observed = await readOwner();
|
|
316
|
-
if (observed === null)
|
|
317
|
-
continue;
|
|
318
|
-
const identity = await options.inspectIdentity(observed.owner.pid);
|
|
319
|
-
if (identity === observed.owner.processIdentity) {
|
|
320
|
-
throw new JournalLockedError(`Execution journal is locked by process ${observed.owner.pid}`, { journalPath, ownerPid: observed.owner.pid });
|
|
321
|
-
}
|
|
322
|
-
await options.hooks?.beforeRemoveObservedOwner?.(observed.owner);
|
|
323
|
-
try {
|
|
324
|
-
await fileSystem.unlink(join(lockDirectory, observed.fileName));
|
|
325
|
-
}
|
|
326
|
-
catch (error) {
|
|
327
|
-
if (codeOf(error) !== "ENOENT")
|
|
328
|
-
throw error;
|
|
329
|
-
continue;
|
|
330
|
-
}
|
|
331
|
-
try {
|
|
332
|
-
await fileSystem.rmdir(lockDirectory);
|
|
333
|
-
await options.syncDirectory(options.directory);
|
|
334
|
-
}
|
|
335
|
-
catch (error) {
|
|
336
|
-
if (codeOf(error) !== "ENOENT" && codeOf(error) !== "ENOTEMPTY")
|
|
337
|
-
throw error;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
finally {
|
|
342
|
-
if (!attached)
|
|
343
|
-
await fileSystem.rm(ownerTemp, { force: true }).catch(() => undefined);
|
|
344
|
-
}
|
|
345
|
-
};
|
|
346
|
-
const acquire = async () => {
|
|
347
|
-
if (status !== "open") {
|
|
348
|
-
throw new JournalLockedError(`Journal lease is ${status}`, { journalPath });
|
|
349
|
-
}
|
|
350
|
-
if (attached) {
|
|
351
|
-
const lease = registry.get(options.directory);
|
|
352
|
-
if (lease === undefined) {
|
|
353
|
-
throw new JournalLockedError("In-process journal lease is missing", { journalPath });
|
|
354
|
-
}
|
|
355
|
-
await finishInstall(lease);
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
358
|
-
const existing = registry.get(options.directory);
|
|
359
|
-
if (existing !== undefined) {
|
|
360
|
-
if (existing.releasing) {
|
|
361
|
-
throw new JournalLockedError("Journal lease release is pending", {
|
|
362
|
-
journalPath,
|
|
363
|
-
ownerPid: existing.owner.pid,
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
await finishInstall(existing);
|
|
367
|
-
existing.refs += 1;
|
|
368
|
-
attached = true;
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
void cleanupReleasedDirectories();
|
|
372
|
-
await install();
|
|
373
|
-
};
|
|
374
|
-
const close = async (closeOptions = {}) => {
|
|
375
|
-
if (status === "closed")
|
|
376
|
-
return;
|
|
377
|
-
const signal = closeOptions.signal;
|
|
378
|
-
throwIfAborted(signal);
|
|
379
|
-
if (!attached) {
|
|
380
|
-
status = "closed";
|
|
381
|
-
return;
|
|
382
|
-
}
|
|
383
|
-
const lease = registry.get(options.directory);
|
|
384
|
-
if (lease === undefined) {
|
|
385
|
-
throw new JournalLockedError("In-process journal lease is missing", { journalPath });
|
|
386
|
-
}
|
|
387
|
-
if (status === "open" && lease.refs > 1) {
|
|
388
|
-
throwIfAborted(signal);
|
|
389
|
-
lease.refs -= 1;
|
|
390
|
-
attached = false;
|
|
391
|
-
status = "closed";
|
|
392
|
-
return;
|
|
393
|
-
}
|
|
394
|
-
status = "closing";
|
|
395
|
-
lease.releasing = true;
|
|
396
|
-
await awaitAbortable((async () => {
|
|
397
|
-
await validateInstalledOwner(lease);
|
|
398
|
-
await options.hooks?.beforeReleaseOwner?.(lease.owner, {
|
|
399
|
-
...(signal === undefined ? {} : { signal }),
|
|
400
|
-
});
|
|
401
|
-
await validateInstalledOwner(lease);
|
|
402
|
-
})(), signal);
|
|
403
|
-
throwIfAborted(signal);
|
|
404
|
-
const releasedDirectory = join(options.directory, releasedLockName(lease.owner.token));
|
|
405
|
-
fileSystem.renameSync(lockDirectory, releasedDirectory);
|
|
406
|
-
lease.refs -= 1;
|
|
407
|
-
registry.delete(options.directory);
|
|
408
|
-
attached = false;
|
|
409
|
-
status = "closed";
|
|
410
|
-
void cleanupReleasedDirectory(releasedDirectory);
|
|
411
|
-
};
|
|
412
|
-
return {
|
|
413
|
-
acquire,
|
|
414
|
-
assertUsable: () => {
|
|
415
|
-
if (status !== "open") {
|
|
416
|
-
throw new JournalLockedError(`Execution journal lease is ${status}`, { journalPath });
|
|
417
|
-
}
|
|
418
|
-
},
|
|
419
|
-
close,
|
|
420
|
-
};
|
|
421
|
-
}
|