@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/cli-runtime.js
CHANGED
|
@@ -4245,15 +4245,16 @@ var init_authority = __esm({
|
|
|
4245
4245
|
// src/state.ts
|
|
4246
4246
|
import { randomUUID } from "crypto";
|
|
4247
4247
|
import { AsyncLocalStorage } from "async_hooks";
|
|
4248
|
-
import { readFileSync, readlinkSync } from "fs";
|
|
4248
|
+
import { constants as fsConstants, fstatSync, readFileSync, readlinkSync } from "fs";
|
|
4249
4249
|
import {
|
|
4250
4250
|
chmod,
|
|
4251
4251
|
link,
|
|
4252
|
-
lstat,
|
|
4253
4252
|
mkdir,
|
|
4253
|
+
lstat,
|
|
4254
4254
|
open,
|
|
4255
4255
|
readFile,
|
|
4256
4256
|
readdir,
|
|
4257
|
+
realpath,
|
|
4257
4258
|
rename,
|
|
4258
4259
|
rmdir,
|
|
4259
4260
|
rm,
|
|
@@ -4265,9 +4266,9 @@ import { connect, createServer } from "net";
|
|
|
4265
4266
|
import { homedir } from "os";
|
|
4266
4267
|
import { basename as basename2, dirname, isAbsolute, join, relative, resolve } from "path";
|
|
4267
4268
|
function processWideContext(key) {
|
|
4268
|
-
var
|
|
4269
|
+
var _a5;
|
|
4269
4270
|
const registry = globalThis;
|
|
4270
|
-
return registry[
|
|
4271
|
+
return registry[_a5 = Symbol.for(key)] ?? (registry[_a5] = new AsyncLocalStorage());
|
|
4271
4272
|
}
|
|
4272
4273
|
function processOrigin() {
|
|
4273
4274
|
if (cachedProcessOrigin !== void 0) return cachedProcessOrigin;
|
|
@@ -4438,9 +4439,14 @@ function tinycloudConfigPath() {
|
|
|
4438
4439
|
function profilesPath() {
|
|
4439
4440
|
return join(tinycloudHomePath(), "profiles");
|
|
4440
4441
|
}
|
|
4441
|
-
function
|
|
4442
|
+
function namedProfilePath(profile) {
|
|
4442
4443
|
return join(profilesPath(), validateProfileName(profile));
|
|
4443
4444
|
}
|
|
4445
|
+
function profilePath(profile) {
|
|
4446
|
+
const named = namedProfilePath(profile);
|
|
4447
|
+
const pinned = pinnedProfiles.getStore()?.get(named);
|
|
4448
|
+
return pinned ? livePinnedPath(profile, pinned) : named;
|
|
4449
|
+
}
|
|
4444
4450
|
function profileConfigPath(profile) {
|
|
4445
4451
|
return join(profilePath(profile), "profile.json");
|
|
4446
4452
|
}
|
|
@@ -4457,10 +4463,12 @@ function profileStoreMetadataPath(profile, store) {
|
|
|
4457
4463
|
return `${profileStorePath(profile, store)}.metadata.json`;
|
|
4458
4464
|
}
|
|
4459
4465
|
function profileLockPath(profile) {
|
|
4460
|
-
return join(
|
|
4466
|
+
return join(namedProfilePath(profile), ".lock");
|
|
4461
4467
|
}
|
|
4462
4468
|
function profileTurnLockPath(profile) {
|
|
4463
|
-
|
|
4469
|
+
const home = tinycloudHomePath();
|
|
4470
|
+
const pinned = pinnedHomes.getStore()?.get(home);
|
|
4471
|
+
return join(pinned ? livePinnedPath(profile, pinned) : home, "profile-locks", validateProfileName(profile));
|
|
4464
4472
|
}
|
|
4465
4473
|
async function readJson(filePath) {
|
|
4466
4474
|
try {
|
|
@@ -4470,6 +4478,44 @@ async function readJson(filePath) {
|
|
|
4470
4478
|
throw error;
|
|
4471
4479
|
}
|
|
4472
4480
|
}
|
|
4481
|
+
async function readProfileJson(profile, filePath) {
|
|
4482
|
+
const before = process.platform !== "linux" ? await lstat(filePath, { bigint: true }).catch((error) => {
|
|
4483
|
+
if (error.code === "ENOENT") return null;
|
|
4484
|
+
throw error;
|
|
4485
|
+
}) : null;
|
|
4486
|
+
if (process.platform !== "linux" && !before) return null;
|
|
4487
|
+
if (before && !before.isFile()) throw new ProfileSymlinkError(profile);
|
|
4488
|
+
const dev = before && exactFileId(before.dev);
|
|
4489
|
+
const ino = before && exactFileId(before.ino);
|
|
4490
|
+
try {
|
|
4491
|
+
const handle = await open(filePath, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0));
|
|
4492
|
+
try {
|
|
4493
|
+
if (before) {
|
|
4494
|
+
const opened = await handle.stat({ bigint: true });
|
|
4495
|
+
if (!opened.isFile() || dev === null || ino === null || dev !== exactFileId(opened.dev) || ino !== exactFileId(opened.ino)) {
|
|
4496
|
+
throw new ProfileSymlinkError(profile, "changed while opening its file");
|
|
4497
|
+
}
|
|
4498
|
+
}
|
|
4499
|
+
const contents = await handle.readFile("utf8");
|
|
4500
|
+
if (before) {
|
|
4501
|
+
const named = await lstat(filePath, { bigint: true }).catch((error) => {
|
|
4502
|
+
if (error.code === "ENOENT") return null;
|
|
4503
|
+
throw error;
|
|
4504
|
+
});
|
|
4505
|
+
if (!named?.isFile() || dev !== exactFileId(named.dev) || ino !== exactFileId(named.ino)) {
|
|
4506
|
+
throw new ProfileSymlinkError(profile, "changed while reading its file");
|
|
4507
|
+
}
|
|
4508
|
+
}
|
|
4509
|
+
return JSON.parse(contents);
|
|
4510
|
+
} finally {
|
|
4511
|
+
await handle.close();
|
|
4512
|
+
}
|
|
4513
|
+
} catch (error) {
|
|
4514
|
+
if (isErrno(error, "ENOENT")) return null;
|
|
4515
|
+
if (isErrno(error, "ELOOP") || isErrno(error, "ENOTDIR")) throw new ProfileSymlinkError(profile);
|
|
4516
|
+
throw error;
|
|
4517
|
+
}
|
|
4518
|
+
}
|
|
4473
4519
|
async function writeJsonAtomic(filePath, value) {
|
|
4474
4520
|
await replaceJson(filePath, value, false);
|
|
4475
4521
|
}
|
|
@@ -4503,11 +4549,14 @@ async function writeFileDurably(path, contents) {
|
|
|
4503
4549
|
}
|
|
4504
4550
|
}
|
|
4505
4551
|
async function readProfile(profile) {
|
|
4506
|
-
return
|
|
4552
|
+
return withPinnedProfileDirectory(profile, (directory) => directory ? readProfileJson(profile, join(directory, "profile.json")) : Promise.resolve(null));
|
|
4507
4553
|
}
|
|
4508
4554
|
async function readSession(profile) {
|
|
4509
|
-
|
|
4510
|
-
|
|
4555
|
+
return withPinnedProfileDirectory(profile, async (directory) => {
|
|
4556
|
+
if (!directory) return null;
|
|
4557
|
+
await readStoreMetadata(profile, "session");
|
|
4558
|
+
return readProfileJson(profile, sessionPath(profile));
|
|
4559
|
+
});
|
|
4511
4560
|
}
|
|
4512
4561
|
async function readAdditionalDelegations(profile) {
|
|
4513
4562
|
return (await readProfileStore(profile, "additional-delegations")).records;
|
|
@@ -4516,42 +4565,186 @@ async function readAuthRequests(profile) {
|
|
|
4516
4565
|
return (await readProfileStore(profile, "auth-requests")).records;
|
|
4517
4566
|
}
|
|
4518
4567
|
async function readStoreMetadata(profile, store) {
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4568
|
+
return withPinnedProfileDirectory(profile, async (directory) => {
|
|
4569
|
+
const metadata = directory ? await readProfileJson(profile, profileStoreMetadataPath(profile, store)) : null;
|
|
4570
|
+
if (metadata === null) return { formatVersion: 1 };
|
|
4571
|
+
if (typeof metadata !== "object" || metadata.formatVersion !== 1) {
|
|
4572
|
+
throw new TypeError(`Unsupported store format for "${store}".`);
|
|
4573
|
+
}
|
|
4574
|
+
return metadata;
|
|
4575
|
+
});
|
|
4525
4576
|
}
|
|
4526
4577
|
async function readProfileStore(profile, store) {
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4578
|
+
return withPinnedProfileDirectory(profile, async (directory) => {
|
|
4579
|
+
if (!directory) return { formatVersion: 1, records: [] };
|
|
4580
|
+
const [metadata, raw] = await Promise.all([
|
|
4581
|
+
readStoreMetadata(profile, store),
|
|
4582
|
+
readProfileJson(profile, profileStorePath(profile, store))
|
|
4583
|
+
]);
|
|
4584
|
+
return {
|
|
4585
|
+
formatVersion: metadata.formatVersion,
|
|
4586
|
+
records: Array.isArray(raw) ? raw : []
|
|
4587
|
+
};
|
|
4588
|
+
});
|
|
4535
4589
|
}
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
}
|
|
4590
|
+
function exactFileId(value) {
|
|
4591
|
+
if (typeof value === "bigint") return value >= 0n ? value : null;
|
|
4592
|
+
return Number.isSafeInteger(value) && value >= 0 ? BigInt(value) : null;
|
|
4593
|
+
}
|
|
4594
|
+
function isPinnedLive(pinned) {
|
|
4595
|
+
if (!pinned.active) return false;
|
|
4596
|
+
try {
|
|
4597
|
+
const stats = fstatSync(pinned.fd, { bigint: true });
|
|
4598
|
+
return exactFileId(stats.dev) === pinned.dev && exactFileId(stats.ino) === pinned.ino;
|
|
4599
|
+
} catch {
|
|
4600
|
+
return false;
|
|
4601
|
+
}
|
|
4602
|
+
}
|
|
4603
|
+
function livePinnedPath(profile, pinned) {
|
|
4604
|
+
if (isPinnedLive(pinned)) return pinned.path;
|
|
4605
|
+
throw new ProfileSymlinkError(profile, "its pinned directory is no longer active or changed during use");
|
|
4606
|
+
}
|
|
4607
|
+
async function withPinnedProfileDirectory(profile, action, options = {}) {
|
|
4608
|
+
const named = namedProfilePath(profile);
|
|
4609
|
+
const existing = pinnedProfiles.getStore()?.get(named);
|
|
4610
|
+
if (existing) return action(livePinnedPath(profile, existing));
|
|
4611
|
+
if (process.platform !== "linux") {
|
|
4612
|
+
if (options.create) await mkdir(named, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
4613
|
+
const stats = await lstat(named).catch((error) => {
|
|
4614
|
+
if (error.code === "ENOENT") return null;
|
|
4615
|
+
throw error;
|
|
4616
|
+
});
|
|
4617
|
+
if (!stats) return action(null);
|
|
4618
|
+
if (!stats.isDirectory() || stats.isSymbolicLink()) throw new ProfileSymlinkError(profile);
|
|
4619
|
+
return action(named);
|
|
4620
|
+
}
|
|
4621
|
+
const home = tinycloudHomePath();
|
|
4622
|
+
const handles = [];
|
|
4623
|
+
const fdPath = (fd) => `/proc/self/fd/${fd}`;
|
|
4624
|
+
const flags = fsConstants.O_RDONLY | fsConstants.O_DIRECTORY | fsConstants.O_NOFOLLOW;
|
|
4548
4625
|
try {
|
|
4549
|
-
|
|
4626
|
+
if (options.create) await mkdir(home, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
4627
|
+
const resolved = canonicalHomes.get(home) ?? await realpath(home).catch((error) => {
|
|
4628
|
+
if (error.code === "ENOENT" && !options.create) return null;
|
|
4629
|
+
if (error.code === "ELOOP" || error.code === "ENOTDIR") throw new ProfileSymlinkError(profile);
|
|
4630
|
+
throw error;
|
|
4631
|
+
});
|
|
4632
|
+
if (!resolved) return await action(null);
|
|
4633
|
+
const root = canonicalHomes.get(home) ?? resolved;
|
|
4634
|
+
canonicalHomes.set(home, root);
|
|
4635
|
+
const segments = [root, "profiles", validateProfileName(profile)];
|
|
4636
|
+
for (let index = 0; index < segments.length; index++) {
|
|
4637
|
+
const path = handles.length ? join(fdPath(handles[handles.length - 1].fd), segments[index]) : segments[index];
|
|
4638
|
+
if (options.create && handles.length) {
|
|
4639
|
+
await mkdir(path, { mode: PRIVATE_DIR_MODE }).catch((error) => {
|
|
4640
|
+
if (error.code !== "EEXIST") throw error;
|
|
4641
|
+
});
|
|
4642
|
+
}
|
|
4643
|
+
const handle = await open(path, index === 1 ? flags & ~fsConstants.O_NOFOLLOW : flags).catch((error) => {
|
|
4644
|
+
if (error.code === "ENOENT" && !options.create) return null;
|
|
4645
|
+
if (error.code === "ELOOP" || error.code === "ENOTDIR") throw new ProfileSymlinkError(profile);
|
|
4646
|
+
throw error;
|
|
4647
|
+
});
|
|
4648
|
+
if (!handle) return await action(null);
|
|
4649
|
+
handles.push(handle);
|
|
4650
|
+
if (options.create) await handle.chmod(PRIVATE_DIR_MODE);
|
|
4651
|
+
}
|
|
4652
|
+
const opened = handles[2];
|
|
4653
|
+
const [actual, namedStats] = await Promise.all([opened.stat({ bigint: true }), lstat(named, { bigint: true })]);
|
|
4654
|
+
const dev = exactFileId(actual.dev);
|
|
4655
|
+
const ino = exactFileId(actual.ino);
|
|
4656
|
+
if (!namedStats.isDirectory() || dev === null || ino === null || dev !== exactFileId(namedStats.dev) || ino !== exactFileId(namedStats.ino)) {
|
|
4657
|
+
throw new ProfileSymlinkError(profile, "changed while opening its directory");
|
|
4658
|
+
}
|
|
4659
|
+
const directory = fdPath(opened.fd);
|
|
4660
|
+
const pinned = {
|
|
4661
|
+
path: directory,
|
|
4662
|
+
fd: opened.fd,
|
|
4663
|
+
dev,
|
|
4664
|
+
ino,
|
|
4665
|
+
active: true
|
|
4666
|
+
};
|
|
4667
|
+
const homeHandle = handles[0];
|
|
4668
|
+
const homeStats = await homeHandle.stat({ bigint: true });
|
|
4669
|
+
const homeDev = exactFileId(homeStats.dev);
|
|
4670
|
+
const homeIno = exactFileId(homeStats.ino);
|
|
4671
|
+
if (homeDev === null || homeIno === null) throw new ProfileSymlinkError(profile, "has an unverified home directory identity");
|
|
4672
|
+
const homePinned = {
|
|
4673
|
+
path: fdPath(homeHandle.fd),
|
|
4674
|
+
fd: homeHandle.fd,
|
|
4675
|
+
dev: homeDev,
|
|
4676
|
+
ino: homeIno,
|
|
4677
|
+
active: true
|
|
4678
|
+
};
|
|
4679
|
+
const context = new Map(pinnedProfiles.getStore());
|
|
4680
|
+
context.set(named, pinned);
|
|
4681
|
+
const homes = new Map(pinnedHomes.getStore());
|
|
4682
|
+
homes.set(home, homePinned);
|
|
4683
|
+
try {
|
|
4684
|
+
return await pinnedHomes.run(homes, () => pinnedProfiles.run(context, () => action(directory)));
|
|
4685
|
+
} finally {
|
|
4686
|
+
pinned.active = false;
|
|
4687
|
+
homePinned.active = false;
|
|
4688
|
+
}
|
|
4550
4689
|
} finally {
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4690
|
+
for (const handle of handles.reverse()) await handle.close();
|
|
4691
|
+
}
|
|
4692
|
+
}
|
|
4693
|
+
async function assertRealProfileDirectory(profile) {
|
|
4694
|
+
const named = namedProfilePath(profile);
|
|
4695
|
+
const pinned = pinnedProfiles.getStore()?.get(named);
|
|
4696
|
+
if (pinned) {
|
|
4697
|
+
livePinnedPath(profile, pinned);
|
|
4698
|
+
return;
|
|
4554
4699
|
}
|
|
4700
|
+
const stats = await lstat(named).catch((error) => {
|
|
4701
|
+
if (error.code === "ENOENT") return null;
|
|
4702
|
+
throw error;
|
|
4703
|
+
});
|
|
4704
|
+
if (stats && (!stats.isDirectory() || stats.isSymbolicLink())) throw new ProfileSymlinkError(profile);
|
|
4705
|
+
}
|
|
4706
|
+
async function withProfileLock(profile, action, options = {}) {
|
|
4707
|
+
const normalizedProfile = validateProfileName(profile);
|
|
4708
|
+
const lockPath = profileLockPath(normalizedProfile);
|
|
4709
|
+
const held = (heldProfileLocks.getStore() ?? []).filter((ownership) => ownership.active);
|
|
4710
|
+
if (held.some((ownership) => ownership.lockPath === lockPath)) {
|
|
4711
|
+
await assertRealProfileDirectory(normalizedProfile);
|
|
4712
|
+
return action();
|
|
4713
|
+
}
|
|
4714
|
+
const named = namedProfilePath(normalizedProfile);
|
|
4715
|
+
const inherited = pinnedProfiles.getStore()?.get(named);
|
|
4716
|
+
if (inherited && !isPinnedLive(inherited)) {
|
|
4717
|
+
const profiles = new Map(pinnedProfiles.getStore());
|
|
4718
|
+
profiles.delete(named);
|
|
4719
|
+
const homes = new Map(pinnedHomes.getStore());
|
|
4720
|
+
homes.delete(tinycloudHomePath());
|
|
4721
|
+
return pinnedHomes.run(homes, () => pinnedProfiles.run(profiles, () => withProfileLock(normalizedProfile, action, options)));
|
|
4722
|
+
}
|
|
4723
|
+
return withPinnedProfileDirectory(normalizedProfile, async (directory) => {
|
|
4724
|
+
if (!directory) throw new ProfileDeletedError(normalizedProfile);
|
|
4725
|
+
const acquired = await acquireProfileLock(normalizedProfile, options);
|
|
4726
|
+
const ownership = {
|
|
4727
|
+
lockPath,
|
|
4728
|
+
turnSlot: acquired.turnSlot,
|
|
4729
|
+
deletedWhileWaiting: acquired.deletedWhileWaiting,
|
|
4730
|
+
active: true
|
|
4731
|
+
};
|
|
4732
|
+
try {
|
|
4733
|
+
return await heldProfileLocks.run([...held, ownership], action);
|
|
4734
|
+
} finally {
|
|
4735
|
+
ownership.active = false;
|
|
4736
|
+
await acquired.release();
|
|
4737
|
+
if (ownership.deletedProfile || acquired.deletedWhileWaiting) {
|
|
4738
|
+
const current = await lstat(namedProfilePath(normalizedProfile), { bigint: true }).catch(() => null);
|
|
4739
|
+
const original = await stat(directory, { bigint: true }).catch(() => null);
|
|
4740
|
+
const currentDev = current && exactFileId(current.dev);
|
|
4741
|
+
const currentIno = current && exactFileId(current.ino);
|
|
4742
|
+
if (currentDev !== null && currentIno !== null && original && currentDev === exactFileId(original.dev) && currentIno === exactFileId(original.ino)) {
|
|
4743
|
+
await rmdir(namedProfilePath(normalizedProfile)).catch(() => void 0);
|
|
4744
|
+
}
|
|
4745
|
+
}
|
|
4746
|
+
}
|
|
4747
|
+
}, { create: !options.requireProfile });
|
|
4555
4748
|
}
|
|
4556
4749
|
async function refuseWriteToDeletedProfile(profile) {
|
|
4557
4750
|
const lockPath = profileLockPath(profile);
|
|
@@ -4585,17 +4778,15 @@ async function acquireProfileLock(profile, options) {
|
|
|
4585
4778
|
retryMs: options.retryMs ?? DEFAULT_LOCK_RETRY_MS
|
|
4586
4779
|
};
|
|
4587
4780
|
const staleAfterMs = options.staleAfterMs ?? DEFAULT_STALE_LOCK_MS;
|
|
4588
|
-
const lockPath =
|
|
4781
|
+
const lockPath = join(profilePath(profile), ".lock");
|
|
4589
4782
|
const turnDirectory = profileTurnLockPath(profile);
|
|
4590
4783
|
const directory = profilePath(profile);
|
|
4784
|
+
await assertRealProfileDirectory(profile);
|
|
4591
4785
|
if (options.requireProfile && !await exists(profileConfigPath(profile))) throw new ProfileDeletedError(profile);
|
|
4592
4786
|
if (!options.requireProfile) await mkdir(directory, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
4787
|
+
await assertRealProfileDirectory(profile);
|
|
4593
4788
|
await mkdir(dirname(turnDirectory), { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
4594
|
-
|
|
4595
|
-
await chmod(path, PRIVATE_DIR_MODE).catch((error) => {
|
|
4596
|
-
if (path !== directory || !isErrno(error, "ENOENT")) throw error;
|
|
4597
|
-
});
|
|
4598
|
-
}
|
|
4789
|
+
await chmod(directory, PRIVATE_DIR_MODE);
|
|
4599
4790
|
const liveness = { directory: turnDirectory, held: void 0 };
|
|
4600
4791
|
const closeAcquisitionLiveness = async () => {
|
|
4601
4792
|
if (liveness.held) await closeLiveness(liveness.held);
|
|
@@ -4615,6 +4806,7 @@ async function acquireProfileLock(profile, options) {
|
|
|
4615
4806
|
}
|
|
4616
4807
|
const deletion = await readJson(join(turnDirectory, "deleted.json")).catch(() => null);
|
|
4617
4808
|
const deletedWhileWaiting = typeof deletion?.slot === "number" && deletion.slot >= firstWaitedTurn;
|
|
4809
|
+
if (deletedWhileWaiting && !await exists(profileConfigPath(profile))) throw new ProfileDeletedError(profile);
|
|
4618
4810
|
while (true) {
|
|
4619
4811
|
const token = randomUUID();
|
|
4620
4812
|
if (await publishProfileLock(profile, lockPath, token, options.requireProfile === true, liveness.held ?? null)) {
|
|
@@ -4760,7 +4952,7 @@ async function takeTurn(profile, directory, latest, liveness) {
|
|
|
4760
4952
|
if (published) await markTurn(path, token, "done");
|
|
4761
4953
|
return null;
|
|
4762
4954
|
} catch (error) {
|
|
4763
|
-
if (published) settleOwnTurn(directory, slot, token);
|
|
4955
|
+
if (published) await settleOwnTurn(directory, slot, token);
|
|
4764
4956
|
throw error;
|
|
4765
4957
|
}
|
|
4766
4958
|
}
|
|
@@ -4771,17 +4963,25 @@ async function settleAbandonedTurn(directory, abandoned) {
|
|
|
4771
4963
|
}
|
|
4772
4964
|
await markTurn(path, abandoned.token, "done");
|
|
4773
4965
|
}
|
|
4774
|
-
function settleOwnTurn(directory, slot, token
|
|
4966
|
+
async function settleOwnTurn(directory, slot, token) {
|
|
4775
4967
|
relinquishedTurns.add(token);
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4968
|
+
const pinned = await pinDirectory(directory).catch(() => null);
|
|
4969
|
+
const path = pinned?.path(".") ?? directory;
|
|
4970
|
+
let retries = SETTLE_RETRIES;
|
|
4971
|
+
const attempt = () => {
|
|
4972
|
+
void (async () => {
|
|
4973
|
+
try {
|
|
4974
|
+
const turn = await readTurn(path, slot);
|
|
4975
|
+
if (turn !== null && turn !== "damaged" && turn.token === token && !turn.done) await settleAbandonedTurn(path, turn);
|
|
4976
|
+
relinquishedTurns.delete(token);
|
|
4977
|
+
await pinned?.close();
|
|
4978
|
+
} catch {
|
|
4979
|
+
if (retries-- > 0) setTimeout(attempt, SETTLE_RETRY_MS).unref();
|
|
4980
|
+
else await pinned?.close();
|
|
4981
|
+
}
|
|
4982
|
+
})();
|
|
4983
|
+
};
|
|
4984
|
+
attempt();
|
|
4785
4985
|
}
|
|
4786
4986
|
async function markTurn(path, token, kind) {
|
|
4787
4987
|
for (let attempt = 0; ; attempt++) {
|
|
@@ -4826,7 +5026,7 @@ async function releaseTurn(profile, turn, liveness) {
|
|
|
4826
5026
|
try {
|
|
4827
5027
|
await markTurn(join(turn.directory, String(turn.slot)), turn.token, "done");
|
|
4828
5028
|
} catch {
|
|
4829
|
-
settleOwnTurn(turn.directory, turn.slot, turn.token);
|
|
5029
|
+
await settleOwnTurn(turn.directory, turn.slot, turn.token);
|
|
4830
5030
|
return;
|
|
4831
5031
|
}
|
|
4832
5032
|
await collectTurns(profile, turn, liveness).catch(() => void 0);
|
|
@@ -5110,7 +5310,7 @@ function isErrno(error, code) {
|
|
|
5110
5310
|
function sleep(milliseconds) {
|
|
5111
5311
|
return new Promise((resolve2) => setTimeout(resolve2, milliseconds));
|
|
5112
5312
|
}
|
|
5113
|
-
var 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,
|
|
5313
|
+
var 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;
|
|
5114
5314
|
var init_state = __esm({
|
|
5115
5315
|
"src/state.ts"() {
|
|
5116
5316
|
"use strict";
|
|
@@ -5135,7 +5335,10 @@ var init_state = __esm({
|
|
|
5135
5335
|
TEST_LOCK_LIVENESS_BARRIER_DIR = "TC_TEST_PROFILE_LOCK_LIVENESS_BARRIER_DIR";
|
|
5136
5336
|
invocationStateRoot = processWideContext("tinycloud.operations.invocationStateRoot.v1");
|
|
5137
5337
|
heldProfileLocks = processWideContext("tinycloud.operations.heldProfileLocks.v2");
|
|
5138
|
-
|
|
5338
|
+
pinnedProfiles = processWideContext("tinycloud.operations.pinnedProfiles.v2");
|
|
5339
|
+
pinnedHomes = processWideContext("tinycloud.operations.pinnedHomes.v2");
|
|
5340
|
+
canonicalHomes = globalThis[_a2 = /* @__PURE__ */ Symbol.for("tinycloud.operations.canonicalHomes.v1")] ?? (globalThis[_a2] = /* @__PURE__ */ new Map());
|
|
5341
|
+
processInstanceId = globalThis[_a3 = /* @__PURE__ */ Symbol.for("tinycloud.operations.profileLockInstance.v1")] ?? (globalThis[_a3] = randomUUID());
|
|
5139
5342
|
LIVENESS_SOCKET = /^\.live-([0-9a-f]+)-([0-9a-f]+)-[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}(\.new)?$/;
|
|
5140
5343
|
LIVENESS_PROBE_TIMEOUT_MS = 1e3;
|
|
5141
5344
|
ProfileLockTimeoutError = class extends Error {
|
|
@@ -5153,8 +5356,15 @@ var init_state = __esm({
|
|
|
5153
5356
|
this.name = "ProfileDeletedError";
|
|
5154
5357
|
}
|
|
5155
5358
|
};
|
|
5359
|
+
ProfileSymlinkError = class extends Error {
|
|
5360
|
+
constructor(profile, detail = "must be a real directory, not a symlink or another file type") {
|
|
5361
|
+
super(`Profile "${profile}" ${detail}.`);
|
|
5362
|
+
this.code = "PROFILE_SYMLINK";
|
|
5363
|
+
this.name = "ProfileSymlinkError";
|
|
5364
|
+
}
|
|
5365
|
+
};
|
|
5156
5366
|
TURN = /^(?:0|[1-9][0-9]*)$/;
|
|
5157
|
-
relinquishedTurns = globalThis[
|
|
5367
|
+
relinquishedTurns = globalThis[_a4 = /* @__PURE__ */ Symbol.for("tinycloud.operations.relinquishedProfileTurns.v1")] ?? (globalThis[_a4] = /* @__PURE__ */ new Set());
|
|
5158
5368
|
MARKER_RETRY_DELAYS_MS = [10, 50, 250];
|
|
5159
5369
|
SETTLE_RETRY_MS = 1e3;
|
|
5160
5370
|
SETTLE_RETRIES = 300;
|