@tinycloud/operations 0.6.1-beta.4 → 0.6.1-beta.5
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/dist/artifacts.cjs +263 -56
- package/dist/artifacts.cjs.map +1 -1
- package/dist/artifacts.js +266 -58
- package/dist/artifacts.js.map +1 -1
- package/dist/cli-runtime.cjs +268 -59
- package/dist/cli-runtime.cjs.map +1 -1
- package/dist/cli-runtime.js +271 -61
- package/dist/cli-runtime.js.map +1 -1
- package/dist/delegation-binding.cjs +268 -58
- package/dist/delegation-binding.cjs.map +1 -1
- package/dist/delegation-binding.js +271 -60
- package/dist/delegation-binding.js.map +1 -1
- package/dist/index.cjs +268 -59
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +271 -61
- package/dist/index.js.map +1 -1
- package/dist/profile.cjs +160 -10
- package/dist/profile.cjs.map +1 -1
- package/dist/profile.js +163 -12
- package/dist/profile.js.map +1 -1
- package/dist/state.cjs +277 -58
- package/dist/state.cjs.map +1 -1
- package/dist/state.d.cts +12 -1
- package/dist/state.d.ts +12 -1
- package/dist/state.js +276 -60
- package/dist/state.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -4246,9 +4246,9 @@ var init_authority = __esm({
|
|
|
4246
4246
|
|
|
4247
4247
|
// src/state.ts
|
|
4248
4248
|
function processWideContext(key) {
|
|
4249
|
-
var
|
|
4249
|
+
var _a5;
|
|
4250
4250
|
const registry = globalThis;
|
|
4251
|
-
return registry[
|
|
4251
|
+
return registry[_a5 = Symbol.for(key)] ?? (registry[_a5] = new import_node_async_hooks.AsyncLocalStorage());
|
|
4252
4252
|
}
|
|
4253
4253
|
function processOrigin() {
|
|
4254
4254
|
if (cachedProcessOrigin !== void 0) return cachedProcessOrigin;
|
|
@@ -4419,9 +4419,14 @@ function tinycloudConfigPath() {
|
|
|
4419
4419
|
function profilesPath() {
|
|
4420
4420
|
return (0, import_node_path2.join)(tinycloudHomePath(), "profiles");
|
|
4421
4421
|
}
|
|
4422
|
-
function
|
|
4422
|
+
function namedProfilePath(profile) {
|
|
4423
4423
|
return (0, import_node_path2.join)(profilesPath(), validateProfileName(profile));
|
|
4424
4424
|
}
|
|
4425
|
+
function profilePath(profile) {
|
|
4426
|
+
const named = namedProfilePath(profile);
|
|
4427
|
+
const pinned = pinnedProfiles.getStore()?.get(named);
|
|
4428
|
+
return pinned ? livePinnedPath(profile, pinned) : named;
|
|
4429
|
+
}
|
|
4425
4430
|
function profileConfigPath(profile) {
|
|
4426
4431
|
return (0, import_node_path2.join)(profilePath(profile), "profile.json");
|
|
4427
4432
|
}
|
|
@@ -4438,10 +4443,12 @@ function profileStoreMetadataPath(profile, store) {
|
|
|
4438
4443
|
return `${profileStorePath(profile, store)}.metadata.json`;
|
|
4439
4444
|
}
|
|
4440
4445
|
function profileLockPath(profile) {
|
|
4441
|
-
return (0, import_node_path2.join)(
|
|
4446
|
+
return (0, import_node_path2.join)(namedProfilePath(profile), ".lock");
|
|
4442
4447
|
}
|
|
4443
4448
|
function profileTurnLockPath(profile) {
|
|
4444
|
-
|
|
4449
|
+
const home = tinycloudHomePath();
|
|
4450
|
+
const pinned = pinnedHomes.getStore()?.get(home);
|
|
4451
|
+
return (0, import_node_path2.join)(pinned ? livePinnedPath(profile, pinned) : home, "profile-locks", validateProfileName(profile));
|
|
4445
4452
|
}
|
|
4446
4453
|
async function readJson(filePath) {
|
|
4447
4454
|
try {
|
|
@@ -4451,6 +4458,44 @@ async function readJson(filePath) {
|
|
|
4451
4458
|
throw error;
|
|
4452
4459
|
}
|
|
4453
4460
|
}
|
|
4461
|
+
async function readProfileJson(profile, filePath) {
|
|
4462
|
+
const before = process.platform !== "linux" ? await (0, import_promises.lstat)(filePath, { bigint: true }).catch((error) => {
|
|
4463
|
+
if (error.code === "ENOENT") return null;
|
|
4464
|
+
throw error;
|
|
4465
|
+
}) : null;
|
|
4466
|
+
if (process.platform !== "linux" && !before) return null;
|
|
4467
|
+
if (before && !before.isFile()) throw new ProfileSymlinkError(profile);
|
|
4468
|
+
const dev = before && exactFileId(before.dev);
|
|
4469
|
+
const ino = before && exactFileId(before.ino);
|
|
4470
|
+
try {
|
|
4471
|
+
const handle = await (0, import_promises.open)(filePath, import_node_fs.constants.O_RDONLY | (import_node_fs.constants.O_NOFOLLOW ?? 0));
|
|
4472
|
+
try {
|
|
4473
|
+
if (before) {
|
|
4474
|
+
const opened = await handle.stat({ bigint: true });
|
|
4475
|
+
if (!opened.isFile() || dev === null || ino === null || dev !== exactFileId(opened.dev) || ino !== exactFileId(opened.ino)) {
|
|
4476
|
+
throw new ProfileSymlinkError(profile, "changed while opening its file");
|
|
4477
|
+
}
|
|
4478
|
+
}
|
|
4479
|
+
const contents = await handle.readFile("utf8");
|
|
4480
|
+
if (before) {
|
|
4481
|
+
const named = await (0, import_promises.lstat)(filePath, { bigint: true }).catch((error) => {
|
|
4482
|
+
if (error.code === "ENOENT") return null;
|
|
4483
|
+
throw error;
|
|
4484
|
+
});
|
|
4485
|
+
if (!named?.isFile() || dev !== exactFileId(named.dev) || ino !== exactFileId(named.ino)) {
|
|
4486
|
+
throw new ProfileSymlinkError(profile, "changed while reading its file");
|
|
4487
|
+
}
|
|
4488
|
+
}
|
|
4489
|
+
return JSON.parse(contents);
|
|
4490
|
+
} finally {
|
|
4491
|
+
await handle.close();
|
|
4492
|
+
}
|
|
4493
|
+
} catch (error) {
|
|
4494
|
+
if (isErrno(error, "ENOENT")) return null;
|
|
4495
|
+
if (isErrno(error, "ELOOP") || isErrno(error, "ENOTDIR")) throw new ProfileSymlinkError(profile);
|
|
4496
|
+
throw error;
|
|
4497
|
+
}
|
|
4498
|
+
}
|
|
4454
4499
|
async function writeJsonAtomic(filePath, value) {
|
|
4455
4500
|
await replaceJson(filePath, value, false);
|
|
4456
4501
|
}
|
|
@@ -4484,11 +4529,14 @@ async function writeFileDurably(path, contents) {
|
|
|
4484
4529
|
}
|
|
4485
4530
|
}
|
|
4486
4531
|
async function readProfile(profile) {
|
|
4487
|
-
return
|
|
4532
|
+
return withPinnedProfileDirectory(profile, (directory) => directory ? readProfileJson(profile, (0, import_node_path2.join)(directory, "profile.json")) : Promise.resolve(null));
|
|
4488
4533
|
}
|
|
4489
4534
|
async function readSession(profile) {
|
|
4490
|
-
|
|
4491
|
-
|
|
4535
|
+
return withPinnedProfileDirectory(profile, async (directory) => {
|
|
4536
|
+
if (!directory) return null;
|
|
4537
|
+
await readStoreMetadata(profile, "session");
|
|
4538
|
+
return readProfileJson(profile, sessionPath(profile));
|
|
4539
|
+
});
|
|
4492
4540
|
}
|
|
4493
4541
|
async function readAdditionalDelegations(profile) {
|
|
4494
4542
|
return (await readProfileStore(profile, "additional-delegations")).records;
|
|
@@ -4497,42 +4545,186 @@ async function readAuthRequests(profile) {
|
|
|
4497
4545
|
return (await readProfileStore(profile, "auth-requests")).records;
|
|
4498
4546
|
}
|
|
4499
4547
|
async function readStoreMetadata(profile, store) {
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4548
|
+
return withPinnedProfileDirectory(profile, async (directory) => {
|
|
4549
|
+
const metadata = directory ? await readProfileJson(profile, profileStoreMetadataPath(profile, store)) : null;
|
|
4550
|
+
if (metadata === null) return { formatVersion: 1 };
|
|
4551
|
+
if (typeof metadata !== "object" || metadata.formatVersion !== 1) {
|
|
4552
|
+
throw new TypeError(`Unsupported store format for "${store}".`);
|
|
4553
|
+
}
|
|
4554
|
+
return metadata;
|
|
4555
|
+
});
|
|
4506
4556
|
}
|
|
4507
4557
|
async function readProfileStore(profile, store) {
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4558
|
+
return withPinnedProfileDirectory(profile, async (directory) => {
|
|
4559
|
+
if (!directory) return { formatVersion: 1, records: [] };
|
|
4560
|
+
const [metadata, raw] = await Promise.all([
|
|
4561
|
+
readStoreMetadata(profile, store),
|
|
4562
|
+
readProfileJson(profile, profileStorePath(profile, store))
|
|
4563
|
+
]);
|
|
4564
|
+
return {
|
|
4565
|
+
formatVersion: metadata.formatVersion,
|
|
4566
|
+
records: Array.isArray(raw) ? raw : []
|
|
4567
|
+
};
|
|
4568
|
+
});
|
|
4516
4569
|
}
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
}
|
|
4570
|
+
function exactFileId(value) {
|
|
4571
|
+
if (typeof value === "bigint") return value >= 0n ? value : null;
|
|
4572
|
+
return Number.isSafeInteger(value) && value >= 0 ? BigInt(value) : null;
|
|
4573
|
+
}
|
|
4574
|
+
function isPinnedLive(pinned) {
|
|
4575
|
+
if (!pinned.active) return false;
|
|
4576
|
+
try {
|
|
4577
|
+
const stats = (0, import_node_fs.fstatSync)(pinned.fd, { bigint: true });
|
|
4578
|
+
return exactFileId(stats.dev) === pinned.dev && exactFileId(stats.ino) === pinned.ino;
|
|
4579
|
+
} catch {
|
|
4580
|
+
return false;
|
|
4581
|
+
}
|
|
4582
|
+
}
|
|
4583
|
+
function livePinnedPath(profile, pinned) {
|
|
4584
|
+
if (isPinnedLive(pinned)) return pinned.path;
|
|
4585
|
+
throw new ProfileSymlinkError(profile, "its pinned directory is no longer active or changed during use");
|
|
4586
|
+
}
|
|
4587
|
+
async function withPinnedProfileDirectory(profile, action, options = {}) {
|
|
4588
|
+
const named = namedProfilePath(profile);
|
|
4589
|
+
const existing = pinnedProfiles.getStore()?.get(named);
|
|
4590
|
+
if (existing) return action(livePinnedPath(profile, existing));
|
|
4591
|
+
if (process.platform !== "linux") {
|
|
4592
|
+
if (options.create) await (0, import_promises.mkdir)(named, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
4593
|
+
const stats = await (0, import_promises.lstat)(named).catch((error) => {
|
|
4594
|
+
if (error.code === "ENOENT") return null;
|
|
4595
|
+
throw error;
|
|
4596
|
+
});
|
|
4597
|
+
if (!stats) return action(null);
|
|
4598
|
+
if (!stats.isDirectory() || stats.isSymbolicLink()) throw new ProfileSymlinkError(profile);
|
|
4599
|
+
return action(named);
|
|
4600
|
+
}
|
|
4601
|
+
const home = tinycloudHomePath();
|
|
4602
|
+
const handles = [];
|
|
4603
|
+
const fdPath = (fd) => `/proc/self/fd/${fd}`;
|
|
4604
|
+
const flags = import_node_fs.constants.O_RDONLY | import_node_fs.constants.O_DIRECTORY | import_node_fs.constants.O_NOFOLLOW;
|
|
4529
4605
|
try {
|
|
4530
|
-
|
|
4606
|
+
if (options.create) await (0, import_promises.mkdir)(home, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
4607
|
+
const resolved = canonicalHomes.get(home) ?? await (0, import_promises.realpath)(home).catch((error) => {
|
|
4608
|
+
if (error.code === "ENOENT" && !options.create) return null;
|
|
4609
|
+
if (error.code === "ELOOP" || error.code === "ENOTDIR") throw new ProfileSymlinkError(profile);
|
|
4610
|
+
throw error;
|
|
4611
|
+
});
|
|
4612
|
+
if (!resolved) return await action(null);
|
|
4613
|
+
const root = canonicalHomes.get(home) ?? resolved;
|
|
4614
|
+
canonicalHomes.set(home, root);
|
|
4615
|
+
const segments = [root, "profiles", validateProfileName(profile)];
|
|
4616
|
+
for (let index = 0; index < segments.length; index++) {
|
|
4617
|
+
const path = handles.length ? (0, import_node_path2.join)(fdPath(handles[handles.length - 1].fd), segments[index]) : segments[index];
|
|
4618
|
+
if (options.create && handles.length) {
|
|
4619
|
+
await (0, import_promises.mkdir)(path, { mode: PRIVATE_DIR_MODE }).catch((error) => {
|
|
4620
|
+
if (error.code !== "EEXIST") throw error;
|
|
4621
|
+
});
|
|
4622
|
+
}
|
|
4623
|
+
const handle = await (0, import_promises.open)(path, index === 1 ? flags & ~import_node_fs.constants.O_NOFOLLOW : flags).catch((error) => {
|
|
4624
|
+
if (error.code === "ENOENT" && !options.create) return null;
|
|
4625
|
+
if (error.code === "ELOOP" || error.code === "ENOTDIR") throw new ProfileSymlinkError(profile);
|
|
4626
|
+
throw error;
|
|
4627
|
+
});
|
|
4628
|
+
if (!handle) return await action(null);
|
|
4629
|
+
handles.push(handle);
|
|
4630
|
+
if (options.create) await handle.chmod(PRIVATE_DIR_MODE);
|
|
4631
|
+
}
|
|
4632
|
+
const opened = handles[2];
|
|
4633
|
+
const [actual, namedStats] = await Promise.all([opened.stat({ bigint: true }), (0, import_promises.lstat)(named, { bigint: true })]);
|
|
4634
|
+
const dev = exactFileId(actual.dev);
|
|
4635
|
+
const ino = exactFileId(actual.ino);
|
|
4636
|
+
if (!namedStats.isDirectory() || dev === null || ino === null || dev !== exactFileId(namedStats.dev) || ino !== exactFileId(namedStats.ino)) {
|
|
4637
|
+
throw new ProfileSymlinkError(profile, "changed while opening its directory");
|
|
4638
|
+
}
|
|
4639
|
+
const directory = fdPath(opened.fd);
|
|
4640
|
+
const pinned = {
|
|
4641
|
+
path: directory,
|
|
4642
|
+
fd: opened.fd,
|
|
4643
|
+
dev,
|
|
4644
|
+
ino,
|
|
4645
|
+
active: true
|
|
4646
|
+
};
|
|
4647
|
+
const homeHandle = handles[0];
|
|
4648
|
+
const homeStats = await homeHandle.stat({ bigint: true });
|
|
4649
|
+
const homeDev = exactFileId(homeStats.dev);
|
|
4650
|
+
const homeIno = exactFileId(homeStats.ino);
|
|
4651
|
+
if (homeDev === null || homeIno === null) throw new ProfileSymlinkError(profile, "has an unverified home directory identity");
|
|
4652
|
+
const homePinned = {
|
|
4653
|
+
path: fdPath(homeHandle.fd),
|
|
4654
|
+
fd: homeHandle.fd,
|
|
4655
|
+
dev: homeDev,
|
|
4656
|
+
ino: homeIno,
|
|
4657
|
+
active: true
|
|
4658
|
+
};
|
|
4659
|
+
const context = new Map(pinnedProfiles.getStore());
|
|
4660
|
+
context.set(named, pinned);
|
|
4661
|
+
const homes = new Map(pinnedHomes.getStore());
|
|
4662
|
+
homes.set(home, homePinned);
|
|
4663
|
+
try {
|
|
4664
|
+
return await pinnedHomes.run(homes, () => pinnedProfiles.run(context, () => action(directory)));
|
|
4665
|
+
} finally {
|
|
4666
|
+
pinned.active = false;
|
|
4667
|
+
homePinned.active = false;
|
|
4668
|
+
}
|
|
4531
4669
|
} finally {
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4670
|
+
for (const handle of handles.reverse()) await handle.close();
|
|
4671
|
+
}
|
|
4672
|
+
}
|
|
4673
|
+
async function assertRealProfileDirectory(profile) {
|
|
4674
|
+
const named = namedProfilePath(profile);
|
|
4675
|
+
const pinned = pinnedProfiles.getStore()?.get(named);
|
|
4676
|
+
if (pinned) {
|
|
4677
|
+
livePinnedPath(profile, pinned);
|
|
4678
|
+
return;
|
|
4535
4679
|
}
|
|
4680
|
+
const stats = await (0, import_promises.lstat)(named).catch((error) => {
|
|
4681
|
+
if (error.code === "ENOENT") return null;
|
|
4682
|
+
throw error;
|
|
4683
|
+
});
|
|
4684
|
+
if (stats && (!stats.isDirectory() || stats.isSymbolicLink())) throw new ProfileSymlinkError(profile);
|
|
4685
|
+
}
|
|
4686
|
+
async function withProfileLock(profile, action, options = {}) {
|
|
4687
|
+
const normalizedProfile = validateProfileName(profile);
|
|
4688
|
+
const lockPath = profileLockPath(normalizedProfile);
|
|
4689
|
+
const held = (heldProfileLocks.getStore() ?? []).filter((ownership) => ownership.active);
|
|
4690
|
+
if (held.some((ownership) => ownership.lockPath === lockPath)) {
|
|
4691
|
+
await assertRealProfileDirectory(normalizedProfile);
|
|
4692
|
+
return action();
|
|
4693
|
+
}
|
|
4694
|
+
const named = namedProfilePath(normalizedProfile);
|
|
4695
|
+
const inherited = pinnedProfiles.getStore()?.get(named);
|
|
4696
|
+
if (inherited && !isPinnedLive(inherited)) {
|
|
4697
|
+
const profiles = new Map(pinnedProfiles.getStore());
|
|
4698
|
+
profiles.delete(named);
|
|
4699
|
+
const homes = new Map(pinnedHomes.getStore());
|
|
4700
|
+
homes.delete(tinycloudHomePath());
|
|
4701
|
+
return pinnedHomes.run(homes, () => pinnedProfiles.run(profiles, () => withProfileLock(normalizedProfile, action, options)));
|
|
4702
|
+
}
|
|
4703
|
+
return withPinnedProfileDirectory(normalizedProfile, async (directory) => {
|
|
4704
|
+
if (!directory) throw new ProfileDeletedError(normalizedProfile);
|
|
4705
|
+
const acquired = await acquireProfileLock(normalizedProfile, options);
|
|
4706
|
+
const ownership = {
|
|
4707
|
+
lockPath,
|
|
4708
|
+
turnSlot: acquired.turnSlot,
|
|
4709
|
+
deletedWhileWaiting: acquired.deletedWhileWaiting,
|
|
4710
|
+
active: true
|
|
4711
|
+
};
|
|
4712
|
+
try {
|
|
4713
|
+
return await heldProfileLocks.run([...held, ownership], action);
|
|
4714
|
+
} finally {
|
|
4715
|
+
ownership.active = false;
|
|
4716
|
+
await acquired.release();
|
|
4717
|
+
if (ownership.deletedProfile || acquired.deletedWhileWaiting) {
|
|
4718
|
+
const current = await (0, import_promises.lstat)(namedProfilePath(normalizedProfile), { bigint: true }).catch(() => null);
|
|
4719
|
+
const original = await (0, import_promises.stat)(directory, { bigint: true }).catch(() => null);
|
|
4720
|
+
const currentDev = current && exactFileId(current.dev);
|
|
4721
|
+
const currentIno = current && exactFileId(current.ino);
|
|
4722
|
+
if (currentDev !== null && currentIno !== null && original && currentDev === exactFileId(original.dev) && currentIno === exactFileId(original.ino)) {
|
|
4723
|
+
await (0, import_promises.rmdir)(namedProfilePath(normalizedProfile)).catch(() => void 0);
|
|
4724
|
+
}
|
|
4725
|
+
}
|
|
4726
|
+
}
|
|
4727
|
+
}, { create: !options.requireProfile });
|
|
4536
4728
|
}
|
|
4537
4729
|
async function refuseWriteToDeletedProfile(profile) {
|
|
4538
4730
|
const lockPath = profileLockPath(profile);
|
|
@@ -4566,17 +4758,15 @@ async function acquireProfileLock(profile, options) {
|
|
|
4566
4758
|
retryMs: options.retryMs ?? DEFAULT_LOCK_RETRY_MS
|
|
4567
4759
|
};
|
|
4568
4760
|
const staleAfterMs = options.staleAfterMs ?? DEFAULT_STALE_LOCK_MS;
|
|
4569
|
-
const lockPath =
|
|
4761
|
+
const lockPath = (0, import_node_path2.join)(profilePath(profile), ".lock");
|
|
4570
4762
|
const turnDirectory = profileTurnLockPath(profile);
|
|
4571
4763
|
const directory = profilePath(profile);
|
|
4764
|
+
await assertRealProfileDirectory(profile);
|
|
4572
4765
|
if (options.requireProfile && !await exists(profileConfigPath(profile))) throw new ProfileDeletedError(profile);
|
|
4573
4766
|
if (!options.requireProfile) await (0, import_promises.mkdir)(directory, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
4767
|
+
await assertRealProfileDirectory(profile);
|
|
4574
4768
|
await (0, import_promises.mkdir)((0, import_node_path2.dirname)(turnDirectory), { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
4575
|
-
|
|
4576
|
-
await (0, import_promises.chmod)(path, PRIVATE_DIR_MODE).catch((error) => {
|
|
4577
|
-
if (path !== directory || !isErrno(error, "ENOENT")) throw error;
|
|
4578
|
-
});
|
|
4579
|
-
}
|
|
4769
|
+
await (0, import_promises.chmod)(directory, PRIVATE_DIR_MODE);
|
|
4580
4770
|
const liveness = { directory: turnDirectory, held: void 0 };
|
|
4581
4771
|
const closeAcquisitionLiveness = async () => {
|
|
4582
4772
|
if (liveness.held) await closeLiveness(liveness.held);
|
|
@@ -4596,6 +4786,7 @@ async function acquireProfileLock(profile, options) {
|
|
|
4596
4786
|
}
|
|
4597
4787
|
const deletion = await readJson((0, import_node_path2.join)(turnDirectory, "deleted.json")).catch(() => null);
|
|
4598
4788
|
const deletedWhileWaiting = typeof deletion?.slot === "number" && deletion.slot >= firstWaitedTurn;
|
|
4789
|
+
if (deletedWhileWaiting && !await exists(profileConfigPath(profile))) throw new ProfileDeletedError(profile);
|
|
4599
4790
|
while (true) {
|
|
4600
4791
|
const token = (0, import_node_crypto.randomUUID)();
|
|
4601
4792
|
if (await publishProfileLock(profile, lockPath, token, options.requireProfile === true, liveness.held ?? null)) {
|
|
@@ -4741,7 +4932,7 @@ async function takeTurn(profile, directory, latest, liveness) {
|
|
|
4741
4932
|
if (published) await markTurn(path, token, "done");
|
|
4742
4933
|
return null;
|
|
4743
4934
|
} catch (error) {
|
|
4744
|
-
if (published) settleOwnTurn(directory, slot, token);
|
|
4935
|
+
if (published) await settleOwnTurn(directory, slot, token);
|
|
4745
4936
|
throw error;
|
|
4746
4937
|
}
|
|
4747
4938
|
}
|
|
@@ -4752,17 +4943,25 @@ async function settleAbandonedTurn(directory, abandoned) {
|
|
|
4752
4943
|
}
|
|
4753
4944
|
await markTurn(path, abandoned.token, "done");
|
|
4754
4945
|
}
|
|
4755
|
-
function settleOwnTurn(directory, slot, token
|
|
4946
|
+
async function settleOwnTurn(directory, slot, token) {
|
|
4756
4947
|
relinquishedTurns.add(token);
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4948
|
+
const pinned = await pinDirectory(directory).catch(() => null);
|
|
4949
|
+
const path = pinned?.path(".") ?? directory;
|
|
4950
|
+
let retries = SETTLE_RETRIES;
|
|
4951
|
+
const attempt = () => {
|
|
4952
|
+
void (async () => {
|
|
4953
|
+
try {
|
|
4954
|
+
const turn = await readTurn(path, slot);
|
|
4955
|
+
if (turn !== null && turn !== "damaged" && turn.token === token && !turn.done) await settleAbandonedTurn(path, turn);
|
|
4956
|
+
relinquishedTurns.delete(token);
|
|
4957
|
+
await pinned?.close();
|
|
4958
|
+
} catch {
|
|
4959
|
+
if (retries-- > 0) setTimeout(attempt, SETTLE_RETRY_MS).unref();
|
|
4960
|
+
else await pinned?.close();
|
|
4961
|
+
}
|
|
4962
|
+
})();
|
|
4963
|
+
};
|
|
4964
|
+
attempt();
|
|
4766
4965
|
}
|
|
4767
4966
|
async function markTurn(path, token, kind) {
|
|
4768
4967
|
for (let attempt = 0; ; attempt++) {
|
|
@@ -4807,7 +5006,7 @@ async function releaseTurn(profile, turn, liveness) {
|
|
|
4807
5006
|
try {
|
|
4808
5007
|
await markTurn((0, import_node_path2.join)(turn.directory, String(turn.slot)), turn.token, "done");
|
|
4809
5008
|
} catch {
|
|
4810
|
-
settleOwnTurn(turn.directory, turn.slot, turn.token);
|
|
5009
|
+
await settleOwnTurn(turn.directory, turn.slot, turn.token);
|
|
4811
5010
|
return;
|
|
4812
5011
|
}
|
|
4813
5012
|
await collectTurns(profile, turn, liveness).catch(() => void 0);
|
|
@@ -5091,7 +5290,7 @@ function isErrno(error, code) {
|
|
|
5091
5290
|
function sleep(milliseconds) {
|
|
5092
5291
|
return new Promise((resolve2) => setTimeout(resolve2, milliseconds));
|
|
5093
5292
|
}
|
|
5094
|
-
var import_node_crypto, import_node_async_hooks, import_node_fs, import_promises, import_node_net, import_node_os, import_node_path2, DEFAULT_PROFILE, DEFAULT_LOCK_TIMEOUT_MS, DEFAULT_LOCK_RETRY_MS, DEFAULT_STALE_LOCK_MS, TEST_LOCK_CONTENTION_SIGNAL_PATH, PRIVATE_FILE_MODE, PRIVATE_DIR_MODE, TEST_LOCK_RECOVERY_BARRIER_DIR, TEST_LOCK_OWNERLESS_BARRIER_DIR, TEST_LOCK_PUBLISH_BARRIER_DIR, TEST_LOCK_RELEASE_BARRIER_DIR, TEST_LOCK_CLAIM_BARRIER_DIR, TEST_LOCK_CLAIMED_BARRIER_DIR, TEST_LOCK_VERIFIED_BARRIER_DIR, TEST_LOCK_MOVED_BARRIER_DIR, TEST_LOCK_TURN_PUBLISH_BARRIER_DIR, TEST_LOCK_TURN_SETTLE_BARRIER_DIR, TEST_LOCK_TURN_COLLECT_BARRIER_DIR, TEST_LOCK_LIVENESS_BARRIER_DIR, invocationStateRoot, heldProfileLocks, _a2, processInstanceId, cachedProcessOrigin, LIVENESS_SOCKET, LIVENESS_PROBE_TIMEOUT_MS, ProfileLockTimeoutError, ProfileDeletedError, TURN,
|
|
5293
|
+
var import_node_crypto, import_node_async_hooks, import_node_fs, import_promises, import_node_net, import_node_os, import_node_path2, DEFAULT_PROFILE, DEFAULT_LOCK_TIMEOUT_MS, DEFAULT_LOCK_RETRY_MS, DEFAULT_STALE_LOCK_MS, TEST_LOCK_CONTENTION_SIGNAL_PATH, PRIVATE_FILE_MODE, PRIVATE_DIR_MODE, TEST_LOCK_RECOVERY_BARRIER_DIR, TEST_LOCK_OWNERLESS_BARRIER_DIR, TEST_LOCK_PUBLISH_BARRIER_DIR, TEST_LOCK_RELEASE_BARRIER_DIR, TEST_LOCK_CLAIM_BARRIER_DIR, TEST_LOCK_CLAIMED_BARRIER_DIR, TEST_LOCK_VERIFIED_BARRIER_DIR, TEST_LOCK_MOVED_BARRIER_DIR, TEST_LOCK_TURN_PUBLISH_BARRIER_DIR, TEST_LOCK_TURN_SETTLE_BARRIER_DIR, TEST_LOCK_TURN_COLLECT_BARRIER_DIR, TEST_LOCK_LIVENESS_BARRIER_DIR, invocationStateRoot, heldProfileLocks, pinnedProfiles, pinnedHomes, _a2, canonicalHomes, _a3, processInstanceId, cachedProcessOrigin, LIVENESS_SOCKET, LIVENESS_PROBE_TIMEOUT_MS, ProfileLockTimeoutError, ProfileDeletedError, ProfileSymlinkError, TURN, _a4, relinquishedTurns, MARKER_RETRY_DELAYS_MS, SETTLE_RETRY_MS, SETTLE_RETRIES, NO_HARD_LINK_ERRORS, ABANDONED_CLAIM, RECOVERY_CLAIM, STAGED_OWNER;
|
|
5095
5294
|
var init_state = __esm({
|
|
5096
5295
|
"src/state.ts"() {
|
|
5097
5296
|
"use strict";
|
|
@@ -5123,7 +5322,10 @@ var init_state = __esm({
|
|
|
5123
5322
|
TEST_LOCK_LIVENESS_BARRIER_DIR = "TC_TEST_PROFILE_LOCK_LIVENESS_BARRIER_DIR";
|
|
5124
5323
|
invocationStateRoot = processWideContext("tinycloud.operations.invocationStateRoot.v1");
|
|
5125
5324
|
heldProfileLocks = processWideContext("tinycloud.operations.heldProfileLocks.v2");
|
|
5126
|
-
|
|
5325
|
+
pinnedProfiles = processWideContext("tinycloud.operations.pinnedProfiles.v2");
|
|
5326
|
+
pinnedHomes = processWideContext("tinycloud.operations.pinnedHomes.v2");
|
|
5327
|
+
canonicalHomes = globalThis[_a2 = /* @__PURE__ */ Symbol.for("tinycloud.operations.canonicalHomes.v1")] ?? (globalThis[_a2] = /* @__PURE__ */ new Map());
|
|
5328
|
+
processInstanceId = globalThis[_a3 = /* @__PURE__ */ Symbol.for("tinycloud.operations.profileLockInstance.v1")] ?? (globalThis[_a3] = (0, import_node_crypto.randomUUID)());
|
|
5127
5329
|
LIVENESS_SOCKET = /^\.live-([0-9a-f]+)-([0-9a-f]+)-[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}(\.new)?$/;
|
|
5128
5330
|
LIVENESS_PROBE_TIMEOUT_MS = 1e3;
|
|
5129
5331
|
ProfileLockTimeoutError = class extends Error {
|
|
@@ -5141,8 +5343,15 @@ var init_state = __esm({
|
|
|
5141
5343
|
this.name = "ProfileDeletedError";
|
|
5142
5344
|
}
|
|
5143
5345
|
};
|
|
5346
|
+
ProfileSymlinkError = class extends Error {
|
|
5347
|
+
constructor(profile, detail = "must be a real directory, not a symlink or another file type") {
|
|
5348
|
+
super(`Profile "${profile}" ${detail}.`);
|
|
5349
|
+
this.code = "PROFILE_SYMLINK";
|
|
5350
|
+
this.name = "ProfileSymlinkError";
|
|
5351
|
+
}
|
|
5352
|
+
};
|
|
5144
5353
|
TURN = /^(?:0|[1-9][0-9]*)$/;
|
|
5145
|
-
relinquishedTurns = globalThis[
|
|
5354
|
+
relinquishedTurns = globalThis[_a4 = /* @__PURE__ */ Symbol.for("tinycloud.operations.relinquishedProfileTurns.v1")] ?? (globalThis[_a4] = /* @__PURE__ */ new Set());
|
|
5146
5355
|
MARKER_RETRY_DELAYS_MS = [10, 50, 250];
|
|
5147
5356
|
SETTLE_RETRY_MS = 1e3;
|
|
5148
5357
|
SETTLE_RETRIES = 300;
|