@abelxiaoxing/cadence 2.0.14 → 2.0.15
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/README.md +22 -1
- package/docs/service-management.md +41 -3
- package/package.json +1 -1
- package/src/native-build.ts +1 -1
- package/src/service/coordination.ts +275 -44
- package/src/service-cli.mjs +259 -40
- package/src/service-host.mjs +228 -43
- package/src/worker-public.mjs +1 -0
- package/web/dist/assets/cadence.js +3 -3
- package/web/dist/manifest.json +5 -5
package/src/service-cli.mjs
CHANGED
|
@@ -327,6 +327,7 @@ var ERROR_CODES = [
|
|
|
327
327
|
"quarantine",
|
|
328
328
|
"capacity",
|
|
329
329
|
"unavailable",
|
|
330
|
+
"registry-locked",
|
|
330
331
|
"auth-unavailable",
|
|
331
332
|
"recording-exhausted",
|
|
332
333
|
"receipt-inconclusive"
|
|
@@ -3517,6 +3518,21 @@ function names(path2, allowed) {
|
|
|
3517
3518
|
dir.closeSync();
|
|
3518
3519
|
}
|
|
3519
3520
|
}
|
|
3521
|
+
function decodeRegistryLock(snapshot) {
|
|
3522
|
+
const o = closed(json(snapshot), ["schema", "token", "pid", "host"]);
|
|
3523
|
+
return {
|
|
3524
|
+
schema: literal(o.schema, "cadence-service-registry-lock/1"),
|
|
3525
|
+
token: decodeId(o.token),
|
|
3526
|
+
pid: integer(o.pid, 1, 2147483647),
|
|
3527
|
+
host: text(o.host, 255, true)
|
|
3528
|
+
};
|
|
3529
|
+
}
|
|
3530
|
+
var registryNames = [
|
|
3531
|
+
"format.json",
|
|
3532
|
+
"roots.json",
|
|
3533
|
+
"registry.lock",
|
|
3534
|
+
"recovery-v1"
|
|
3535
|
+
];
|
|
3520
3536
|
function recoveryOwnerStatus(pid, host) {
|
|
3521
3537
|
if (host !== hostname2())
|
|
3522
3538
|
return "ambiguous";
|
|
@@ -3568,11 +3584,7 @@ class ServiceCoordination {
|
|
|
3568
3584
|
registry() {
|
|
3569
3585
|
if (!this.files.directory(this.registryDirectory))
|
|
3570
3586
|
return;
|
|
3571
|
-
names(this.registryDirectory,
|
|
3572
|
-
"format.json",
|
|
3573
|
-
"roots.json",
|
|
3574
|
-
"registry.lock"
|
|
3575
|
-
]);
|
|
3587
|
+
names(this.registryDirectory, registryNames);
|
|
3576
3588
|
const format = this.files.read(this.registryPath("format.json"));
|
|
3577
3589
|
const snapshot = this.files.read(this.registryPath("roots.json"), REGISTRY_BYTES);
|
|
3578
3590
|
if (!format || !snapshot)
|
|
@@ -3597,10 +3609,10 @@ class ServiceCoordination {
|
|
|
3597
3609
|
}
|
|
3598
3610
|
};
|
|
3599
3611
|
}
|
|
3600
|
-
assertNamespace(walks) {
|
|
3601
|
-
if (!this.files.directory(
|
|
3612
|
+
assertNamespace(walks, directory2 = this.directory) {
|
|
3613
|
+
if (!this.files.directory(directory2, false, walks))
|
|
3602
3614
|
refused();
|
|
3603
|
-
names(
|
|
3615
|
+
names(directory2, [
|
|
3604
3616
|
"format.json",
|
|
3605
3617
|
"config.json",
|
|
3606
3618
|
"registration.json",
|
|
@@ -3612,15 +3624,16 @@ class ServiceCoordination {
|
|
|
3612
3624
|
"history-v2",
|
|
3613
3625
|
"conversations-v1"
|
|
3614
3626
|
]);
|
|
3615
|
-
this.files.directory(join5(
|
|
3627
|
+
this.files.directory(join5(directory2, "history-v2"), false, walks);
|
|
3616
3628
|
}
|
|
3617
|
-
selected() {
|
|
3629
|
+
selected(directory2 = this.directory, root = this.store.root, stoppedOnly = false) {
|
|
3618
3630
|
const walks = new DirectoryWalks;
|
|
3619
|
-
if (!this.files.directory(
|
|
3631
|
+
if (!this.files.directory(directory2, false, walks))
|
|
3620
3632
|
return;
|
|
3621
|
-
this.assertNamespace(walks);
|
|
3622
|
-
const
|
|
3623
|
-
const
|
|
3633
|
+
this.assertNamespace(walks, directory2);
|
|
3634
|
+
const path2 = (name) => join5(directory2, name);
|
|
3635
|
+
const manifest = this.files.read(path2("format.json"), undefined, walks);
|
|
3636
|
+
const config = this.files.read(path2("config.json"), undefined, walks);
|
|
3624
3637
|
if (!manifest || !config)
|
|
3625
3638
|
refused();
|
|
3626
3639
|
const f = closed(json(manifest), [
|
|
@@ -3630,20 +3643,22 @@ class ServiceCoordination {
|
|
|
3630
3643
|
"configIdentity"
|
|
3631
3644
|
]);
|
|
3632
3645
|
literal(f.schema, "cadence-service-format/1");
|
|
3633
|
-
literal(f.root,
|
|
3646
|
+
literal(f.root, root);
|
|
3634
3647
|
literal(f.stateHome, this.stateHome);
|
|
3635
3648
|
literal(f.configIdentity, config.identity);
|
|
3636
3649
|
const c = decodeOwnerConfig(json(config));
|
|
3637
|
-
const hostConfig = this.files.read(
|
|
3650
|
+
const hostConfig = this.files.read(path2("host-config.json"), undefined, walks);
|
|
3638
3651
|
if (hostConfig && hostConfig.identity !== c.policyIdentity)
|
|
3639
3652
|
refused();
|
|
3640
|
-
const registration = this.files.read(
|
|
3641
|
-
const owner = this.files.read(
|
|
3642
|
-
const capability = this.files.read(
|
|
3643
|
-
const checkout = this.files.read(
|
|
3653
|
+
const registration = this.files.read(path2("registration.json"), undefined, walks);
|
|
3654
|
+
const owner = this.files.read(path2("owner.lock"), undefined, walks);
|
|
3655
|
+
const capability = this.files.read(path2("local-capability.json"), undefined, walks);
|
|
3656
|
+
const checkout = this.files.read(join5(dirname5(directory2), "checkout.lock"), 8192, walks);
|
|
3644
3657
|
const value = registration ? decodeRegistration(json(registration)) : undefined;
|
|
3645
|
-
if (value && (value.root !==
|
|
3658
|
+
if (value && (value.root !== root || value.stateHome !== this.stateHome || value.configIdentity !== config.identity || !endpointMatches(c.endpoint, value.endpoint)))
|
|
3646
3659
|
refused();
|
|
3660
|
+
if (stoppedOnly && (checkout || value && value.phase !== "stopped"))
|
|
3661
|
+
throw new ContractError("quarantine");
|
|
3647
3662
|
if (value && value.phase !== "stopped") {
|
|
3648
3663
|
if (!owner || !checkout || !capability)
|
|
3649
3664
|
refused();
|
|
@@ -3723,18 +3738,21 @@ class ServiceCoordination {
|
|
|
3723
3738
|
}
|
|
3724
3739
|
withRegistry(operation) {
|
|
3725
3740
|
this.files.directory(this.registryDirectory, true);
|
|
3726
|
-
names(this.registryDirectory,
|
|
3727
|
-
"format.json",
|
|
3728
|
-
"roots.json",
|
|
3729
|
-
"registry.lock"
|
|
3730
|
-
]);
|
|
3741
|
+
names(this.registryDirectory, registryNames);
|
|
3731
3742
|
const lockPath = this.registryPath("registry.lock");
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3743
|
+
let lock;
|
|
3744
|
+
try {
|
|
3745
|
+
lock = this.files.create(lockPath, encoded({
|
|
3746
|
+
schema: "cadence-service-registry-lock/1",
|
|
3747
|
+
token: randomUUID3(),
|
|
3748
|
+
pid: process.pid,
|
|
3749
|
+
host: hostname2()
|
|
3750
|
+
}));
|
|
3751
|
+
} catch (error) {
|
|
3752
|
+
if (error instanceof Error && "code" in error && error.code === "EEXIST")
|
|
3753
|
+
throw new ContractError("registry-locked");
|
|
3754
|
+
throw error;
|
|
3755
|
+
}
|
|
3738
3756
|
let dirty = false;
|
|
3739
3757
|
try {
|
|
3740
3758
|
let format = this.files.read(this.registryPath("format.json"));
|
|
@@ -3758,11 +3776,7 @@ class ServiceCoordination {
|
|
|
3758
3776
|
}, () => {
|
|
3759
3777
|
this.files.assert(lockPath, lock);
|
|
3760
3778
|
this.files.assert(this.registryPath("format.json"), format);
|
|
3761
|
-
names(this.registryDirectory,
|
|
3762
|
-
"format.json",
|
|
3763
|
-
"roots.json",
|
|
3764
|
-
"registry.lock"
|
|
3765
|
-
]);
|
|
3779
|
+
names(this.registryDirectory, registryNames);
|
|
3766
3780
|
});
|
|
3767
3781
|
this.files.remove(lockPath, lock);
|
|
3768
3782
|
return result;
|
|
@@ -3894,6 +3908,154 @@ class ServiceCoordination {
|
|
|
3894
3908
|
});
|
|
3895
3909
|
});
|
|
3896
3910
|
}
|
|
3911
|
+
registryLockDiagnostic() {
|
|
3912
|
+
try {
|
|
3913
|
+
const lock = this.files.read(this.registryPath("registry.lock"));
|
|
3914
|
+
if (!lock)
|
|
3915
|
+
return;
|
|
3916
|
+
const value = decodeRegistryLock(lock);
|
|
3917
|
+
return {
|
|
3918
|
+
reason: "registry-locked",
|
|
3919
|
+
owner: recoveryOwnerStatus(value.pid, value.host)
|
|
3920
|
+
};
|
|
3921
|
+
} catch {
|
|
3922
|
+
return { reason: "registry-locked", owner: "unreadable" };
|
|
3923
|
+
}
|
|
3924
|
+
}
|
|
3925
|
+
registryRecoveryState() {
|
|
3926
|
+
const lock = this.files.read(this.registryPath("registry.lock"));
|
|
3927
|
+
if (!lock)
|
|
3928
|
+
throw new ContractError("conflict");
|
|
3929
|
+
const value = decodeRegistryLock(lock);
|
|
3930
|
+
names(this.registryDirectory, registryNames);
|
|
3931
|
+
const format = this.files.read(this.registryPath("format.json"));
|
|
3932
|
+
const roots = this.files.read(this.registryPath("roots.json"), REGISTRY_BYTES);
|
|
3933
|
+
const registry = !format && !roots ? undefined : this.registry();
|
|
3934
|
+
const entries = registry?.value.entries ?? [];
|
|
3935
|
+
const protectedRoots = new Set(entries.map((row) => hash(row.root)));
|
|
3936
|
+
const proofs = [];
|
|
3937
|
+
const directory2 = opendirSync(this.files.namespace);
|
|
3938
|
+
try {
|
|
3939
|
+
let count = 0;
|
|
3940
|
+
for (let entry = directory2.readSync();entry; entry = directory2.readSync()) {
|
|
3941
|
+
if (++count > 1024)
|
|
3942
|
+
throw new ContractError("capacity");
|
|
3943
|
+
if (!/^[a-f0-9]{64}$/.test(entry.name) || protectedRoots.has(entry.name))
|
|
3944
|
+
continue;
|
|
3945
|
+
const service = join5(this.files.namespace, entry.name, "service-v1");
|
|
3946
|
+
if (!this.files.directory(service))
|
|
3947
|
+
continue;
|
|
3948
|
+
const manifest = this.files.read(join5(service, "format.json"));
|
|
3949
|
+
if (!manifest)
|
|
3950
|
+
refused();
|
|
3951
|
+
const f = closed(json(manifest), [
|
|
3952
|
+
"schema",
|
|
3953
|
+
"root",
|
|
3954
|
+
"stateHome",
|
|
3955
|
+
"configIdentity"
|
|
3956
|
+
]);
|
|
3957
|
+
const root = pathValue(f.root);
|
|
3958
|
+
if (hash(root) !== entry.name)
|
|
3959
|
+
refused();
|
|
3960
|
+
const selected = this.selected(service, root, true);
|
|
3961
|
+
if (!selected)
|
|
3962
|
+
throw new ContractError("quarantine");
|
|
3963
|
+
proofs.push([
|
|
3964
|
+
entry.name,
|
|
3965
|
+
...[
|
|
3966
|
+
selected.manifest,
|
|
3967
|
+
selected.config,
|
|
3968
|
+
selected.hostConfig,
|
|
3969
|
+
selected.registration
|
|
3970
|
+
].map((file) => file ? [file.identity, file.stamp] : null)
|
|
3971
|
+
]);
|
|
3972
|
+
}
|
|
3973
|
+
} finally {
|
|
3974
|
+
directory2.closeSync();
|
|
3975
|
+
}
|
|
3976
|
+
proofs.sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b), "en"));
|
|
3977
|
+
const stateDigest = hash(encoded([
|
|
3978
|
+
lock.identity,
|
|
3979
|
+
lock.stamp,
|
|
3980
|
+
format ? [format.identity, format.stamp] : null,
|
|
3981
|
+
roots ? [roots.identity, roots.stamp] : null,
|
|
3982
|
+
proofs
|
|
3983
|
+
]));
|
|
3984
|
+
const epoch = hash(encoded([lock.identity, lock.stamp]));
|
|
3985
|
+
const archive = this.registryPath(join5("recovery-v1", epoch));
|
|
3986
|
+
const attempts = [];
|
|
3987
|
+
let previousResolver = null;
|
|
3988
|
+
if (this.files.directory(archive)) {
|
|
3989
|
+
names(archive, Array.from({ length: 128 }, (_, n) => `${n}.json`));
|
|
3990
|
+
for (let n = 0;n < 128; n++) {
|
|
3991
|
+
const attempt = this.files.read(join5(archive, `${n}.json`), 32768);
|
|
3992
|
+
if (!attempt) {
|
|
3993
|
+
names(archive, attempts.map((_, i) => `${i}.json`));
|
|
3994
|
+
break;
|
|
3995
|
+
}
|
|
3996
|
+
const o = closed(json(attempt, 32768), [
|
|
3997
|
+
"schema",
|
|
3998
|
+
"lock",
|
|
3999
|
+
"pid",
|
|
4000
|
+
"host",
|
|
4001
|
+
"digest"
|
|
4002
|
+
]);
|
|
4003
|
+
literal(o.schema, "cadence-registry-recovery/1");
|
|
4004
|
+
literal(o.lock, lock.bytes.toString("utf8"));
|
|
4005
|
+
decodeHash(o.digest);
|
|
4006
|
+
previousResolver = recoveryOwnerStatus(integer(o.pid, 1, 2147483647), text(o.host, 255, true));
|
|
4007
|
+
attempts.push(attempt);
|
|
4008
|
+
}
|
|
4009
|
+
}
|
|
4010
|
+
const inspection = {
|
|
4011
|
+
scope: "global-registry",
|
|
4012
|
+
epoch,
|
|
4013
|
+
coordinationDigest: hash(encoded([stateDigest, attempts.map((a) => [a.identity, a.stamp])])),
|
|
4014
|
+
owner: recoveryOwnerStatus(value.pid, value.host),
|
|
4015
|
+
process: { pid: value.pid, host: value.host },
|
|
4016
|
+
reservations: entries.length,
|
|
4017
|
+
previousResolver,
|
|
4018
|
+
resolution: "local-interactive-only"
|
|
4019
|
+
};
|
|
4020
|
+
return { lock, archive, attempts, stateDigest, inspection };
|
|
4021
|
+
}
|
|
4022
|
+
inspectRegistryRecovery() {
|
|
4023
|
+
return this.registryRecoveryState().inspection;
|
|
4024
|
+
}
|
|
4025
|
+
resolveRegistryRecovery(expected, assertAuthority) {
|
|
4026
|
+
assertAuthority();
|
|
4027
|
+
const state = this.registryRecoveryState();
|
|
4028
|
+
if (encoded(state.inspection) !== encoded(expected))
|
|
4029
|
+
throw new ContractError("conflict");
|
|
4030
|
+
if (expected.owner !== "absent" || expected.previousResolver !== null && expected.previousResolver !== "absent")
|
|
4031
|
+
throw new ContractError("quarantine");
|
|
4032
|
+
if (state.attempts.length >= 128)
|
|
4033
|
+
throw new ContractError("capacity");
|
|
4034
|
+
assertAuthority();
|
|
4035
|
+
const receiptPath = join5(state.archive, `${state.attempts.length}.json`);
|
|
4036
|
+
const receipt = this.files.create(receiptPath, encoded({
|
|
4037
|
+
schema: "cadence-registry-recovery/1",
|
|
4038
|
+
lock: state.lock.bytes.toString("utf8"),
|
|
4039
|
+
pid: process.pid,
|
|
4040
|
+
host: hostname2(),
|
|
4041
|
+
digest: expected.coordinationDigest
|
|
4042
|
+
}), 32768);
|
|
4043
|
+
assertAuthority();
|
|
4044
|
+
const current = this.registryRecoveryState();
|
|
4045
|
+
if (current.stateDigest !== state.stateDigest || current.inspection.owner !== "absent" || current.attempts.length !== state.attempts.length + 1)
|
|
4046
|
+
throw new ContractError("conflict");
|
|
4047
|
+
for (const [n, attempt] of state.attempts.entries())
|
|
4048
|
+
this.files.assert(join5(state.archive, `${n}.json`), attempt, 32768);
|
|
4049
|
+
this.files.assert(receiptPath, receipt, 32768);
|
|
4050
|
+
assertAuthority();
|
|
4051
|
+
this.files.remove(this.registryPath("registry.lock"), state.lock);
|
|
4052
|
+
return {
|
|
4053
|
+
status: "resolved",
|
|
4054
|
+
scope: "global-registry",
|
|
4055
|
+
recoveryId: expected.epoch,
|
|
4056
|
+
notice: "registry-only-project-fences-preserved"
|
|
4057
|
+
};
|
|
4058
|
+
}
|
|
3897
4059
|
prepareRecoveryRelease(expected, assertAuthority) {
|
|
3898
4060
|
assertAuthority();
|
|
3899
4061
|
if (this.recoverySnapshot().digest !== expected)
|
|
@@ -6991,7 +7153,7 @@ async function confirm(expected) {
|
|
|
6991
7153
|
"storage"
|
|
6992
7154
|
],
|
|
6993
7155
|
[
|
|
6994
|
-
|
|
7156
|
+
`${expected.scope === "global-registry" ? "Release ONLY the global registry lock; all project fences remain." : "Release ONLY the displayed unchanged service-owned fences."} Type resolve ${expected.epoch}`,
|
|
6995
7157
|
`resolve ${expected.epoch}`
|
|
6996
7158
|
]
|
|
6997
7159
|
];
|
|
@@ -7054,6 +7216,42 @@ async function confirm(expected) {
|
|
|
7054
7216
|
async function localRecovery(c, operation) {
|
|
7055
7217
|
if (operation === "resolve")
|
|
7056
7218
|
native();
|
|
7219
|
+
const registryLock = c.registryLockDiagnostic();
|
|
7220
|
+
if (registryLock) {
|
|
7221
|
+
let expected2;
|
|
7222
|
+
try {
|
|
7223
|
+
expected2 = c.inspectRegistryRecovery();
|
|
7224
|
+
} catch {
|
|
7225
|
+
const blocked = {
|
|
7226
|
+
scope: "global-registry",
|
|
7227
|
+
...registryLock,
|
|
7228
|
+
coordination: "unavailable",
|
|
7229
|
+
resolution: "blocked-preserve-state",
|
|
7230
|
+
notice: "Registry state is incomplete, changed or unsafe. Preserve it; no lock or project resource was released."
|
|
7231
|
+
};
|
|
7232
|
+
if (operation === "inspect")
|
|
7233
|
+
return blocked;
|
|
7234
|
+
process.stdout.write(`${JSON.stringify(blocked)}
|
|
7235
|
+
`);
|
|
7236
|
+
throw new ContractError("quarantine");
|
|
7237
|
+
}
|
|
7238
|
+
const view2 = {
|
|
7239
|
+
...expected2,
|
|
7240
|
+
process: {
|
|
7241
|
+
...expected2.process,
|
|
7242
|
+
host: diagnosticText(expected2.process.host, 255)
|
|
7243
|
+
},
|
|
7244
|
+
notice: `Global registry lock blocks every project in this state home. Recovery preserves its original bytes and all project reservations, owner/checkout locks and history. Inspect project fences separately afterward. ${warning}`
|
|
7245
|
+
};
|
|
7246
|
+
if (operation === "inspect")
|
|
7247
|
+
return view2;
|
|
7248
|
+
process.stdout.write(`${JSON.stringify(view2)}
|
|
7249
|
+
`);
|
|
7250
|
+
if (expected2.owner !== "absent" || expected2.previousResolver !== null && expected2.previousResolver !== "absent")
|
|
7251
|
+
throw new ContractError("quarantine");
|
|
7252
|
+
const assertFresh2 = await confirm(expected2);
|
|
7253
|
+
return c.resolveRegistryRecovery(expected2, assertFresh2);
|
|
7254
|
+
}
|
|
7057
7255
|
const history = new HistoryRecovery(c);
|
|
7058
7256
|
const expected = await inspectRecovery(c, history);
|
|
7059
7257
|
const registration2 = c.recoverySnapshot().registration;
|
|
@@ -7355,6 +7553,13 @@ function nativeCall(c, operation, reveal = false, controlBody) {
|
|
|
7355
7553
|
}
|
|
7356
7554
|
var pause = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
7357
7555
|
async function serviceStatus(c, diagnose = false) {
|
|
7556
|
+
const registryLock = c.registryLockDiagnostic?.();
|
|
7557
|
+
if (registryLock)
|
|
7558
|
+
return {
|
|
7559
|
+
status: "unavailable",
|
|
7560
|
+
...registryLock,
|
|
7561
|
+
recovery: "cadence recover inspect --workspace <project> [--state-home <state-home>]"
|
|
7562
|
+
};
|
|
7358
7563
|
const observed2 = c.inspect();
|
|
7359
7564
|
if (observed2.status === "absent" || observed2.status === "stopped")
|
|
7360
7565
|
return observed2;
|
|
@@ -7397,6 +7602,10 @@ function serverConfigHint(error) {
|
|
|
7397
7602
|
Fix it and run the command again; the file holds the WebUI port and token as ${PORT_KEY}=<1-65535> and ${TOKEN_KEY}=<at least 32 characters>, one line each.
|
|
7398
7603
|
`;
|
|
7399
7604
|
}
|
|
7605
|
+
function registryGuidance(workspace, stateHome) {
|
|
7606
|
+
return `The shared registry lock blocks project access in this state home. No automatic lock deletion. Inspect locally: cadence recover inspect --workspace ${JSON.stringify(workspace)} --state-home ${JSON.stringify(stateHome)}; eligible dead locks require cadence recover resolve in a local terminal.
|
|
7607
|
+
`;
|
|
7608
|
+
}
|
|
7400
7609
|
function startupGuidance(result, input) {
|
|
7401
7610
|
let report;
|
|
7402
7611
|
try {
|
|
@@ -7404,6 +7613,8 @@ function startupGuidance(result, input) {
|
|
|
7404
7613
|
} catch {
|
|
7405
7614
|
return "";
|
|
7406
7615
|
}
|
|
7616
|
+
if (report.error === "registry-locked")
|
|
7617
|
+
return registryGuidance(input.workspace, input.stateHome);
|
|
7407
7618
|
const agent = input.config.agentDir;
|
|
7408
7619
|
const inspect = `Inspect: cadence-service diagnose|recover inspect --workspace ${JSON.stringify(input.workspace)}
|
|
7409
7620
|
`;
|
|
@@ -7431,6 +7642,15 @@ ${inspect}` : "";
|
|
|
7431
7642
|
async function startService(options, foreground, hostUrl) {
|
|
7432
7643
|
const network = networkSnapshot(process.env);
|
|
7433
7644
|
const env = hostEnvironment(process.env);
|
|
7645
|
+
const c = new ServiceCoordination(options.workspace, options.stateHome);
|
|
7646
|
+
if (c.registryLockDiagnostic()) {
|
|
7647
|
+
process.stderr.write(registryGuidance(c.store.root, c.stateHome));
|
|
7648
|
+
return {
|
|
7649
|
+
status: "refused",
|
|
7650
|
+
error: "registry-locked",
|
|
7651
|
+
stage: "service"
|
|
7652
|
+
};
|
|
7653
|
+
}
|
|
7434
7654
|
let input;
|
|
7435
7655
|
try {
|
|
7436
7656
|
input = decodeHostInput(selectStart(options, process.env));
|
|
@@ -7443,7 +7663,6 @@ async function startService(options, foreground, hostUrl) {
|
|
|
7443
7663
|
`;
|
|
7444
7664
|
if (Buffer.byteLength(serialized) > STARTUP_INPUT_BYTES)
|
|
7445
7665
|
throw new ContractError("oversize");
|
|
7446
|
-
const c = new ServiceCoordination(input.workspace, input.stateHome);
|
|
7447
7666
|
const existing = await serviceStatus(c);
|
|
7448
7667
|
if (existing && typeof existing === "object" && "status" in existing && existing.status === "alive") {
|
|
7449
7668
|
const running = readHostConfig(c);
|
|
@@ -7891,7 +8110,7 @@ async function projectMain(argv, hostUrl) {
|
|
|
7891
8110
|
mkdirSync6(options.workspace, { recursive: true });
|
|
7892
8111
|
effectiveWorkspace(options.workspace, options.stateHome);
|
|
7893
8112
|
const c = new ServiceCoordination(options.workspace, options.stateHome);
|
|
7894
|
-
const kept = readHostConfig(c);
|
|
8113
|
+
const kept = c.registryLockDiagnostic() ? undefined : readHostConfig(c);
|
|
7895
8114
|
if (!kept)
|
|
7896
8115
|
options.port = 0;
|
|
7897
8116
|
const result = await startService(options, false, hostUrl);
|