@devrouter/cli 0.1.1 → 0.1.3
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/devrouter-lifecycle-worker.js +418 -119
- package/dist/devrouter.js +764 -226
- package/package.json +6 -1
- package/upgrade-prompts/0.1.2.md +57 -0
- package/upgrade-prompts/0.1.3.md +55 -0
|
@@ -23,8 +23,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
));
|
|
24
24
|
|
|
25
25
|
// src/core/devpod-exec.ts
|
|
26
|
-
var
|
|
27
|
-
var
|
|
26
|
+
var import_node_child_process30 = require("child_process");
|
|
27
|
+
var import_node_crypto23 = require("crypto");
|
|
28
28
|
|
|
29
29
|
// src/core/devpod-environment.ts
|
|
30
30
|
var import_node_child_process3 = require("child_process");
|
|
@@ -1164,7 +1164,11 @@ function listDevpodWorkspacesRaw(options = {}) {
|
|
|
1164
1164
|
}
|
|
1165
1165
|
if (result.error || result.status !== 0) {
|
|
1166
1166
|
const details = [result.error?.message, result.stdout, result.stderr].filter(Boolean).join("\n").trim();
|
|
1167
|
-
|
|
1167
|
+
const failure = new Error(
|
|
1168
|
+
`devpod list failed: ${details || "devpod is not installed or unavailable"}`
|
|
1169
|
+
);
|
|
1170
|
+
if (result.error) failure.code = result.error.code;
|
|
1171
|
+
throw failure;
|
|
1168
1172
|
}
|
|
1169
1173
|
let parsed;
|
|
1170
1174
|
try {
|
|
@@ -1303,9 +1307,11 @@ function listDevsyWorkspaces() {
|
|
|
1303
1307
|
);
|
|
1304
1308
|
if (result.status !== 0) {
|
|
1305
1309
|
const details = [result.error?.message, result.stdout, result.stderr].filter(Boolean).join("\n").trim();
|
|
1306
|
-
|
|
1310
|
+
const failure = new Error(
|
|
1307
1311
|
`devsy workspace list failed: ${details || "devsy is not installed or unavailable"}`
|
|
1308
1312
|
);
|
|
1313
|
+
if (result.error) failure.code = result.error.code;
|
|
1314
|
+
throw failure;
|
|
1309
1315
|
}
|
|
1310
1316
|
let parsed;
|
|
1311
1317
|
try {
|
|
@@ -1775,25 +1781,23 @@ function readWorkspaceRuntimeConfig() {
|
|
|
1775
1781
|
}
|
|
1776
1782
|
return config;
|
|
1777
1783
|
}
|
|
1784
|
+
function readRegistrySnapshot(runtime, read2, snapshots) {
|
|
1785
|
+
try {
|
|
1786
|
+
return read2();
|
|
1787
|
+
} catch (error) {
|
|
1788
|
+
if (error?.code === "ENOENT") return void 0;
|
|
1789
|
+
snapshots.unavailable.push(runtime);
|
|
1790
|
+
snapshots.errors[runtime] = error instanceof Error ? error.message : String(error);
|
|
1791
|
+
return void 0;
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1778
1794
|
function getWorkspaceRegistrySnapshots() {
|
|
1779
1795
|
if (cachedSnapshots) return cachedSnapshots;
|
|
1780
1796
|
const snapshots = { unavailable: [], errors: {} };
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
snapshots.unavailable.push("devsy");
|
|
1786
|
-
snapshots.errors.devsy = error instanceof Error ? error.message : String(error);
|
|
1787
|
-
}
|
|
1788
|
-
}
|
|
1789
|
-
if (isRuntimeInstalled("devpod")) {
|
|
1790
|
-
try {
|
|
1791
|
-
snapshots.devpod = listDevpodWorkspacesRaw();
|
|
1792
|
-
} catch (error) {
|
|
1793
|
-
snapshots.unavailable.push("devpod");
|
|
1794
|
-
snapshots.errors.devpod = error instanceof Error ? error.message : String(error);
|
|
1795
|
-
}
|
|
1796
|
-
}
|
|
1797
|
+
const devsy = readRegistrySnapshot("devsy", listDevsyWorkspaces, snapshots);
|
|
1798
|
+
if (devsy !== void 0) snapshots.devsy = devsy;
|
|
1799
|
+
const devpod = readRegistrySnapshot("devpod", listDevpodWorkspacesRaw, snapshots);
|
|
1800
|
+
if (devpod !== void 0) snapshots.devpod = devpod;
|
|
1797
1801
|
cachedSnapshots = snapshots;
|
|
1798
1802
|
return snapshots;
|
|
1799
1803
|
}
|
|
@@ -1954,7 +1958,7 @@ function selectDevpodWorkspace(workspaces, repoPath) {
|
|
|
1954
1958
|
}
|
|
1955
1959
|
|
|
1956
1960
|
// src/core/devsy-exec.ts
|
|
1957
|
-
var
|
|
1961
|
+
var import_node_child_process29 = require("child_process");
|
|
1958
1962
|
|
|
1959
1963
|
// src/core/devsy-exec-proof.ts
|
|
1960
1964
|
var import_node_fs7 = __toESM(require("fs"));
|
|
@@ -4040,11 +4044,19 @@ function absentRegistrationIdentity(state) {
|
|
|
4040
4044
|
resetWorkspaceRuntimeCaches();
|
|
4041
4045
|
if (resolveWorkspaceRuntimeOrDefault(state.repoPath) !== "devsy")
|
|
4042
4046
|
throw new Error("Stop provider changed.");
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4047
|
+
const linked = isLinkedWorktree(state.repoPath);
|
|
4048
|
+
if (linked !== (state.workspace !== void 0))
|
|
4049
|
+
throw new Error("Absent stop requires the exact workspace identity.");
|
|
4050
|
+
let identity2;
|
|
4051
|
+
if (linked) {
|
|
4052
|
+
const workspace = state.workspace;
|
|
4053
|
+
const record5 = readWorkspaceOwnership(state.repoPath, workspace);
|
|
4054
|
+
if (!record5 || record5.workspace !== workspace || record5.devpodId !== state.devpodId || !sameWorkspacePath(record5.worktreePath, state.repoPath) || resolveWorktreeWorkspace(state.repoPath) !== workspace || inspectWorkspaceOwnership(record5, listGitWorktrees(state.repoPath), void 0).ownerStatus !== "present")
|
|
4055
|
+
throw new Error("Absent stop workspace ownership changed.");
|
|
4056
|
+
identity2 = { kind: "linked", record: record5, gitCommonDir: resolveGitCommonDir(state.repoPath) };
|
|
4057
|
+
} else {
|
|
4058
|
+
identity2 = { kind: "primary", repoPath: state.repoPath, devpodId: state.devpodId };
|
|
4059
|
+
}
|
|
4048
4060
|
for (const entries of [
|
|
4049
4061
|
listDevsyWorkspaces(),
|
|
4050
4062
|
listDevpodWorkspacesRaw({ allowMissingExecutable: true })
|
|
@@ -4054,7 +4066,7 @@ function absentRegistrationIdentity(state) {
|
|
|
4054
4066
|
))
|
|
4055
4067
|
throw new Error("Absent stop requires both provider registrations to remain absent.");
|
|
4056
4068
|
}
|
|
4057
|
-
return
|
|
4069
|
+
return identity2;
|
|
4058
4070
|
}
|
|
4059
4071
|
function replacementObservation(state) {
|
|
4060
4072
|
resetWorkspaceRuntimeCaches();
|
|
@@ -4164,7 +4176,7 @@ function proveManagedStop(state) {
|
|
|
4164
4176
|
const identityKeys = ["context", "providerId", "uid", "sourcePath", "sourceContainer"];
|
|
4165
4177
|
const current = identityOf(owner.workspace);
|
|
4166
4178
|
const changed = identityKeys.filter((key) => baseline[key] !== current[key]);
|
|
4167
|
-
if (changed.length === 0 &&
|
|
4179
|
+
if (changed.length === 0 && inspectManagedStopContainers(baseline.project, baseline.endpoint).length === 0) {
|
|
4168
4180
|
proveAbsentPopulationStop(state, baseline, current);
|
|
4169
4181
|
return { status: "proven-absent", containers: [] };
|
|
4170
4182
|
}
|
|
@@ -4327,12 +4339,13 @@ function reliabilityFence(state) {
|
|
|
4327
4339
|
}
|
|
4328
4340
|
|
|
4329
4341
|
// src/core/reliability-operation-store.ts
|
|
4330
|
-
var
|
|
4342
|
+
var import_node_crypto9 = require("crypto");
|
|
4331
4343
|
var import_node_fs17 = __toESM(require("fs"));
|
|
4332
4344
|
var import_node_path21 = __toESM(require("path"));
|
|
4333
4345
|
var import_node_util4 = require("util");
|
|
4334
4346
|
|
|
4335
4347
|
// src/core/capacity-store.ts
|
|
4348
|
+
var import_node_crypto7 = require("crypto");
|
|
4336
4349
|
var import_node_fs15 = __toESM(require("fs"));
|
|
4337
4350
|
var import_node_path19 = __toESM(require("path"));
|
|
4338
4351
|
|
|
@@ -4393,11 +4406,13 @@ function evaluateCapacity(budgets, samples, charges, requested, nowMs, maxSample
|
|
|
4393
4406
|
// src/core/capacity-store.ts
|
|
4394
4407
|
var MAX_BYTES = 1048576;
|
|
4395
4408
|
var CapacityHistoryError = class extends Error {
|
|
4396
|
-
constructor(code) {
|
|
4409
|
+
constructor(code, cause = null, location = null) {
|
|
4397
4410
|
super(
|
|
4398
4411
|
code === "capacity-ledger-lost" ? "Capacity ledger history is lost; restore verified capacity-reservations.json." : "Capacity history cannot be proven; inspect the private lifecycle journals."
|
|
4399
4412
|
);
|
|
4400
4413
|
this.code = code;
|
|
4414
|
+
this.cause = cause;
|
|
4415
|
+
this.location = location;
|
|
4401
4416
|
this.name = "CapacityHistoryError";
|
|
4402
4417
|
}
|
|
4403
4418
|
};
|
|
@@ -4554,9 +4569,10 @@ var CapacityStore = class {
|
|
|
4554
4569
|
const buffer = Buffer.alloc(MAX_BYTES + 1);
|
|
4555
4570
|
const count = import_node_fs15.default.readSync(descriptor, buffer, 0, buffer.length, 0);
|
|
4556
4571
|
if (count > MAX_BYTES) throw new Error("Capacity reservation snapshot exceeds byte limit.");
|
|
4557
|
-
const
|
|
4572
|
+
const bytes2 = buffer.subarray(0, count);
|
|
4573
|
+
const value = JSON.parse(bytes2.toString("utf8"));
|
|
4558
4574
|
validate(value);
|
|
4559
|
-
this.ledgerGeneration =
|
|
4575
|
+
this.ledgerGeneration = this.generationIdentity(stat, bytes2);
|
|
4560
4576
|
if (value.revision < minimumRevision) return { kind: "stale", revision: value.revision };
|
|
4561
4577
|
this.observedLedger = true;
|
|
4562
4578
|
return { kind: "intact", revision: value.revision, snapshot: value };
|
|
@@ -4609,20 +4625,30 @@ var CapacityStore = class {
|
|
|
4609
4625
|
return snapshot;
|
|
4610
4626
|
}
|
|
4611
4627
|
commit(snapshot) {
|
|
4612
|
-
|
|
4628
|
+
const contents = serializeSnapshot(snapshot);
|
|
4629
|
+
this.write(this.file, contents);
|
|
4613
4630
|
this.observedLedger = true;
|
|
4614
|
-
this.rememberGeneration();
|
|
4631
|
+
this.rememberGeneration(contents);
|
|
4615
4632
|
this.establish();
|
|
4616
4633
|
}
|
|
4617
4634
|
/** Remember the published snapshot identity for the next generation fence. */
|
|
4618
|
-
rememberGeneration() {
|
|
4635
|
+
rememberGeneration(contents) {
|
|
4619
4636
|
try {
|
|
4620
4637
|
const stat = import_node_fs15.default.statSync(this.file);
|
|
4621
|
-
this.ledgerGeneration = stat.isFile() ?
|
|
4638
|
+
this.ledgerGeneration = stat.isFile() ? this.generationIdentity(stat, contents) : void 0;
|
|
4622
4639
|
} catch {
|
|
4623
4640
|
this.ledgerGeneration = void 0;
|
|
4624
4641
|
}
|
|
4625
4642
|
}
|
|
4643
|
+
/**
|
|
4644
|
+
* File identity plus content digest. Deleting and recreating the snapshot can
|
|
4645
|
+
* reuse its inode number, so the inode alone cannot fence a replacement that
|
|
4646
|
+
* reuses a revision number.
|
|
4647
|
+
*/
|
|
4648
|
+
generationIdentity(stat, contents) {
|
|
4649
|
+
const digest = (0, import_node_crypto7.createHash)("sha256").update(contents).digest("hex");
|
|
4650
|
+
return `${stat.dev}:${stat.ino}:${digest}`;
|
|
4651
|
+
}
|
|
4626
4652
|
/**
|
|
4627
4653
|
* Durable evidence that recorded history is gone, for a stop whose physical
|
|
4628
4654
|
* cessation is already proven. The caller holds that proof; this writes no
|
|
@@ -4871,7 +4897,7 @@ var CapacityStore = class {
|
|
|
4871
4897
|
};
|
|
4872
4898
|
|
|
4873
4899
|
// src/core/controller-store.ts
|
|
4874
|
-
var
|
|
4900
|
+
var import_node_crypto8 = require("crypto");
|
|
4875
4901
|
var import_node_fs16 = __toESM(require("fs"));
|
|
4876
4902
|
var import_node_path20 = __toESM(require("path"));
|
|
4877
4903
|
var CONTROLLER_SNAPSHOT_BYTES = 1048576;
|
|
@@ -5173,7 +5199,7 @@ var ControllerStore = class {
|
|
|
5173
5199
|
provenance(identity2) {
|
|
5174
5200
|
if (identity2.version !== 2) throw new Error("Controller binding provenance is unavailable.");
|
|
5175
5201
|
return {
|
|
5176
|
-
digest: (0,
|
|
5202
|
+
digest: (0, import_node_crypto8.createHash)("sha256").update(JSON.stringify(["devrouter-controller-binding-v1", identity2.store, identity2.key])).digest("hex"),
|
|
5177
5203
|
enrolledEpoch: identity2.enrolledEpoch
|
|
5178
5204
|
};
|
|
5179
5205
|
}
|
|
@@ -5197,7 +5223,7 @@ var ControllerStore = class {
|
|
|
5197
5223
|
if (!current || current.store !== captured.store || current.epoch !== captured.epoch || JSON.stringify(current.fingerprint) !== JSON.stringify(captured.fingerprint) || identity2?.version !== 2)
|
|
5198
5224
|
throw new Error("Controller binding incarnation changed.");
|
|
5199
5225
|
this.checkProvenance(current, identity2);
|
|
5200
|
-
return (0,
|
|
5226
|
+
return (0, import_node_crypto8.createHmac)("sha256", Buffer.from(identity2.key, "hex")).update(JSON.stringify({ owner, config })).digest("hex");
|
|
5201
5227
|
};
|
|
5202
5228
|
}
|
|
5203
5229
|
read() {
|
|
@@ -5250,7 +5276,7 @@ var ControllerStore = class {
|
|
|
5250
5276
|
const identity2 = existing?.version === 2 ? existing : {
|
|
5251
5277
|
version: 2,
|
|
5252
5278
|
store,
|
|
5253
|
-
key: (0,
|
|
5279
|
+
key: (0, import_node_crypto8.randomBytes)(32).toString("hex"),
|
|
5254
5280
|
enrolledEpoch: epoch
|
|
5255
5281
|
};
|
|
5256
5282
|
if (existing?.version !== 2)
|
|
@@ -5296,7 +5322,7 @@ var ControllerStore = class {
|
|
|
5296
5322
|
if (!previous) {
|
|
5297
5323
|
previous = {
|
|
5298
5324
|
version: 2,
|
|
5299
|
-
store: (0,
|
|
5325
|
+
store: (0, import_node_crypto8.randomUUID)(),
|
|
5300
5326
|
epoch: 0,
|
|
5301
5327
|
revision: 0,
|
|
5302
5328
|
parkingRevision: 0,
|
|
@@ -5347,7 +5373,7 @@ function retain(session, previous) {
|
|
|
5347
5373
|
|
|
5348
5374
|
// src/core/reliability-operation-store.ts
|
|
5349
5375
|
function installedCliVersion() {
|
|
5350
|
-
return true ? "0.1.
|
|
5376
|
+
return true ? "0.1.3" : "0.0.0-dev";
|
|
5351
5377
|
}
|
|
5352
5378
|
function compareCliVersions(left, right) {
|
|
5353
5379
|
const parts = (value) => value.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
@@ -5370,7 +5396,7 @@ function identityKey(identity2) {
|
|
|
5370
5396
|
if (!import_node_path21.default.isAbsolute(identity2.repoPath) || import_node_path21.default.resolve(identity2.repoPath) !== identity2.repoPath || identity2.repoPath.includes("\0") || identity2.workspace !== null && !isReliabilityId(identity2.workspace) || !["devpod", "devsy"].includes(identity2.provider)) {
|
|
5371
5397
|
throw new Error("Invalid reliability workspace identity.");
|
|
5372
5398
|
}
|
|
5373
|
-
return (0,
|
|
5399
|
+
return (0, import_node_crypto9.createHash)("sha256").update(identity2.repoPath).digest("hex");
|
|
5374
5400
|
}
|
|
5375
5401
|
function reliabilityOperationPath(identity2) {
|
|
5376
5402
|
return import_node_path21.default.join(DEVROUTER_HOME, "reliability", `${identityKey(identity2)}.json`);
|
|
@@ -5487,6 +5513,7 @@ function validate2(record5, identity2) {
|
|
|
5487
5513
|
"revision",
|
|
5488
5514
|
"state",
|
|
5489
5515
|
"worker",
|
|
5516
|
+
"stopWorkStarted",
|
|
5490
5517
|
"effectSequence",
|
|
5491
5518
|
"outcome",
|
|
5492
5519
|
"result",
|
|
@@ -5668,13 +5695,39 @@ function validate2(record5, identity2) {
|
|
|
5668
5695
|
throw new Error("Reliability worker identity is invalid.");
|
|
5669
5696
|
}
|
|
5670
5697
|
}
|
|
5698
|
+
if (record5.stopWorkStarted !== void 0 && record5.stopWorkStarted !== null) {
|
|
5699
|
+
if (record5.stopWorkStarted !== true) throw new Error("Invalid stop work marker.");
|
|
5700
|
+
}
|
|
5701
|
+
}
|
|
5702
|
+
var ReliabilityJournalError = class extends Error {
|
|
5703
|
+
constructor(cause, location, message) {
|
|
5704
|
+
super(message);
|
|
5705
|
+
this.cause = cause;
|
|
5706
|
+
this.location = location;
|
|
5707
|
+
this.name = "ReliabilityJournalError";
|
|
5708
|
+
}
|
|
5709
|
+
};
|
|
5710
|
+
var MAX_REPORTED_ENTRY_NAME = 160;
|
|
5711
|
+
var JournalFileGuardError = class extends Error {
|
|
5712
|
+
constructor(message) {
|
|
5713
|
+
super(message);
|
|
5714
|
+
this.name = "JournalFileGuardError";
|
|
5715
|
+
}
|
|
5716
|
+
};
|
|
5717
|
+
function journalEntryLocation(name) {
|
|
5718
|
+
const printable = name.replace(/[^A-Za-z0-9._-]/g, "?");
|
|
5719
|
+
return printable.length > MAX_REPORTED_ENTRY_NAME ? `${printable.slice(0, MAX_REPORTED_ENTRY_NAME - 3)}...` : printable;
|
|
5671
5720
|
}
|
|
5672
5721
|
function createLifecycleCapacityStore(directory = import_node_path21.default.join(DEVROUTER_HOME, "controller")) {
|
|
5673
5722
|
return new CapacityStore(directory, void 0, () => {
|
|
5674
5723
|
try {
|
|
5675
5724
|
return listUnsettledCapacityBindings().floor;
|
|
5676
|
-
} catch {
|
|
5677
|
-
throw new CapacityHistoryError("capacity-history-unprovable")
|
|
5725
|
+
} catch (error) {
|
|
5726
|
+
throw error instanceof ReliabilityJournalError ? new CapacityHistoryError("capacity-history-unprovable", error.cause, error.location) : new CapacityHistoryError(
|
|
5727
|
+
"capacity-history-unprovable",
|
|
5728
|
+
"journal-enumeration-failed",
|
|
5729
|
+
null
|
|
5730
|
+
);
|
|
5678
5731
|
}
|
|
5679
5732
|
});
|
|
5680
5733
|
}
|
|
@@ -5733,11 +5786,12 @@ function readBoundedPrivateJson(file, label) {
|
|
|
5733
5786
|
try {
|
|
5734
5787
|
const stat = import_node_fs17.default.fstatSync(descriptor);
|
|
5735
5788
|
if (!stat.isFile() || stat.uid !== process.getuid?.() || stat.size > MAX_RECORD_BYTES || (stat.mode & 63) !== 0) {
|
|
5736
|
-
throw new
|
|
5789
|
+
throw new JournalFileGuardError(`${label} is not a bounded private file.`);
|
|
5737
5790
|
}
|
|
5738
5791
|
const bytes2 = Buffer.alloc(MAX_RECORD_BYTES + 1);
|
|
5739
5792
|
const count = import_node_fs17.default.readSync(descriptor, bytes2, 0, bytes2.length, 0);
|
|
5740
|
-
if (count > MAX_RECORD_BYTES)
|
|
5793
|
+
if (count > MAX_RECORD_BYTES)
|
|
5794
|
+
throw new JournalFileGuardError(`${label} exceeds its byte limit.`);
|
|
5741
5795
|
return JSON.parse(bytes2.subarray(0, count).toString("utf8"));
|
|
5742
5796
|
} finally {
|
|
5743
5797
|
import_node_fs17.default.closeSync(descriptor);
|
|
@@ -5745,7 +5799,11 @@ function readBoundedPrivateJson(file, label) {
|
|
|
5745
5799
|
}
|
|
5746
5800
|
function assertReliabilityEnumerationTime(startedAtMs) {
|
|
5747
5801
|
if (performance.now() - startedAtMs > MAX_RELIABILITY_ENUMERATION_MS)
|
|
5748
|
-
throw new
|
|
5802
|
+
throw new ReliabilityJournalError(
|
|
5803
|
+
"journal-enumeration-timeout",
|
|
5804
|
+
null,
|
|
5805
|
+
"Reliability journal enumeration exceeded its time limit."
|
|
5806
|
+
);
|
|
5749
5807
|
}
|
|
5750
5808
|
function reliabilityDirectory() {
|
|
5751
5809
|
return import_node_path21.default.join(DEVROUTER_HOME, "reliability");
|
|
@@ -5766,22 +5824,56 @@ function listReliabilityOperations() {
|
|
|
5766
5824
|
stat = import_node_fs17.default.lstatSync(directory);
|
|
5767
5825
|
} catch (error) {
|
|
5768
5826
|
if (error.code === "ENOENT") return [];
|
|
5769
|
-
throw
|
|
5827
|
+
throw new ReliabilityJournalError(
|
|
5828
|
+
"journal-directory-unreadable",
|
|
5829
|
+
null,
|
|
5830
|
+
"Reliability journal directory could not be read."
|
|
5831
|
+
);
|
|
5770
5832
|
}
|
|
5771
5833
|
if (!stat.isDirectory() || stat.isSymbolicLink())
|
|
5772
|
-
throw new
|
|
5834
|
+
throw new ReliabilityJournalError(
|
|
5835
|
+
"journal-directory-unsafe",
|
|
5836
|
+
null,
|
|
5837
|
+
"Reliability journal directory is unsafe."
|
|
5838
|
+
);
|
|
5773
5839
|
if (stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
|
|
5774
|
-
throw new
|
|
5840
|
+
throw new ReliabilityJournalError(
|
|
5841
|
+
"journal-directory-unsafe",
|
|
5842
|
+
null,
|
|
5843
|
+
"Reliability journal directory is not private."
|
|
5844
|
+
);
|
|
5775
5845
|
const entries = [];
|
|
5776
|
-
|
|
5846
|
+
let handle;
|
|
5847
|
+
try {
|
|
5848
|
+
handle = import_node_fs17.default.opendirSync(directory);
|
|
5849
|
+
} catch {
|
|
5850
|
+
throw new ReliabilityJournalError(
|
|
5851
|
+
"journal-directory-unreadable",
|
|
5852
|
+
null,
|
|
5853
|
+
"Reliability journal directory could not be read."
|
|
5854
|
+
);
|
|
5855
|
+
}
|
|
5777
5856
|
try {
|
|
5778
5857
|
for (; ; ) {
|
|
5779
5858
|
assertReliabilityEnumerationTime(startedAtMs);
|
|
5780
|
-
|
|
5859
|
+
let entry;
|
|
5860
|
+
try {
|
|
5861
|
+
entry = handle.readSync();
|
|
5862
|
+
} catch {
|
|
5863
|
+
throw new ReliabilityJournalError(
|
|
5864
|
+
"journal-directory-unreadable",
|
|
5865
|
+
null,
|
|
5866
|
+
"Reliability journal directory could not be read."
|
|
5867
|
+
);
|
|
5868
|
+
}
|
|
5781
5869
|
if (!entry) break;
|
|
5782
5870
|
entries.push(entry);
|
|
5783
5871
|
if (entries.length > MAX_RELIABILITY_DIRECTORY_ENTRIES)
|
|
5784
|
-
throw new
|
|
5872
|
+
throw new ReliabilityJournalError(
|
|
5873
|
+
"journal-enumeration-limit",
|
|
5874
|
+
null,
|
|
5875
|
+
"Reliability journal directory exceeds its entry limit."
|
|
5876
|
+
);
|
|
5785
5877
|
}
|
|
5786
5878
|
} finally {
|
|
5787
5879
|
handle.closeSync();
|
|
@@ -5790,27 +5882,82 @@ function listReliabilityOperations() {
|
|
|
5790
5882
|
for (const entry of entries) {
|
|
5791
5883
|
assertReliabilityEnumerationTime(startedAtMs);
|
|
5792
5884
|
if (entry.isSymbolicLink())
|
|
5793
|
-
throw new
|
|
5885
|
+
throw new ReliabilityJournalError(
|
|
5886
|
+
"journal-entry-unsupported",
|
|
5887
|
+
journalEntryLocation(entry.name),
|
|
5888
|
+
"Reliability journal directory contains a symlink."
|
|
5889
|
+
);
|
|
5794
5890
|
if (!entry.isFile())
|
|
5795
|
-
throw new
|
|
5891
|
+
throw new ReliabilityJournalError(
|
|
5892
|
+
"journal-entry-unsupported",
|
|
5893
|
+
journalEntryLocation(entry.name),
|
|
5894
|
+
"Reliability journal directory contains an unsupported entry."
|
|
5895
|
+
);
|
|
5796
5896
|
if (RELIABILITY_LOCK_NAME_RE.test(entry.name) || RELIABILITY_ATOMIC_TEMP_NAME_RE.test(entry.name))
|
|
5797
5897
|
continue;
|
|
5798
5898
|
const match = RELIABILITY_JOURNAL_NAME_RE.exec(entry.name);
|
|
5799
|
-
if (!match)
|
|
5899
|
+
if (!match)
|
|
5900
|
+
throw new ReliabilityJournalError(
|
|
5901
|
+
"journal-entry-unsupported",
|
|
5902
|
+
journalEntryLocation(entry.name),
|
|
5903
|
+
"Reliability journal directory contains an unsupported entry."
|
|
5904
|
+
);
|
|
5800
5905
|
journals.push({ name: entry.name, key: match[1] });
|
|
5801
5906
|
}
|
|
5802
5907
|
if (journals.length > MAX_RELIABILITY_JOURNALS)
|
|
5803
|
-
throw new
|
|
5908
|
+
throw new ReliabilityJournalError(
|
|
5909
|
+
"journal-enumeration-limit",
|
|
5910
|
+
null,
|
|
5911
|
+
"Reliability journal directory exceeds its journal limit."
|
|
5912
|
+
);
|
|
5804
5913
|
journals.sort((left, right) => left.name.localeCompare(right.name));
|
|
5805
5914
|
const records = [];
|
|
5806
5915
|
for (const journal of journals) {
|
|
5807
5916
|
assertReliabilityEnumerationTime(startedAtMs);
|
|
5808
5917
|
const file = import_node_path21.default.join(directory, journal.name);
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5918
|
+
let raw;
|
|
5919
|
+
try {
|
|
5920
|
+
raw = readBoundedPrivateJson(file, "Reliability journal");
|
|
5921
|
+
} catch (error) {
|
|
5922
|
+
const fsCode = error?.code;
|
|
5923
|
+
const cause = error instanceof JournalFileGuardError ? "journal-entry-unsafe" : error instanceof SyntaxError || !fsCode ? "journal-invalid" : "journal-unreadable";
|
|
5924
|
+
throw new ReliabilityJournalError(
|
|
5925
|
+
cause,
|
|
5926
|
+
journalEntryLocation(journal.name),
|
|
5927
|
+
error instanceof JournalFileGuardError ? error.message : cause === "journal-invalid" ? "Reliability journal entry is invalid." : "Reliability journal entry could not be read."
|
|
5928
|
+
);
|
|
5929
|
+
}
|
|
5930
|
+
if (raw === void 0)
|
|
5931
|
+
throw new ReliabilityJournalError(
|
|
5932
|
+
"journal-unstable",
|
|
5933
|
+
journalEntryLocation(journal.name),
|
|
5934
|
+
"Reliability journal disappeared during enumeration."
|
|
5935
|
+
);
|
|
5936
|
+
let identity2;
|
|
5937
|
+
let record5;
|
|
5938
|
+
try {
|
|
5939
|
+
identity2 = journalIdentity(raw);
|
|
5940
|
+
if (identityKey(identity2) !== journal.key)
|
|
5941
|
+
throw new ReliabilityJournalError(
|
|
5942
|
+
"journal-identity-mismatch",
|
|
5943
|
+
journalEntryLocation(journal.name),
|
|
5944
|
+
"Reliability journal filename does not match its identity."
|
|
5945
|
+
);
|
|
5946
|
+
record5 = readReliabilityOperation(identity2);
|
|
5947
|
+
} catch (error) {
|
|
5948
|
+
if (error instanceof ReliabilityJournalError) throw error;
|
|
5949
|
+
throw new ReliabilityJournalError(
|
|
5950
|
+
"journal-invalid",
|
|
5951
|
+
journalEntryLocation(journal.name),
|
|
5952
|
+
"Reliability journal entry is invalid."
|
|
5953
|
+
);
|
|
5954
|
+
}
|
|
5955
|
+
if (!record5)
|
|
5956
|
+
throw new ReliabilityJournalError(
|
|
5957
|
+
"journal-unstable",
|
|
5958
|
+
journalEntryLocation(journal.name),
|
|
5959
|
+
"Reliability journal disappeared during enumeration."
|
|
5960
|
+
);
|
|
5814
5961
|
records.push(record5);
|
|
5815
5962
|
assertReliabilityEnumerationTime(startedAtMs);
|
|
5816
5963
|
}
|
|
@@ -5886,7 +6033,7 @@ function clearStartupWitness(identity2, fence) {
|
|
|
5886
6033
|
}
|
|
5887
6034
|
|
|
5888
6035
|
// src/core/repo-config.ts
|
|
5889
|
-
var
|
|
6036
|
+
var import_node_crypto10 = require("crypto");
|
|
5890
6037
|
var import_node_fs18 = __toESM(require("fs"));
|
|
5891
6038
|
var import_node_path22 = __toESM(require("path"));
|
|
5892
6039
|
var import_yaml3 = __toESM(require("yaml"));
|
|
@@ -7163,7 +7310,7 @@ function loadRepoConfig(repoPath, read2 = (file) => import_node_fs18.default.rea
|
|
|
7163
7310
|
const config = parseConfig(parsed ?? {}, configPath);
|
|
7164
7311
|
const requiredVersion = config.devrouter?.version;
|
|
7165
7312
|
if (requiredVersion && !hasWarnedVersionMismatch) {
|
|
7166
|
-
const cliVersion = true ? "0.1.
|
|
7313
|
+
const cliVersion = true ? "0.1.3" : "0.0.0-dev";
|
|
7167
7314
|
if (cliVersion !== "0.0.0-dev" && compareSemver2(requiredVersion, cliVersion) > 0) {
|
|
7168
7315
|
hasWarnedVersionMismatch = true;
|
|
7169
7316
|
process.stderr.write(
|
|
@@ -7342,7 +7489,7 @@ async function ensureNetwork(name) {
|
|
|
7342
7489
|
|
|
7343
7490
|
// src/core/tls.ts
|
|
7344
7491
|
var import_node_child_process13 = require("child_process");
|
|
7345
|
-
var
|
|
7492
|
+
var import_node_crypto11 = require("crypto");
|
|
7346
7493
|
var import_node_fs19 = __toESM(require("fs"));
|
|
7347
7494
|
var import_node_path23 = __toESM(require("path"));
|
|
7348
7495
|
var DEFAULT_TLS_CERT_HOSTS = ["localhost", "*.localhost"];
|
|
@@ -7412,7 +7559,7 @@ function parseDnsHostsFromSubjectAltName(subjectAltName) {
|
|
|
7412
7559
|
return normalizeUniqueHosts(names);
|
|
7413
7560
|
}
|
|
7414
7561
|
function parseCertificateDnsHosts(pem) {
|
|
7415
|
-
const certificate = new
|
|
7562
|
+
const certificate = new import_node_crypto11.X509Certificate(pem);
|
|
7416
7563
|
const subjectAltName = certificate.subjectAltName ?? "";
|
|
7417
7564
|
if (subjectAltName.length === 0) {
|
|
7418
7565
|
return [];
|
|
@@ -8241,6 +8388,41 @@ function stopInitialManagedDevsyWorkspace(repoPath, devsyId) {
|
|
|
8241
8388
|
throw new Error("Initial managed stop left a workload running.");
|
|
8242
8389
|
return true;
|
|
8243
8390
|
}
|
|
8391
|
+
function restoreRecordedManagedDevcontainerConfig(repoPath) {
|
|
8392
|
+
const plan = recordedManagedDevcontainerRestorePlan(repoPath);
|
|
8393
|
+
if (!plan) return false;
|
|
8394
|
+
writeManagedDevcontainerConfig(plan);
|
|
8395
|
+
if (inspectManagedDevcontainerGeneratedConfig(plan).status !== "valid") {
|
|
8396
|
+
throw new Error(
|
|
8397
|
+
`Managed Dev Container path '${plan.generatedPath}' could not be restored from the recorded state.`
|
|
8398
|
+
);
|
|
8399
|
+
}
|
|
8400
|
+
return true;
|
|
8401
|
+
}
|
|
8402
|
+
function recordedManagedDevcontainerRestorePlan(repoPath) {
|
|
8403
|
+
try {
|
|
8404
|
+
if (!import_node_fs21.default.existsSync(import_node_path25.default.join(repoPath, ".devrouter.yml"))) return void 0;
|
|
8405
|
+
const linked = isLinkedWorktree(repoPath);
|
|
8406
|
+
const workspace = linked ? resolveWorktreeWorkspace(repoPath) : void 0;
|
|
8407
|
+
const state = readManagedRuntimeState(repoPath, workspace);
|
|
8408
|
+
if (!state) return void 0;
|
|
8409
|
+
const runtime = loadRuntimeConfig(repoPath, workspace ?? "", state.profile);
|
|
8410
|
+
if (!runtime.config.managedRuntime || runtime.profile !== state.profile || runtime.workspace !== workspace)
|
|
8411
|
+
return void 0;
|
|
8412
|
+
const plan = inspectManagedDevcontainerConfig({
|
|
8413
|
+
repoPath,
|
|
8414
|
+
config: runtime.config,
|
|
8415
|
+
profile: runtime.resolvedProfile,
|
|
8416
|
+
linked
|
|
8417
|
+
});
|
|
8418
|
+
if (inspectManagedDevcontainerGeneratedConfig(plan).status !== "missing") return void 0;
|
|
8419
|
+
if (plan.sourceConfigSha256 !== state.sourceConfigSha256 || plan.effectiveConfigSha256 !== state.effectiveConfigSha256)
|
|
8420
|
+
return void 0;
|
|
8421
|
+
return plan;
|
|
8422
|
+
} catch {
|
|
8423
|
+
return void 0;
|
|
8424
|
+
}
|
|
8425
|
+
}
|
|
8244
8426
|
function stopRetainedManagedDevsyWorkspace(options) {
|
|
8245
8427
|
const { repoPath, devsyId } = options;
|
|
8246
8428
|
const linked = isLinkedWorktree(repoPath);
|
|
@@ -8478,12 +8660,12 @@ function networkProviderEnvironment(binding, source) {
|
|
|
8478
8660
|
}
|
|
8479
8661
|
|
|
8480
8662
|
// src/core/network-claim-lookup.ts
|
|
8481
|
-
var
|
|
8663
|
+
var import_node_crypto13 = require("crypto");
|
|
8482
8664
|
var import_node_fs24 = __toESM(require("fs"));
|
|
8483
8665
|
var import_node_path28 = __toESM(require("path"));
|
|
8484
8666
|
|
|
8485
8667
|
// src/core/network-claims.ts
|
|
8486
|
-
var
|
|
8668
|
+
var import_node_crypto12 = require("crypto");
|
|
8487
8669
|
var import_node_fs23 = __toESM(require("fs"));
|
|
8488
8670
|
var import_node_path27 = __toESM(require("path"));
|
|
8489
8671
|
|
|
@@ -9673,7 +9855,7 @@ function assertDirectory(directory) {
|
|
|
9673
9855
|
}
|
|
9674
9856
|
}
|
|
9675
9857
|
function hashKey(value) {
|
|
9676
|
-
return (0,
|
|
9858
|
+
return (0, import_node_crypto12.createHash)("sha256").update(value).digest("hex");
|
|
9677
9859
|
}
|
|
9678
9860
|
function cloneFence(fence) {
|
|
9679
9861
|
return { ...fence };
|
|
@@ -9701,7 +9883,7 @@ function claimError(code, message) {
|
|
|
9701
9883
|
function networkOwnerKey(repoPath) {
|
|
9702
9884
|
if (!import_node_path28.default.isAbsolute(repoPath) || import_node_path28.default.resolve(repoPath) !== repoPath)
|
|
9703
9885
|
throw new Error("Invalid network owner path.");
|
|
9704
|
-
return (0,
|
|
9886
|
+
return (0, import_node_crypto13.createHash)("sha256").update(repoPath).digest("hex");
|
|
9705
9887
|
}
|
|
9706
9888
|
function findOwnedNetworkClaim(repoPath, root = NETWORK_CLAIMS_ROOT) {
|
|
9707
9889
|
let names;
|
|
@@ -9741,7 +9923,7 @@ function findOwnedNetworkClaim(repoPath, root = NETWORK_CLAIMS_ROOT) {
|
|
|
9741
9923
|
} finally {
|
|
9742
9924
|
import_node_fs24.default.closeSync(descriptor);
|
|
9743
9925
|
}
|
|
9744
|
-
if (typeof value.daemonId !== "string" || `${(0,
|
|
9926
|
+
if (typeof value.daemonId !== "string" || `${(0, import_node_crypto13.createHash)("sha256").update(value.daemonId).digest("hex")}.json` !== file)
|
|
9745
9927
|
throw new Error("Retained network daemon identity is invalid.");
|
|
9746
9928
|
const claim2 = readNetworkClaim({
|
|
9747
9929
|
daemonId: value.daemonId,
|
|
@@ -9758,7 +9940,7 @@ function findOwnedNetworkClaim(repoPath, root = NETWORK_CLAIMS_ROOT) {
|
|
|
9758
9940
|
|
|
9759
9941
|
// src/core/network-provider-inspect.ts
|
|
9760
9942
|
var import_node_child_process15 = require("child_process");
|
|
9761
|
-
var
|
|
9943
|
+
var import_node_crypto14 = require("crypto");
|
|
9762
9944
|
function object2(value) {
|
|
9763
9945
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
9764
9946
|
throw new Error("Invalid provider evidence.");
|
|
@@ -9778,7 +9960,7 @@ function qualifyNetworkProviderDefinition(value, provider) {
|
|
|
9778
9960
|
if (definition.name !== "docker" || ![true, "true"].includes(agent.local) || agent.driver !== void 0 && agent.driver !== "docker" && agent.driver !== "" || docker.path !== "${DOCKER_PATH}" || docker.builder !== "${DOCKER_BUILDER}" || ![false, "false"].includes(docker.install) || (provider === "devsy" ? docker.elevation !== "${DOCKER_ELEVATION}" : docker.elevation !== void 0 && docker.elevation !== "") || env.DOCKER_HOST !== "${DOCKER_HOST}" || Object.keys(env).some((key) => key !== "DOCKER_HOST") || host.global !== true || dockerPath.default !== "docker" || actualCommand.length !== 1 || typeof actualCommand[0] !== "string" || actualCommand[0].trim() !== command || Object.keys(exec).some((key) => key !== "command") || agent.path !== void 0 && agent.path !== "" || agent.downloadURL !== void 0 && agent.downloadURL !== "") {
|
|
9779
9961
|
throw new Error("Docker provider definition is not qualified for network allocation.");
|
|
9780
9962
|
}
|
|
9781
|
-
return (0,
|
|
9963
|
+
return (0, import_node_crypto14.createHash)("sha256").update(JSON.stringify({ name: definition.name, agent, options, exec })).digest("hex");
|
|
9782
9964
|
}
|
|
9783
9965
|
function read(provider, args) {
|
|
9784
9966
|
const result = (0, import_node_child_process15.spawnSync)(provider, args, {
|
|
@@ -9996,6 +10178,7 @@ function inspectExactOwnership(devsyId, worktreePath) {
|
|
|
9996
10178
|
}
|
|
9997
10179
|
function mutateOwnedDevsyWorkspace(action, devsyId, worktreePath) {
|
|
9998
10180
|
return withMutationLock(`Devsy ${action}`, worktreePath, () => {
|
|
10181
|
+
restoreRecordedManagedDevcontainerConfig(worktreePath);
|
|
9999
10182
|
if (action === "stop") {
|
|
10000
10183
|
const stopped = stopRetainedManagedDevsyWorkspace({
|
|
10001
10184
|
repoPath: worktreePath,
|
|
@@ -10161,7 +10344,7 @@ var ExecutionOutcomeError = class extends Error {
|
|
|
10161
10344
|
};
|
|
10162
10345
|
|
|
10163
10346
|
// src/core/reliability-lifecycle.ts
|
|
10164
|
-
var
|
|
10347
|
+
var import_node_crypto22 = require("crypto");
|
|
10165
10348
|
var import_node_path40 = __toESM(require("path"));
|
|
10166
10349
|
var import_node_util7 = require("util");
|
|
10167
10350
|
|
|
@@ -10170,7 +10353,7 @@ var import_node_fs26 = __toESM(require("fs"));
|
|
|
10170
10353
|
var import_node_path30 = __toESM(require("path"));
|
|
10171
10354
|
|
|
10172
10355
|
// src/core/controller-client.ts
|
|
10173
|
-
var
|
|
10356
|
+
var import_node_crypto15 = require("crypto");
|
|
10174
10357
|
var import_node_fs27 = __toESM(require("fs"));
|
|
10175
10358
|
var import_node_net2 = __toESM(require("net"));
|
|
10176
10359
|
var import_node_path32 = __toESM(require("path"));
|
|
@@ -10185,7 +10368,7 @@ var CONTROLLER_PROFILE_RE = new RegExp(
|
|
|
10185
10368
|
);
|
|
10186
10369
|
|
|
10187
10370
|
// src/core/controller-monitor.ts
|
|
10188
|
-
var
|
|
10371
|
+
var import_node_crypto16 = require("crypto");
|
|
10189
10372
|
var import_node_util6 = require("util");
|
|
10190
10373
|
|
|
10191
10374
|
// src/core/reliability-output.ts
|
|
@@ -10516,7 +10699,7 @@ function handleOperationRequest(state, event) {
|
|
|
10516
10699
|
if (state.phase === "stopping" || state.desired === "parked-for-capacity")
|
|
10517
10700
|
return blocked(
|
|
10518
10701
|
state,
|
|
10519
|
-
`phase is '${state.phase}' with desired '${state.desired}'; wait for the running stop to finish.`
|
|
10702
|
+
`phase is '${state.phase}' with desired '${state.desired}'; wait for the running stop to finish, or when no stop worker is running rerun 'devrouter stop <path> --delete', removing the exact provider registration first when the stop cannot prove the recorded population.`
|
|
10520
10703
|
);
|
|
10521
10704
|
if (event.kind === "exec" && (!event.runtimeRunning || state.desired !== "running" && state.intentRevision !== 0))
|
|
10522
10705
|
return blocked(
|
|
@@ -10974,7 +11157,7 @@ function stepReliability(state, event, nowMs) {
|
|
|
10974
11157
|
|
|
10975
11158
|
// src/core/reliability-worker.ts
|
|
10976
11159
|
var import_node_child_process17 = require("child_process");
|
|
10977
|
-
var
|
|
11160
|
+
var import_node_crypto17 = require("crypto");
|
|
10978
11161
|
var import_node_fs28 = __toESM(require("fs"));
|
|
10979
11162
|
var import_node_path33 = __toESM(require("path"));
|
|
10980
11163
|
|
|
@@ -11043,7 +11226,7 @@ function workerGroupAbsent(pid) {
|
|
|
11043
11226
|
}
|
|
11044
11227
|
|
|
11045
11228
|
// src/core/workspace-ensure.ts
|
|
11046
|
-
var
|
|
11229
|
+
var import_node_child_process28 = require("child_process");
|
|
11047
11230
|
var import_node_fs34 = __toESM(require("fs"));
|
|
11048
11231
|
var import_node_path39 = __toESM(require("path"));
|
|
11049
11232
|
|
|
@@ -11742,7 +11925,7 @@ async function runManagedHostPreparation(repoPath, config) {
|
|
|
11742
11925
|
|
|
11743
11926
|
// src/core/managed-post-start.ts
|
|
11744
11927
|
var import_node_child_process22 = require("child_process");
|
|
11745
|
-
var
|
|
11928
|
+
var import_node_crypto18 = require("crypto");
|
|
11746
11929
|
var import_node_fs31 = __toESM(require("fs"));
|
|
11747
11930
|
var import_node_path36 = __toESM(require("path"));
|
|
11748
11931
|
var MANAGED_MARKER = "devrouter:managed devcontainer";
|
|
@@ -11799,7 +11982,7 @@ function readRegularFile(filePath) {
|
|
|
11799
11982
|
return readRegularFileBytes(filePath)?.toString("utf-8");
|
|
11800
11983
|
}
|
|
11801
11984
|
function adapterFingerprint(adapter) {
|
|
11802
|
-
return (0,
|
|
11985
|
+
return (0, import_node_crypto18.createHash)("sha256").update(adapter).digest("hex");
|
|
11803
11986
|
}
|
|
11804
11987
|
function resolveProcessHelperPath() {
|
|
11805
11988
|
const candidates = [
|
|
@@ -11951,26 +12134,125 @@ function runManagedProcessAction(options) {
|
|
|
11951
12134
|
return "drifted";
|
|
11952
12135
|
}
|
|
11953
12136
|
|
|
12137
|
+
// src/core/managed-mount-nesting.ts
|
|
12138
|
+
var import_node_child_process23 = require("child_process");
|
|
12139
|
+
var NESTED_MOUNT_REASON = "no configured mount is nested inside another configured mount";
|
|
12140
|
+
var UNREADABLE_MOUNT_REASON = "the container's own mount table could not be read";
|
|
12141
|
+
function normalizeDestination(value) {
|
|
12142
|
+
const trimmed = value.trim().replace(/\/+$/, "");
|
|
12143
|
+
return trimmed === "" ? "/" : trimmed;
|
|
12144
|
+
}
|
|
12145
|
+
function isNestedWithin(destination, candidate) {
|
|
12146
|
+
return candidate === "/" ? destination !== "/" : destination.startsWith(`${candidate}/`);
|
|
12147
|
+
}
|
|
12148
|
+
function unescapeMountField(value) {
|
|
12149
|
+
return value.replace(
|
|
12150
|
+
/\\([0-7]{3})/g,
|
|
12151
|
+
(_match, octal) => String.fromCharCode(Number.parseInt(octal, 8))
|
|
12152
|
+
);
|
|
12153
|
+
}
|
|
12154
|
+
function parseMountinfoDestinations(text4) {
|
|
12155
|
+
const destinations = /* @__PURE__ */ new Set();
|
|
12156
|
+
for (const line of text4.split("\n")) {
|
|
12157
|
+
const fields2 = line.split(" ");
|
|
12158
|
+
if (fields2.length < 5 || !/^\d+$/.test(fields2[0] ?? "")) continue;
|
|
12159
|
+
destinations.add(normalizeDestination(unescapeMountField(fields2[4] ?? "")));
|
|
12160
|
+
}
|
|
12161
|
+
return [...destinations];
|
|
12162
|
+
}
|
|
12163
|
+
function isNestedIndex(destinations, index) {
|
|
12164
|
+
return destinations.some(
|
|
12165
|
+
(candidate, other) => other !== index && isNestedWithin(destinations[index] ?? "", candidate)
|
|
12166
|
+
);
|
|
12167
|
+
}
|
|
12168
|
+
function configuredDestinations(configured) {
|
|
12169
|
+
return configured.map((mount) => normalizeDestination(mount.Destination));
|
|
12170
|
+
}
|
|
12171
|
+
function classifyManagedMountNesting(configured, effectiveDestinations, containerId) {
|
|
12172
|
+
const destinations = configuredDestinations(configured);
|
|
12173
|
+
const nested = configured.filter((_mount, index) => isNestedIndex(destinations, index));
|
|
12174
|
+
const identity2 = containerId ? { container: containerId } : {};
|
|
12175
|
+
if (nested.length === 0) {
|
|
12176
|
+
return { status: "not-applicable", checked: [], unwound: [], reason: NESTED_MOUNT_REASON };
|
|
12177
|
+
}
|
|
12178
|
+
if (effectiveDestinations === null) {
|
|
12179
|
+
return {
|
|
12180
|
+
status: "unverified",
|
|
12181
|
+
checked: [],
|
|
12182
|
+
unwound: [],
|
|
12183
|
+
...identity2,
|
|
12184
|
+
reason: UNREADABLE_MOUNT_REASON
|
|
12185
|
+
};
|
|
12186
|
+
}
|
|
12187
|
+
const effective = new Set(effectiveDestinations.map(normalizeDestination));
|
|
12188
|
+
const checked = [];
|
|
12189
|
+
const unwound = [];
|
|
12190
|
+
for (const mount of nested) {
|
|
12191
|
+
const destination = normalizeDestination(mount.Destination);
|
|
12192
|
+
if (effective.has(destination)) {
|
|
12193
|
+
checked.push(mount.Destination);
|
|
12194
|
+
continue;
|
|
12195
|
+
}
|
|
12196
|
+
const nestedWithin = destinations.filter((candidate) => isNestedWithin(destination, candidate)).sort((a, b) => a.length - b.length)[0];
|
|
12197
|
+
unwound.push({
|
|
12198
|
+
destination: mount.Destination,
|
|
12199
|
+
source: mount.Source,
|
|
12200
|
+
nestedWithin: nestedWithin ?? "/"
|
|
12201
|
+
});
|
|
12202
|
+
}
|
|
12203
|
+
return {
|
|
12204
|
+
status: unwound.length > 0 ? "unwound" : "effective",
|
|
12205
|
+
checked,
|
|
12206
|
+
unwound,
|
|
12207
|
+
...identity2
|
|
12208
|
+
};
|
|
12209
|
+
}
|
|
12210
|
+
function readContainerMountinfo(containerId, options) {
|
|
12211
|
+
const result = (0, import_node_child_process23.spawnSync)("docker", ["exec", containerId, "cat", "/proc/self/mountinfo"], {
|
|
12212
|
+
encoding: "utf-8",
|
|
12213
|
+
timeout: options.timeoutMs ?? 5e3,
|
|
12214
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
12215
|
+
...networkDockerOptions()
|
|
12216
|
+
});
|
|
12217
|
+
if (result.status !== 0 || typeof result.stdout !== "string") return null;
|
|
12218
|
+
const destinations = parseMountinfoDestinations(result.stdout);
|
|
12219
|
+
return destinations.length > 0 ? destinations : null;
|
|
12220
|
+
}
|
|
12221
|
+
function observeManagedMountNesting(container, options = {}) {
|
|
12222
|
+
const destinations = configuredDestinations(container.mounts);
|
|
12223
|
+
const effective = destinations.some((_destination, index) => isNestedIndex(destinations, index)) ? readContainerMountinfo(container.id, options) : null;
|
|
12224
|
+
return classifyManagedMountNesting(container.mounts, effective, container.id);
|
|
12225
|
+
}
|
|
12226
|
+
|
|
11954
12227
|
// src/core/managed-runtime-status.ts
|
|
12228
|
+
function recoveryPath(repoPath) {
|
|
12229
|
+
if (/^[A-Za-z0-9_@%+=:,./-]+$/.test(repoPath)) return repoPath;
|
|
12230
|
+
return `'${repoPath.replaceAll("'", "'\\''")}'`;
|
|
12231
|
+
}
|
|
11955
12232
|
function reliabilityRecovery(reason2, repoPath) {
|
|
12233
|
+
const checkout = recoveryPath(repoPath);
|
|
11956
12234
|
switch (reason2) {
|
|
11957
12235
|
case "stop-incomplete":
|
|
11958
12236
|
case "operation-unknown":
|
|
11959
|
-
return [`Run: devrouter stop ${
|
|
12237
|
+
return [`Run: devrouter stop ${checkout}`, `Run: devrouter ensure ${checkout} --repair`];
|
|
11960
12238
|
case "start-refused":
|
|
11961
12239
|
return [
|
|
11962
|
-
`Run: devrouter doctor ${
|
|
11963
|
-
`Stop the holding workspace or change this repository's own published binding, then run: devrouter ensure ${
|
|
12240
|
+
`Run: devrouter doctor --repo ${checkout} to read the refused fixed host-port claim and its holder`,
|
|
12241
|
+
`Stop the holding workspace or change this repository's own published binding, then run: devrouter ensure ${checkout}`
|
|
11964
12242
|
];
|
|
11965
12243
|
case "unadmittable":
|
|
11966
12244
|
return [
|
|
11967
|
-
`Run: devrouter stop ${
|
|
11968
|
-
`Lower the requested capacity in .devrouter.yml, then run: devrouter ensure ${
|
|
12245
|
+
`Run: devrouter stop ${checkout}`,
|
|
12246
|
+
`Lower the requested capacity in .devrouter.yml, then run: devrouter ensure ${checkout}`
|
|
11969
12247
|
];
|
|
11970
12248
|
case "capacity-parked":
|
|
11971
|
-
return [
|
|
12249
|
+
return [
|
|
12250
|
+
"Parked for host capacity: the enrolled foreground controller resumes this checkout after its pressure dwell and live-consumer checks pass.",
|
|
12251
|
+
`Inspect the parked intent with: devrouter status --repo ${checkout} --json`,
|
|
12252
|
+
`'devrouter ensure' refuses while the journal stays parked; release the intent instead with: devrouter stop ${checkout}`
|
|
12253
|
+
];
|
|
11972
12254
|
case "capacity-waiting":
|
|
11973
|
-
return [`Run: devrouter ensure ${
|
|
12255
|
+
return [`Run: devrouter ensure ${checkout} (still queued until capacity frees up)`];
|
|
11974
12256
|
default:
|
|
11975
12257
|
return [];
|
|
11976
12258
|
}
|
|
@@ -12222,6 +12504,7 @@ function inspectManagedRuntime(options) {
|
|
|
12222
12504
|
(mount) => mount.Type === "bind" && sameWorkspacePath(mount.Source, repoPath)
|
|
12223
12505
|
)?.Destination;
|
|
12224
12506
|
const primaryActive = Boolean(primary?.state.Running);
|
|
12507
|
+
const mountNesting = primary && primaryActive ? observeManagedMountNesting(primary) : void 0;
|
|
12225
12508
|
if (generatedConfigMissing && (state || primaryActive || managedServicesActive)) {
|
|
12226
12509
|
drift.push("managed generated Dev Container configuration is missing");
|
|
12227
12510
|
}
|
|
@@ -12288,7 +12571,8 @@ function inspectManagedRuntime(options) {
|
|
|
12288
12571
|
processStatuses,
|
|
12289
12572
|
activeServices: sortedUnique(activeServices),
|
|
12290
12573
|
activeProcesses: sortedUnique(activeProcesses),
|
|
12291
|
-
drift
|
|
12574
|
+
drift,
|
|
12575
|
+
...mountNesting ? { mountNesting } : {}
|
|
12292
12576
|
};
|
|
12293
12577
|
}
|
|
12294
12578
|
function collectManagedRuntimeStatus(options) {
|
|
@@ -12436,6 +12720,7 @@ function collectManagedRuntimeStatus(options) {
|
|
|
12436
12720
|
baseServiceStatuses: inspection.baseStatuses,
|
|
12437
12721
|
processStatuses: inspection.processStatuses,
|
|
12438
12722
|
drift: sortedUnique(drift),
|
|
12723
|
+
...inspection.mountNesting ? { mountNesting: inspection.mountNesting } : {},
|
|
12439
12724
|
...inspection.plan?.sourceConfigSha256 || state?.sourceConfigSha256 ? { sourceConfigSha256: inspection.plan?.sourceConfigSha256 ?? state?.sourceConfigSha256 } : {},
|
|
12440
12725
|
...inspection.plan?.effectiveConfigSha256 || state?.effectiveConfigSha256 ? {
|
|
12441
12726
|
effectiveConfigSha256: inspection.plan?.effectiveConfigSha256 ?? state?.effectiveConfigSha256
|
|
@@ -12445,19 +12730,19 @@ function collectManagedRuntimeStatus(options) {
|
|
|
12445
12730
|
}
|
|
12446
12731
|
|
|
12447
12732
|
// src/core/network-inventory.ts
|
|
12448
|
-
var
|
|
12733
|
+
var import_node_child_process24 = require("child_process");
|
|
12449
12734
|
var NetworkInventoryError = class extends Error {
|
|
12450
12735
|
};
|
|
12451
12736
|
var MAX_IDS = 4096;
|
|
12452
12737
|
var MAX_OUTPUT_BYTES = 2 * 1024 * 1024;
|
|
12453
12738
|
var FULL_ID = /^[a-f0-9]{64}$/;
|
|
12454
|
-
var NETWORK_TEMPLATE = '{"id":{{json .Id}},"name":{{json .Name}},"driver":{{json .Driver}},"ipam":{{json .IPAM.Config}},"activeEndpoints":{{len .Containers}},"project":{{json (index .Labels "com.docker.compose.project")}},"network":{{json (index .Labels "com.docker.compose.network")}}';
|
|
12739
|
+
var NETWORK_TEMPLATE = '{"id":{{json .Id}},"name":{{json .Name}},"driver":{{json .Driver}},"ipam":{{json .IPAM.Config}},"activeEndpoints":{{len .Containers}},"project":{{json (index .Labels "com.docker.compose.project")}},"network":{{json (index .Labels "com.docker.compose.network")}}}';
|
|
12455
12740
|
var CONTAINER_TEMPLATE = '{"id":{{json .Id}},"networks":{{json .NetworkSettings.Networks}}}';
|
|
12456
12741
|
function readDocker(endpoint, args, timeoutMs) {
|
|
12457
12742
|
const env = { ...process.env };
|
|
12458
12743
|
delete env.DOCKER_CONTEXT;
|
|
12459
12744
|
delete env.DOCKER_HOST;
|
|
12460
|
-
const result = (0,
|
|
12745
|
+
const result = (0, import_node_child_process24.spawnSync)("docker", ["--host", endpoint, ...args], {
|
|
12461
12746
|
env,
|
|
12462
12747
|
encoding: "utf-8",
|
|
12463
12748
|
timeout: timeoutMs,
|
|
@@ -12488,7 +12773,14 @@ function id4(value) {
|
|
|
12488
12773
|
}
|
|
12489
12774
|
function rows(output) {
|
|
12490
12775
|
const trimmed = output.trim();
|
|
12491
|
-
return trimmed ? trimmed.split(/\r?\n/).map((line) =>
|
|
12776
|
+
return trimmed ? trimmed.split(/\r?\n/).map((line) => parseRecord(line)) : [];
|
|
12777
|
+
}
|
|
12778
|
+
function parseRecord(line) {
|
|
12779
|
+
try {
|
|
12780
|
+
return JSON.parse(line);
|
|
12781
|
+
} catch {
|
|
12782
|
+
throw new NetworkInventoryError("Docker network inventory contains a malformed record.");
|
|
12783
|
+
}
|
|
12492
12784
|
}
|
|
12493
12785
|
function ids(output) {
|
|
12494
12786
|
const values = output.trim() ? output.trim().split(/\r?\n/).map(id4) : [];
|
|
@@ -12533,7 +12825,7 @@ function collectDockerNetworkInventory(options = {}, dependencies = {}) {
|
|
|
12533
12825
|
return output;
|
|
12534
12826
|
};
|
|
12535
12827
|
const info = record4(
|
|
12536
|
-
|
|
12828
|
+
parseRecord(
|
|
12537
12829
|
read2(["info", "--format", '{"id":{{json .ID}},"pools":{{json .DefaultAddressPools}}}'])
|
|
12538
12830
|
)
|
|
12539
12831
|
);
|
|
@@ -12614,7 +12906,7 @@ function collectDockerNetworkInventory(options = {}, dependencies = {}) {
|
|
|
12614
12906
|
}
|
|
12615
12907
|
|
|
12616
12908
|
// src/core/network-routes.ts
|
|
12617
|
-
var
|
|
12909
|
+
var import_node_child_process25 = require("child_process");
|
|
12618
12910
|
var import_node_net3 = require("net");
|
|
12619
12911
|
|
|
12620
12912
|
// src/core/network-local-daemon.ts
|
|
@@ -12683,7 +12975,7 @@ function parseLinuxNetworkRoutes(output) {
|
|
|
12683
12975
|
function collectNetworkRoutes(options) {
|
|
12684
12976
|
if ((options.platform ?? process.platform) !== "linux" || !hasLocalDockerNetworkNamespace(options.endpoint))
|
|
12685
12977
|
return { status: "unknown", routes: [] };
|
|
12686
|
-
const result = (0,
|
|
12978
|
+
const result = (0, import_node_child_process25.spawnSync)("ip", ["-j", "-4", "route", "show", "table", "all"], {
|
|
12687
12979
|
encoding: "utf8",
|
|
12688
12980
|
timeout: 5e3,
|
|
12689
12981
|
maxBuffer: 1024 * 1024
|
|
@@ -12719,10 +13011,10 @@ function inspectLegacyNetworkCapacity(input) {
|
|
|
12719
13011
|
}
|
|
12720
13012
|
|
|
12721
13013
|
// src/core/network-managed.ts
|
|
12722
|
-
var
|
|
13014
|
+
var import_node_crypto21 = require("crypto");
|
|
12723
13015
|
|
|
12724
13016
|
// src/core/network-compose.ts
|
|
12725
|
-
var
|
|
13017
|
+
var import_node_crypto19 = require("crypto");
|
|
12726
13018
|
var SAFE_ENDPOINT_UPPER_BOUND_SEMANTICS = "full-lifecycle-demand";
|
|
12727
13019
|
function isRecord5(value) {
|
|
12728
13020
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -13528,12 +13820,12 @@ function stableValue(value) {
|
|
|
13528
13820
|
}
|
|
13529
13821
|
function fingerprintComposeNetworkModel(input) {
|
|
13530
13822
|
const projection = normalizedFingerprintProjection(input);
|
|
13531
|
-
return (0,
|
|
13823
|
+
return (0, import_node_crypto19.createHash)("sha256").update(stableValue(projection), "utf8").digest("hex");
|
|
13532
13824
|
}
|
|
13533
13825
|
|
|
13534
13826
|
// src/core/network-compose-files.ts
|
|
13535
|
-
var
|
|
13536
|
-
var
|
|
13827
|
+
var import_node_child_process26 = require("child_process");
|
|
13828
|
+
var import_node_crypto20 = require("crypto");
|
|
13537
13829
|
var import_node_fs33 = __toESM(require("fs"));
|
|
13538
13830
|
var import_node_path38 = __toESM(require("path"));
|
|
13539
13831
|
var import_yaml4 = __toESM(require("yaml"));
|
|
@@ -13569,7 +13861,7 @@ function inspectNetworkComposeFiles(input) {
|
|
|
13569
13861
|
const env = managedComposeEnvironment(input.workspace);
|
|
13570
13862
|
delete env.DOCKER_CONTEXT;
|
|
13571
13863
|
delete env.DOCKER_HOST;
|
|
13572
|
-
const result = (0,
|
|
13864
|
+
const result = (0, import_node_child_process26.spawnSync)(
|
|
13573
13865
|
"docker",
|
|
13574
13866
|
[
|
|
13575
13867
|
"--host",
|
|
@@ -13604,7 +13896,7 @@ function prepareNetworkComposeFiles(plan, subnet) {
|
|
|
13604
13896
|
const overlay = deriveComposeNetworkOverlay(subnet);
|
|
13605
13897
|
if (overlay.status !== "ready" || !overlay.overlay) throw new Error("Network subnet is invalid.");
|
|
13606
13898
|
const overlayPath = import_node_path38.default.join(plan.composeDirectory, OVERLAY);
|
|
13607
|
-
const ignored = (0,
|
|
13899
|
+
const ignored = (0, import_node_child_process26.spawnSync)("git", [
|
|
13608
13900
|
"-C",
|
|
13609
13901
|
plan.composeDirectory,
|
|
13610
13902
|
"check-ignore",
|
|
@@ -13634,7 +13926,7 @@ ${JSON.stringify(extended.config, null, 2)}
|
|
|
13634
13926
|
...plan,
|
|
13635
13927
|
contents,
|
|
13636
13928
|
composeFiles: [...plan.composeFiles, overlayPath],
|
|
13637
|
-
effectiveConfigSha256: (0,
|
|
13929
|
+
effectiveConfigSha256: (0, import_node_crypto20.createHash)("sha256").update(contents).digest("hex")
|
|
13638
13930
|
};
|
|
13639
13931
|
return {
|
|
13640
13932
|
plan: result,
|
|
@@ -13646,7 +13938,7 @@ ${JSON.stringify(extended.config, null, 2)}
|
|
|
13646
13938
|
}
|
|
13647
13939
|
|
|
13648
13940
|
// src/core/network-connected-route.ts
|
|
13649
|
-
var
|
|
13941
|
+
var import_node_child_process27 = require("child_process");
|
|
13650
13942
|
function bounds(cidr) {
|
|
13651
13943
|
const canonical = parseIPv4Cidr(cidr);
|
|
13652
13944
|
if (!canonical) throw new Error("Invalid connected route.");
|
|
@@ -13671,7 +13963,7 @@ function inspectClaimedBridge(claim2, networkId) {
|
|
|
13671
13963
|
const env = { ...process.env };
|
|
13672
13964
|
delete env.DOCKER_CONTEXT;
|
|
13673
13965
|
delete env.DOCKER_HOST;
|
|
13674
|
-
const result = (0,
|
|
13966
|
+
const result = (0, import_node_child_process27.spawnSync)(
|
|
13675
13967
|
"docker",
|
|
13676
13968
|
[
|
|
13677
13969
|
"--host",
|
|
@@ -13698,7 +13990,7 @@ function inspectClaimedBridge(claim2, networkId) {
|
|
|
13698
13990
|
const [first, last] = bounds(claim2.subnet);
|
|
13699
13991
|
const [address] = bounds(gateway);
|
|
13700
13992
|
if (address <= first || address >= last) throw new Error();
|
|
13701
|
-
const interfaces = (0,
|
|
13993
|
+
const interfaces = (0, import_node_child_process27.spawnSync)("ip", ["-j", "-d", "-4", "address", "show", "dev", bridge], {
|
|
13702
13994
|
encoding: "utf8",
|
|
13703
13995
|
timeout: 5e3,
|
|
13704
13996
|
maxBuffer: 65536
|
|
@@ -13952,7 +14244,7 @@ function createManagedNetworkSession(input) {
|
|
|
13952
14244
|
"Insufficient endpoint headroom; request an allowed larger network before first allocation."
|
|
13953
14245
|
);
|
|
13954
14246
|
const authority = readNetworkOperationAuthority(identity2);
|
|
13955
|
-
const fingerprint = (0,
|
|
14247
|
+
const fingerprint = (0, import_node_crypto21.createHash)("sha256").update(input.plan().sourceConfigSha256).update(fingerprintComposeNetworkModel(model)).digest("hex");
|
|
13956
14248
|
if (!saved) {
|
|
13957
14249
|
if (policy.status !== "valid")
|
|
13958
14250
|
throw new Error("New network allocation requires valid machine policy.");
|
|
@@ -14454,7 +14746,7 @@ async function waitForContainerPreflight(repoPath, target, upstreamHosts, timeou
|
|
|
14454
14746
|
target
|
|
14455
14747
|
});
|
|
14456
14748
|
if (target.kind === "linked") {
|
|
14457
|
-
const workspaceEnv = (0,
|
|
14749
|
+
const workspaceEnv = (0, import_node_child_process28.spawnSync)(
|
|
14458
14750
|
"docker",
|
|
14459
14751
|
["exec", appContainer.id, "printenv", "WORKSPACE"],
|
|
14460
14752
|
{ encoding: "utf-8", ...networkDockerOptions() }
|
|
@@ -14464,7 +14756,7 @@ async function waitForContainerPreflight(repoPath, target, upstreamHosts, timeou
|
|
|
14464
14756
|
`Workspace app container must expose WORKSPACE='${target.workspace}' (got '${workspaceEnv.stdout.trim() || "(empty)"}').`
|
|
14465
14757
|
);
|
|
14466
14758
|
}
|
|
14467
|
-
const devrouterWorkspaceEnv = (0,
|
|
14759
|
+
const devrouterWorkspaceEnv = (0, import_node_child_process28.spawnSync)(
|
|
14468
14760
|
"docker",
|
|
14469
14761
|
["exec", appContainer.id, "printenv", "DEVROUTER_WORKSPACE"],
|
|
14470
14762
|
{ encoding: "utf-8", ...networkDockerOptions() }
|
|
@@ -14475,7 +14767,7 @@ async function waitForContainerPreflight(repoPath, target, upstreamHosts, timeou
|
|
|
14475
14767
|
);
|
|
14476
14768
|
}
|
|
14477
14769
|
}
|
|
14478
|
-
const gitCheck = (0,
|
|
14770
|
+
const gitCheck = (0, import_node_child_process28.spawnSync)(
|
|
14479
14771
|
"docker",
|
|
14480
14772
|
[
|
|
14481
14773
|
"exec",
|
|
@@ -14755,7 +15047,7 @@ function isPrimaryCheckout(repoPath) {
|
|
|
14755
15047
|
}
|
|
14756
15048
|
function openUrls(urls) {
|
|
14757
15049
|
for (const url of urls) {
|
|
14758
|
-
const opened = (0,
|
|
15050
|
+
const opened = (0, import_node_child_process28.spawnSync)("open", [url], { encoding: "utf-8" });
|
|
14759
15051
|
if (opened.status !== 0) {
|
|
14760
15052
|
process.stderr.write(`Warning: could not open '${url}'.
|
|
14761
15053
|
`);
|
|
@@ -15038,7 +15330,7 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
15038
15330
|
transitionPhase = "service-start";
|
|
15039
15331
|
reportLifecycleProgress("service-start");
|
|
15040
15332
|
claimLifecycleEffect();
|
|
15041
|
-
const result = (0,
|
|
15333
|
+
const result = (0, import_node_child_process28.spawnSync)("docker", ["start", ...stopped.map((entry) => entry.id)], {
|
|
15042
15334
|
...networkDockerOptions(),
|
|
15043
15335
|
encoding: "utf-8",
|
|
15044
15336
|
timeout: DEFAULT_READINESS_TIMEOUT_MS,
|
|
@@ -15982,6 +16274,11 @@ async function executeLifecycleWorker(request, operation) {
|
|
|
15982
16274
|
];
|
|
15983
16275
|
for (const project of stopProjects) inspectManagedStopContainers(project);
|
|
15984
16276
|
}
|
|
16277
|
+
updateReliabilityOperation(request.identity, (record5) => {
|
|
16278
|
+
if (!matchesFence(record5, request.fence))
|
|
16279
|
+
throw new Error("Lifecycle intent changed before stop work began.");
|
|
16280
|
+
record5.stopWorkStarted = true;
|
|
16281
|
+
});
|
|
15985
16282
|
} else {
|
|
15986
16283
|
updateReliabilityOperation(request.identity, (record5) => {
|
|
15987
16284
|
stepRecord(record5, {
|
|
@@ -16093,6 +16390,7 @@ function proveLifecycleStopped() {
|
|
|
16093
16390
|
const settlement = updateReliabilityOperation(request.identity, (record5) => {
|
|
16094
16391
|
if (!matchesFence(record5, request.fence) || record5.worker)
|
|
16095
16392
|
throw new Error("Stop proof was superseded or an earlier worker remains.");
|
|
16393
|
+
delete record5.stopWorkStarted;
|
|
16096
16394
|
if (record5.version === 2) {
|
|
16097
16395
|
if (record5.capacity) record5.capacity.validUntilMs = 0;
|
|
16098
16396
|
return record5.state.environmentId;
|
|
@@ -16131,6 +16429,7 @@ function proveLifecycleStopped() {
|
|
|
16131
16429
|
if (!matchesFence(record5, request.fence) || record5.worker || record5.state.environmentId !== settlement || record5.capacity && record5.capacity.validUntilMs !== 0)
|
|
16132
16430
|
throw new Error("Capacity settlement was superseded before journal confirmation.");
|
|
16133
16431
|
record5.capacity = null;
|
|
16432
|
+
delete record5.stopWorkStarted;
|
|
16134
16433
|
if (record5.enrollment) {
|
|
16135
16434
|
record5.phaseSettlement = null;
|
|
16136
16435
|
record5.activeProfile = null;
|
|
@@ -16184,7 +16483,7 @@ async function devsyExecOutcome(repoPath, command, retainedProof) {
|
|
|
16184
16483
|
const launch = () => {
|
|
16185
16484
|
if (retainedProof) revalidateDevsyExecProof(repoPath, retainedProof);
|
|
16186
16485
|
claimLifecycleEffect();
|
|
16187
|
-
return (0,
|
|
16486
|
+
return (0, import_node_child_process29.spawn)("devsy", args, { stdio: "inherit" });
|
|
16188
16487
|
};
|
|
16189
16488
|
child = retainedProof ? withMutationLock("Execute retained workspace", repoPath, launch) : launch();
|
|
16190
16489
|
} catch (error) {
|
|
@@ -16232,7 +16531,7 @@ function ensureGuidance2(repoPath) {
|
|
|
16232
16531
|
return `Run 'devrouter ensure ${repoPath}' first.`;
|
|
16233
16532
|
}
|
|
16234
16533
|
function assertRunningDevpod(devpodId, repoPath) {
|
|
16235
|
-
const result = (0,
|
|
16534
|
+
const result = (0, import_node_child_process30.spawnSync)("devpod", ["status", devpodId, "--output", "json"], {
|
|
16236
16535
|
encoding: "utf-8"
|
|
16237
16536
|
});
|
|
16238
16537
|
if (result.status !== 0) {
|
|
@@ -16279,7 +16578,7 @@ async function devpodExecOutcome(repoPath, command) {
|
|
|
16279
16578
|
}
|
|
16280
16579
|
assertRunningDevpod(devpod.id, repoPath);
|
|
16281
16580
|
const workspaceDirectory = resolveWorkspaceDirectory(repoPath);
|
|
16282
|
-
const statusMarker = `__DEVROUTER_EXIT_${(0,
|
|
16581
|
+
const statusMarker = `__DEVROUTER_EXIT_${(0, import_node_crypto23.randomUUID)()}__:`;
|
|
16283
16582
|
const statusMarkerBytes = Buffer.from(statusMarker, "ascii");
|
|
16284
16583
|
const literalCommand = command.map(quotePosixArg).join(" ");
|
|
16285
16584
|
const wrappedCommand = `${literalCommand}; __devrouter_status=$?; printf '${statusMarker}%s\\n' "$__devrouter_status" >&2; exit 0`;
|
|
@@ -16411,7 +16710,7 @@ async function devpodExecOutcome(repoPath, command) {
|
|
|
16411
16710
|
let child;
|
|
16412
16711
|
try {
|
|
16413
16712
|
claimLifecycleEffect();
|
|
16414
|
-
child = (0,
|
|
16713
|
+
child = (0, import_node_child_process30.spawn)("devpod", args, { stdio: ["inherit", "inherit", "pipe"] });
|
|
16415
16714
|
} catch (error) {
|
|
16416
16715
|
settled = true;
|
|
16417
16716
|
reject(
|
|
@@ -16468,7 +16767,7 @@ async function devpodExecOutcome(repoPath, command) {
|
|
|
16468
16767
|
}
|
|
16469
16768
|
|
|
16470
16769
|
// src/core/workspace-lifecycle.ts
|
|
16471
|
-
var
|
|
16770
|
+
var import_node_child_process31 = require("child_process");
|
|
16472
16771
|
var import_node_fs36 = __toESM(require("fs"));
|
|
16473
16772
|
var import_node_path41 = __toESM(require("path"));
|
|
16474
16773
|
|