agentwheel 0.20.17 → 0.20.18
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 +29 -0
- package/dist/index.js +678 -124
- package/openpack.json +1 -1
- package/package.json +1 -1
- package/skills/agentwheel/SKILL.md +8 -1
- package/skills/agentwheel-discovery/SKILL.md +1 -1
package/dist/index.js
CHANGED
|
@@ -113,11 +113,11 @@ import {
|
|
|
113
113
|
} from "./chunk-GE7F76AC.js";
|
|
114
114
|
|
|
115
115
|
// src/cli/index.ts
|
|
116
|
-
import { createHash as
|
|
116
|
+
import { createHash as createHash16 } from "crypto";
|
|
117
117
|
import { existsSync } from "fs";
|
|
118
|
-
import { mkdir as mkdir16, mkdtemp as mkdtemp4, realpath as
|
|
118
|
+
import { mkdir as mkdir16, mkdtemp as mkdtemp4, realpath as realpath3, rm as rm10, writeFile as writeFile13 } from "fs/promises";
|
|
119
119
|
import { homedir as homedir12, tmpdir as tmpdir5 } from "os";
|
|
120
|
-
import { basename as basename21, dirname as dirname23, isAbsolute as
|
|
120
|
+
import { basename as basename21, dirname as dirname23, isAbsolute as isAbsolute5, join as join46, relative as relative11, resolve as resolve27 } from "path";
|
|
121
121
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
122
122
|
import { Command } from "commander";
|
|
123
123
|
|
|
@@ -3113,14 +3113,14 @@ async function writeMermaidAsset(response) {
|
|
|
3113
3113
|
"content-type": "application/javascript; charset=utf-8",
|
|
3114
3114
|
"cache-control": "public, max-age=3600"
|
|
3115
3115
|
});
|
|
3116
|
-
await new Promise((
|
|
3116
|
+
await new Promise((resolve28) => {
|
|
3117
3117
|
const stream = createReadStream(assetPath);
|
|
3118
3118
|
stream.on("error", () => {
|
|
3119
3119
|
if (!response.headersSent) writeText(response, 404, "Mermaid asset unavailable.\n");
|
|
3120
3120
|
else response.destroy();
|
|
3121
|
-
|
|
3121
|
+
resolve28();
|
|
3122
3122
|
});
|
|
3123
|
-
stream.on("end",
|
|
3123
|
+
stream.on("end", resolve28);
|
|
3124
3124
|
stream.pipe(response);
|
|
3125
3125
|
});
|
|
3126
3126
|
}
|
|
@@ -3147,14 +3147,14 @@ function escapeHtml2(value) {
|
|
|
3147
3147
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
3148
3148
|
}
|
|
3149
3149
|
function listen(server, port, bind) {
|
|
3150
|
-
return new Promise((
|
|
3150
|
+
return new Promise((resolve28, reject) => {
|
|
3151
3151
|
const onError = (error) => {
|
|
3152
3152
|
server.off("listening", onListening);
|
|
3153
3153
|
reject(error);
|
|
3154
3154
|
};
|
|
3155
3155
|
const onListening = () => {
|
|
3156
3156
|
server.off("error", onError);
|
|
3157
|
-
|
|
3157
|
+
resolve28();
|
|
3158
3158
|
};
|
|
3159
3159
|
server.once("error", onError);
|
|
3160
3160
|
server.once("listening", onListening);
|
|
@@ -3162,7 +3162,7 @@ function listen(server, port, bind) {
|
|
|
3162
3162
|
});
|
|
3163
3163
|
}
|
|
3164
3164
|
function waitForShutdown(server, beforeClose) {
|
|
3165
|
-
return new Promise((
|
|
3165
|
+
return new Promise((resolve28) => {
|
|
3166
3166
|
let closing = false;
|
|
3167
3167
|
const shutdown = () => {
|
|
3168
3168
|
if (closing) return;
|
|
@@ -3170,7 +3170,7 @@ function waitForShutdown(server, beforeClose) {
|
|
|
3170
3170
|
process.off("SIGINT", shutdown);
|
|
3171
3171
|
process.off("SIGTERM", shutdown);
|
|
3172
3172
|
beforeClose();
|
|
3173
|
-
server.close(() =>
|
|
3173
|
+
server.close(() => resolve28());
|
|
3174
3174
|
};
|
|
3175
3175
|
process.once("SIGINT", shutdown);
|
|
3176
3176
|
process.once("SIGTERM", shutdown);
|
|
@@ -3180,7 +3180,7 @@ function waitForShutdown(server, beforeClose) {
|
|
|
3180
3180
|
process.off("SIGINT", shutdown);
|
|
3181
3181
|
process.off("SIGTERM", shutdown);
|
|
3182
3182
|
beforeClose();
|
|
3183
|
-
|
|
3183
|
+
resolve28();
|
|
3184
3184
|
});
|
|
3185
3185
|
});
|
|
3186
3186
|
}
|
|
@@ -3733,7 +3733,7 @@ async function fetchClawHubPackage(fetchImpl, packageName) {
|
|
|
3733
3733
|
throw new Error(`ClawHub lookup failed for ${packageName}`);
|
|
3734
3734
|
}
|
|
3735
3735
|
async function delay(ms) {
|
|
3736
|
-
await new Promise((
|
|
3736
|
+
await new Promise((resolve28) => setTimeout(resolve28, ms));
|
|
3737
3737
|
}
|
|
3738
3738
|
function parseClawHubSource(source) {
|
|
3739
3739
|
if (!source.startsWith(sourcePrefix)) {
|
|
@@ -3910,7 +3910,7 @@ async function withGitCacheMaintenanceLock(cacheRoot, timeoutMs, fn) {
|
|
|
3910
3910
|
if (Date.now() - started > timeoutMs) {
|
|
3911
3911
|
throw new Error(`Timed out waiting for git cache maintenance lock at ${lockPath}`);
|
|
3912
3912
|
}
|
|
3913
|
-
await new Promise((
|
|
3913
|
+
await new Promise((resolve28) => setTimeout(resolve28, 50));
|
|
3914
3914
|
}
|
|
3915
3915
|
}
|
|
3916
3916
|
try {
|
|
@@ -6776,8 +6776,8 @@ function verifyIntegrity(integrity, sourceHash, label) {
|
|
|
6776
6776
|
async function withCachePathLock(path, fn) {
|
|
6777
6777
|
const previous = cacheLocks.get(path) ?? Promise.resolve();
|
|
6778
6778
|
let release = () => void 0;
|
|
6779
|
-
const current = previous.then(() => new Promise((
|
|
6780
|
-
release =
|
|
6779
|
+
const current = previous.then(() => new Promise((resolve28) => {
|
|
6780
|
+
release = resolve28;
|
|
6781
6781
|
}));
|
|
6782
6782
|
cacheLocks.set(path, current);
|
|
6783
6783
|
await previous;
|
|
@@ -8785,6 +8785,7 @@ async function assertNoForeignWorkspaceState(check) {
|
|
|
8785
8785
|
}
|
|
8786
8786
|
const foreign = [...foreignByOwner.values()];
|
|
8787
8787
|
if (foreign.length === 0) return observations.sort((left, right) => left.path.localeCompare(right.path));
|
|
8788
|
+
const currentOwner = parseWorkspaceOwner(check.workspaceOwner);
|
|
8788
8789
|
throw new Error([
|
|
8789
8790
|
`Refusing to plan ${check.adapter} at ${check.installRoot}.`,
|
|
8790
8791
|
"This runtime root already carries Agentwheel state owned by another workspace, at paths this run would install:",
|
|
@@ -8795,7 +8796,10 @@ async function assertNoForeignWorkspaceState(check) {
|
|
|
8795
8796
|
`Current workspace: ${check.workspaceRoot} (state key ${check.stateKey})`,
|
|
8796
8797
|
"Agentwheel keys install state by target fingerprint, so this run cannot read that manifest and",
|
|
8797
8798
|
"would report those paths as unmanaged conflicts or drift.",
|
|
8798
|
-
|
|
8799
|
+
...currentOwner?.fleetId ? [
|
|
8800
|
+
"Reconcile the owners with an explicit agentwheel fleet normalize operation before planning this fleet.",
|
|
8801
|
+
...currentOwner.root !== root ? ["For this nested workspace, adopt legacy fingerprint-keyed claims with agentwheel ownership adopt-legacy."] : []
|
|
8802
|
+
] : ["Re-run from the owning workspace, or pass --force-foreign-state to plan against it anyway."]
|
|
8799
8803
|
].join("\n"));
|
|
8800
8804
|
}
|
|
8801
8805
|
async function exactForeignKeepOperation(check, collision, operations) {
|
|
@@ -10107,7 +10111,7 @@ async function requireManifest(request, stateKey, transport, label) {
|
|
|
10107
10111
|
stateKey
|
|
10108
10112
|
});
|
|
10109
10113
|
if (!manifest) throw new Error(`No ${label} install manifest for state key ${stateKey}.`);
|
|
10110
|
-
if (manifest.version !== 2) throw new Error(
|
|
10114
|
+
if (manifest.version !== 2) throw new Error(`The ${label} install manifest must be an Agentwheel v2 manifest.`);
|
|
10111
10115
|
const actualStateKey = stateKeyFor(manifest.adapter, {
|
|
10112
10116
|
installationType: manifest.installationType,
|
|
10113
10117
|
stateKey: manifest.stateKey
|
|
@@ -10214,9 +10218,14 @@ function canonicalJson(value) {
|
|
|
10214
10218
|
return `{${Object.keys(record).filter((key) => record[key] !== void 0).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
|
|
10215
10219
|
}
|
|
10216
10220
|
|
|
10221
|
+
// src/lifecycle/ownership-adopt-legacy.ts
|
|
10222
|
+
import { createHash as createHash11 } from "crypto";
|
|
10223
|
+
import { realpath as realpath2 } from "fs/promises";
|
|
10224
|
+
import { isAbsolute as isAbsolute3, relative as relative9, resolve as resolve23 } from "path";
|
|
10225
|
+
|
|
10217
10226
|
// src/lifecycle/legacy-recovery-plan.ts
|
|
10218
10227
|
import { createHash as createHash10 } from "crypto";
|
|
10219
|
-
import { readdir as readdir10 } from "fs/promises";
|
|
10228
|
+
import { lstat as lstat3, readdir as readdir10 } from "fs/promises";
|
|
10220
10229
|
import { basename as basename20, dirname as dirname21, join as join39, posix as posix3, relative as relative8, resolve as resolve22 } from "path";
|
|
10221
10230
|
async function inventoryHistoricalLocks(fleetRoot) {
|
|
10222
10231
|
const root = join39(fleetRoot, ".agentwheel", "locks");
|
|
@@ -10228,15 +10237,38 @@ async function inventoryHistoricalLocks(fleetRoot) {
|
|
|
10228
10237
|
})) {
|
|
10229
10238
|
const path = join39(dir, entry.name);
|
|
10230
10239
|
if (entry.isDirectory()) await visit(path);
|
|
10231
|
-
else if (entry.isFile() && entry.name.endsWith(".graph-lock.json"))
|
|
10232
|
-
const lock = await readGraphLock(path);
|
|
10233
|
-
found.push({ path, digest: sha2564(canonicalGraphLockJson(lock)), lock });
|
|
10234
|
-
}
|
|
10240
|
+
else if (entry.isFile() && entry.name.endsWith(".graph-lock.json")) found.push(await historicalLock(path));
|
|
10235
10241
|
}
|
|
10236
10242
|
}
|
|
10237
10243
|
await visit(root);
|
|
10238
10244
|
return found.sort((a, b) => a.path.localeCompare(b.path));
|
|
10239
10245
|
}
|
|
10246
|
+
async function findLegacyNamedLocks(workspaceRoot, adapter, fingerprint) {
|
|
10247
|
+
const root = join39(workspaceRoot, ".agentwheel", "locks");
|
|
10248
|
+
const found = [];
|
|
10249
|
+
for (const target of await readdir10(root, { withFileTypes: true }).catch((error) => {
|
|
10250
|
+
if (error.code === "ENOENT") return [];
|
|
10251
|
+
throw error;
|
|
10252
|
+
})) {
|
|
10253
|
+
if (!target.isDirectory()) continue;
|
|
10254
|
+
const path = join39(root, target.name, adapter, `${fingerprint}.graph-lock.json`);
|
|
10255
|
+
const stats = await lstat3(path).catch((error) => {
|
|
10256
|
+
if (error.code === "ENOENT") return void 0;
|
|
10257
|
+
throw error;
|
|
10258
|
+
});
|
|
10259
|
+
if (!stats?.isFile()) continue;
|
|
10260
|
+
const item = await historicalLock(path);
|
|
10261
|
+
if (item.lock.canonical.targetFingerprint === fingerprint) found.push(item);
|
|
10262
|
+
}
|
|
10263
|
+
return found.sort((a, b) => a.path.localeCompare(b.path));
|
|
10264
|
+
}
|
|
10265
|
+
function lockCoversEntry(lock, entry) {
|
|
10266
|
+
return lock.canonical.artifacts.some((artifact) => artifact.graphNodeId === entry.graphNodeId && artifact.logicalSelector === entry.logicalSelector && artifact.hash === entry.sourceHash);
|
|
10267
|
+
}
|
|
10268
|
+
async function historicalLock(path) {
|
|
10269
|
+
const lock = await readGraphLock(path);
|
|
10270
|
+
return { path, digest: sha2564(canonicalGraphLockJson(lock)), lock };
|
|
10271
|
+
}
|
|
10240
10272
|
async function planLegacyRecovery(input) {
|
|
10241
10273
|
const { graphPlan, installRoot, targetKey: targetKey2, adapter, installationType, historicalLocks, transport } = input;
|
|
10242
10274
|
assertCompleteGraphPlan(graphPlan);
|
|
@@ -10367,7 +10399,7 @@ function claimFor(item, entry, locks, binding) {
|
|
|
10367
10399
|
if (inTargetDir.length && !boundLocks.length) evidence.push("graph-lock fingerprint, filename, or state key does not bind claim");
|
|
10368
10400
|
if (boundLocks.length && !boundLocks.some((candidate) => candidate.lock.canonical.artifacts.some((artifact) => artifact.graphNodeId === nodeId && artifact.logicalSelector === selector))) {
|
|
10369
10401
|
evidence.push("historical graph-lock selector/node does not cover claim");
|
|
10370
|
-
} else if (boundLocks.length && !boundLocks.some((candidate) => candidate.lock
|
|
10402
|
+
} else if (boundLocks.length && !boundLocks.some((candidate) => lockCoversEntry(candidate.lock, { graphNodeId: nodeId, logicalSelector: selector, sourceHash: entry.sourceHash }))) {
|
|
10371
10403
|
evidence.push("historical graph-lock artifact hash differs from manifest source hash");
|
|
10372
10404
|
}
|
|
10373
10405
|
const foreign = owner !== binding.expectedOwner || !sameRoot || item.manifest.version !== 2 || item.manifest.installationType !== binding.installationType || item.manifest.version === 2 && (item.manifest.stateKey !== item.stateKey || item.manifest.adapter !== binding.adapter) || matches.length > 0 && inTargetDir.length === 0;
|
|
@@ -10466,10 +10498,394 @@ function sha2564(value) {
|
|
|
10466
10498
|
return createHash10("sha256").update(value).digest("hex");
|
|
10467
10499
|
}
|
|
10468
10500
|
|
|
10501
|
+
// src/lifecycle/ownership-adopt-legacy.ts
|
|
10502
|
+
var absentRevision = "absent";
|
|
10503
|
+
function desiredCoverageFromPlan(plan) {
|
|
10504
|
+
return plan.operations.filter((operation) => operation.desiredHash && operation.action !== "remove" && operation.action !== "keep").map((operation) => ({
|
|
10505
|
+
path: operation.relativeDestPath,
|
|
10506
|
+
artifactType: operation.artifactType,
|
|
10507
|
+
artifactName: operation.artifactName,
|
|
10508
|
+
kind: operation.kind,
|
|
10509
|
+
...operation.packageName ? { packageName: operation.packageName } : {}
|
|
10510
|
+
})).sort((left, right) => left.path.localeCompare(right.path));
|
|
10511
|
+
}
|
|
10512
|
+
async function planAdoptLegacyOwnership(request) {
|
|
10513
|
+
return observe2(request, request.transport ?? localTransport);
|
|
10514
|
+
}
|
|
10515
|
+
async function applyAdoptLegacyOwnership(request) {
|
|
10516
|
+
assertApplyPreconditions2(request);
|
|
10517
|
+
const transport = request.transport ?? localTransport;
|
|
10518
|
+
const targetRoot = resolve23(request.targetRoot);
|
|
10519
|
+
const installationType = request.installationType.trim();
|
|
10520
|
+
const lock = await acquireApplyLock(targetRoot, request.adapter, transport, {}, {
|
|
10521
|
+
installationType,
|
|
10522
|
+
stateKey: request.destinationStateKey
|
|
10523
|
+
});
|
|
10524
|
+
try {
|
|
10525
|
+
await assertNoPendingJournals(targetRoot, request.adapter, transport);
|
|
10526
|
+
const current = await observe2(request, transport);
|
|
10527
|
+
assertExpected(request.planDigest, current.planDigest, "plan digest");
|
|
10528
|
+
assertExpected(request.expectedSourceRevision, current.source.revision, "source manifest revision");
|
|
10529
|
+
assertExpected(request.expectedDestinationRevision, current.destination.revision ?? absentRevision, "destination manifest revision");
|
|
10530
|
+
assertExpected(request.expectedInventoryRevision, current.manifestInventoryRevision, "manifest inventory revision");
|
|
10531
|
+
const identity = { targetRoot, adapter: request.adapter, installationType };
|
|
10532
|
+
const source = await requireManifest(identity, current.source.stateKey, transport, "source");
|
|
10533
|
+
const destination = await readDestination(identity, current.destination.stateKey, transport);
|
|
10534
|
+
const selectedByDigest = new Map(current.selected.map((entry) => [entry.sourceEntryDigest, entry]));
|
|
10535
|
+
const adopted = [];
|
|
10536
|
+
const retained = [];
|
|
10537
|
+
for (const entry of source.entries) {
|
|
10538
|
+
const selected = selectedByDigest.get(entryDigest(entry));
|
|
10539
|
+
if (!selected) retained.push(entry);
|
|
10540
|
+
else if (selected.action === "adopt") adopted.push({ ...entry, workspaceOwner: current.destination.owner });
|
|
10541
|
+
}
|
|
10542
|
+
if (source.entries.length - retained.length !== current.selected.length) {
|
|
10543
|
+
throw new Error("Source manifest selection changed while locked; replan required.");
|
|
10544
|
+
}
|
|
10545
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10546
|
+
if (adopted.length > 0) {
|
|
10547
|
+
declareMutationPath(current.destination.manifestPath);
|
|
10548
|
+
const base = destination ?? {
|
|
10549
|
+
version: 2,
|
|
10550
|
+
adapter: request.adapter,
|
|
10551
|
+
installationType,
|
|
10552
|
+
stateKey: current.destination.stateKey,
|
|
10553
|
+
targetRoot,
|
|
10554
|
+
generatedAt: now,
|
|
10555
|
+
revision: "pending-apply-0000",
|
|
10556
|
+
legacy: false,
|
|
10557
|
+
entries: []
|
|
10558
|
+
};
|
|
10559
|
+
await commitMetadata({
|
|
10560
|
+
...base,
|
|
10561
|
+
entries: [...base.entries, ...adopted].sort((left, right) => left.path.localeCompare(right.path))
|
|
10562
|
+
}, destination?.revision ?? null, now, transport);
|
|
10563
|
+
}
|
|
10564
|
+
declareMutationPath(current.source.manifestPath);
|
|
10565
|
+
await commitMetadata({ ...source, entries: retained }, source.revision, now, transport);
|
|
10566
|
+
return {
|
|
10567
|
+
...current,
|
|
10568
|
+
applied: true,
|
|
10569
|
+
adoptedEntries: adopted.length,
|
|
10570
|
+
retiredCoveredEntries: current.selected.length - adopted.length,
|
|
10571
|
+
sourceManifestRemoved: retained.length === 0,
|
|
10572
|
+
retainedSourceEntries: retained.length
|
|
10573
|
+
};
|
|
10574
|
+
} finally {
|
|
10575
|
+
await lock.release();
|
|
10576
|
+
}
|
|
10577
|
+
}
|
|
10578
|
+
async function commitMetadata(manifest, baseRevision, now, transport) {
|
|
10579
|
+
const mutation = mutationMetadataForApplyJournal();
|
|
10580
|
+
const journal = {
|
|
10581
|
+
version: mutation ? 2 : 1,
|
|
10582
|
+
...mutation ? { mutation } : {},
|
|
10583
|
+
mode: "uninstall",
|
|
10584
|
+
adapter: manifest.adapter,
|
|
10585
|
+
installationType: manifest.installationType,
|
|
10586
|
+
stateKey: manifest.stateKey,
|
|
10587
|
+
targetRoot: manifest.targetRoot,
|
|
10588
|
+
baseRevision,
|
|
10589
|
+
createdAt: now,
|
|
10590
|
+
updatedAt: now,
|
|
10591
|
+
operations: [],
|
|
10592
|
+
completed: [],
|
|
10593
|
+
manifest
|
|
10594
|
+
};
|
|
10595
|
+
await writeApplyJournal(journal, transport);
|
|
10596
|
+
await commitManifestMetadataJournal(journal, transport);
|
|
10597
|
+
}
|
|
10598
|
+
async function observe2(request, transport) {
|
|
10599
|
+
if (transport.kind === "ssh") {
|
|
10600
|
+
throw new Error("Legacy ownership adoption does not support SSH targets; legacy SSH state does not prove its endpoint.");
|
|
10601
|
+
}
|
|
10602
|
+
const input = await normalizeRequest2(request);
|
|
10603
|
+
const { targetRoot, adapter, installationType } = input;
|
|
10604
|
+
await assertNoPendingJournals(targetRoot, adapter, transport);
|
|
10605
|
+
const identity = { targetRoot, adapter, installationType };
|
|
10606
|
+
if (!await readInstallManifest(targetRoot, adapter, transport, { installationType, stateKey: input.sourceStateKey })) {
|
|
10607
|
+
throw new Error(`Nothing to adopt: no source install manifest for state key ${input.sourceStateKey}.`);
|
|
10608
|
+
}
|
|
10609
|
+
const source = await requireManifest(identity, input.sourceStateKey, transport, "source");
|
|
10610
|
+
const destination = await readDestination(identity, input.destinationStateKey, transport);
|
|
10611
|
+
if (!destination && await pathExists(request.destinationGraphLockPath)) {
|
|
10612
|
+
throw new Error(
|
|
10613
|
+
`Destination has a stable graph lock without a stable manifest at ${request.destinationGraphLockPath}; reconcile it before adopting legacy ownership.`
|
|
10614
|
+
);
|
|
10615
|
+
}
|
|
10616
|
+
const desiredByPath = /* @__PURE__ */ new Map();
|
|
10617
|
+
for (const item of request.desiredCoverage) desiredByPath.set(item.path, [...desiredByPath.get(item.path) ?? [], item]);
|
|
10618
|
+
const { entries: selectedEntries, retained, provenance } = await selectLegacySource(
|
|
10619
|
+
input,
|
|
10620
|
+
input.sourceStateKey,
|
|
10621
|
+
input.fingerprint,
|
|
10622
|
+
source,
|
|
10623
|
+
desiredByPath
|
|
10624
|
+
);
|
|
10625
|
+
const selectedPaths = new Set(selectedEntries.map((entry) => entry.path));
|
|
10626
|
+
const selected = [];
|
|
10627
|
+
for (const entry of selectedEntries) {
|
|
10628
|
+
const runtimePath = containedArtifactPath(targetRoot, entry.path);
|
|
10629
|
+
const runtimeHash = await transport.pathExists(runtimePath) ? await transport.hashPath(runtimePath) : null;
|
|
10630
|
+
const drift = runtimeHash !== entry.hash;
|
|
10631
|
+
if (drift && request.carryDrift !== true) {
|
|
10632
|
+
throw new Error(
|
|
10633
|
+
runtimeHash === null ? `Managed artifact is missing at ${entry.path}; pass --carry-drift to keep the recorded hash.` : `Managed artifact is drifted at ${entry.path}: manifest ${entry.hash}, current ${runtimeHash}; pass --carry-drift to keep the recorded hash.`
|
|
10634
|
+
);
|
|
10635
|
+
}
|
|
10636
|
+
const atPath = destination?.entries.filter((candidate) => candidate.path === entry.path) ?? [];
|
|
10637
|
+
let destinationEntryDigest;
|
|
10638
|
+
if (atPath.length > 0) {
|
|
10639
|
+
const covering = atPath.length === 1 ? atPath[0] : void 0;
|
|
10640
|
+
if (!covering || !coversSourceEntry(covering, entry, input.destinationOwner)) {
|
|
10641
|
+
throw new Error(`Destination state at ${entry.path} is not an exact copy of the source claim owned by this workspace.`);
|
|
10642
|
+
}
|
|
10643
|
+
if (!drift) await verifyManifestEntryRuntime(targetRoot, covering, transport);
|
|
10644
|
+
destinationEntryDigest = entryDigest(covering);
|
|
10645
|
+
}
|
|
10646
|
+
selected.push({
|
|
10647
|
+
path: entry.path,
|
|
10648
|
+
action: destinationEntryDigest ? "retire-covered" : "adopt",
|
|
10649
|
+
sourceEntryDigest: entryDigest(entry),
|
|
10650
|
+
...destinationEntryDigest ? { destinationEntryDigest } : {},
|
|
10651
|
+
recordedHash: entry.hash,
|
|
10652
|
+
runtimeHash,
|
|
10653
|
+
drift
|
|
10654
|
+
});
|
|
10655
|
+
}
|
|
10656
|
+
const remainingDuplicates = [];
|
|
10657
|
+
const selectedByPath = new Map(selectedEntries.map((entry) => [entry.path, entry]));
|
|
10658
|
+
for (const item of await listInstallManifests(targetRoot, adapter, transport)) {
|
|
10659
|
+
if (item.stateKey === input.destinationStateKey) continue;
|
|
10660
|
+
const duplicatePaths = [];
|
|
10661
|
+
for (const entry of item.manifest.entries) {
|
|
10662
|
+
if (!selectedPaths.has(entry.path)) continue;
|
|
10663
|
+
const owner = "workspaceOwner" in entry ? entry.workspaceOwner : legacyUnownedWorkspaceOwner;
|
|
10664
|
+
if (owner !== input.sourceOwner) {
|
|
10665
|
+
throw new Error(`${entry.path} is also claimed by ${owner} in ${item.fileName}; legacy ownership cannot be adopted.`);
|
|
10666
|
+
}
|
|
10667
|
+
if (item.stateKey !== input.sourceStateKey) duplicatePaths.push(entry.path);
|
|
10668
|
+
}
|
|
10669
|
+
if (duplicatePaths.length === 0) continue;
|
|
10670
|
+
await assertRetirableDuplicate(input, item, duplicatePaths, selectedByPath, desiredByPath, transport);
|
|
10671
|
+
for (const path of duplicatePaths) remainingDuplicates.push({ stateKey: item.stateKey, path });
|
|
10672
|
+
}
|
|
10673
|
+
const withoutDigest = {
|
|
10674
|
+
adapter,
|
|
10675
|
+
installationType,
|
|
10676
|
+
targetRoot,
|
|
10677
|
+
source: {
|
|
10678
|
+
stateKey: input.sourceStateKey,
|
|
10679
|
+
manifestPath: installManifestPath(targetRoot, adapter, { installationType, stateKey: input.sourceStateKey }),
|
|
10680
|
+
revision: source.revision,
|
|
10681
|
+
owner: input.sourceOwner,
|
|
10682
|
+
class: input.sourceClass,
|
|
10683
|
+
provenanceLock: { path: provenance.path, digest: provenance.digest }
|
|
10684
|
+
},
|
|
10685
|
+
destination: {
|
|
10686
|
+
stateKey: input.destinationStateKey,
|
|
10687
|
+
manifestPath: installManifestPath(targetRoot, adapter, { installationType, stateKey: input.destinationStateKey }),
|
|
10688
|
+
revision: destination?.revision ?? null,
|
|
10689
|
+
owner: input.destinationOwner,
|
|
10690
|
+
workspaceRoot: input.workspaceRoot
|
|
10691
|
+
},
|
|
10692
|
+
manifestInventoryRevision: await computeInstallManifestInventoryRevision(targetRoot, adapter, transport),
|
|
10693
|
+
selected: selected.sort((left, right) => left.path.localeCompare(right.path)),
|
|
10694
|
+
retained: retained.sort((left, right) => left.path.localeCompare(right.path) || left.owner.localeCompare(right.owner)),
|
|
10695
|
+
remainingDuplicates: remainingDuplicates.sort((left, right) => left.stateKey.localeCompare(right.stateKey) || left.path.localeCompare(right.path)),
|
|
10696
|
+
// identity only, so a newer package revision doesn't invalidate a reviewed plan
|
|
10697
|
+
desiredCoverage: request.desiredCoverage.filter((item) => selectedPaths.has(item.path)).map(({ path, artifactType, artifactName, kind }) => ({ path, artifactType, artifactName, kind })).sort((left, right) => left.path.localeCompare(right.path) || left.artifactType.localeCompare(right.artifactType) || left.artifactName.localeCompare(right.artifactName))
|
|
10698
|
+
};
|
|
10699
|
+
return { ...withoutDigest, planDigest: createHash11("sha256").update(canonicalJson(withoutDigest)).digest("hex") };
|
|
10700
|
+
}
|
|
10701
|
+
async function normalizeRequest2(request) {
|
|
10702
|
+
if (request.destinationFleetId !== void 0) {
|
|
10703
|
+
throw new Error("Legacy ownership adoption targets a nested workspace; a Fleet target uses fleet normalize or ownership retire-stale.");
|
|
10704
|
+
}
|
|
10705
|
+
const targetRoot = resolve23(request.targetRoot);
|
|
10706
|
+
const installationType = request.installationType.trim();
|
|
10707
|
+
const sourceStateKey = exactStateKey(request.adapter, request.sourceStateKey, installationType, "source");
|
|
10708
|
+
const destinationStateKey = exactStateKey(request.adapter, request.destinationStateKey, installationType, "destination");
|
|
10709
|
+
const fingerprint = legacyFingerprint(request.adapter, installationType, sourceStateKey);
|
|
10710
|
+
if (!fingerprint) {
|
|
10711
|
+
throw new Error(`The source must be a legacy state key of the form ${request.adapter}.${installationType}.<64-hex target fingerprint>.`);
|
|
10712
|
+
}
|
|
10713
|
+
if (sourceStateKey === destinationStateKey) throw new Error("Source and destination state keys must differ.");
|
|
10714
|
+
const scope = await resolveWorkspaceOwnershipScope(request.workspaceRoot, { globalRoot: request.globalRoot });
|
|
10715
|
+
const destinationOwner = workspaceOwnerForRoot(scope.root, scope.fleetId);
|
|
10716
|
+
const workspaceRoot = await realpath2(request.workspaceRoot);
|
|
10717
|
+
const sourceRoot = resolve23(request.fromWorkspaceRoot);
|
|
10718
|
+
const sourceOwner = workspaceOwnerForRoot(sourceRoot);
|
|
10719
|
+
let sourceClass;
|
|
10720
|
+
if (sourceRoot === workspaceRoot || sourceRoot === resolve23(request.workspaceRoot)) {
|
|
10721
|
+
if (sourceOwner === destinationOwner) {
|
|
10722
|
+
throw new Error(`${sourceOwner} is already the destination owner; ordinary install migration handles its legacy state.`);
|
|
10723
|
+
}
|
|
10724
|
+
sourceClass = "own-legacy-owner";
|
|
10725
|
+
} else {
|
|
10726
|
+
const sourceForms = [sourceRoot, ...await realpath2(sourceRoot).then((path) => [path], () => [])];
|
|
10727
|
+
const workspaceForms = [workspaceRoot, resolve23(request.workspaceRoot), scope.root];
|
|
10728
|
+
if (sourceForms.some((source) => workspaceForms.some((workspace) => containsPath2(source, workspace) || containsPath2(workspace, source)))) {
|
|
10729
|
+
throw new Error(`Source root ${sourceRoot} contains or is contained by the destination workspace; it cannot be adopted as a foreign root.`);
|
|
10730
|
+
}
|
|
10731
|
+
for (const fleet of await listRegisteredFleets({ globalRoot: request.globalRoot })) {
|
|
10732
|
+
const fleetForms = [resolve23(fleet.root), ...await realpath2(fleet.root).then((path) => [path], () => [])];
|
|
10733
|
+
if (sourceForms.some((source) => fleetForms.some((root) => containsPath2(root, source)))) {
|
|
10734
|
+
throw new Error(`Source root ${sourceRoot} is inside registered Fleet '${fleet.id}'; use fleet normalize or ownership retire-stale.`);
|
|
10735
|
+
}
|
|
10736
|
+
}
|
|
10737
|
+
sourceClass = "foreign-root";
|
|
10738
|
+
}
|
|
10739
|
+
return {
|
|
10740
|
+
targetRoot,
|
|
10741
|
+
adapter: request.adapter,
|
|
10742
|
+
installationType,
|
|
10743
|
+
sourceStateKey,
|
|
10744
|
+
destinationStateKey,
|
|
10745
|
+
fingerprint,
|
|
10746
|
+
sourceRoot,
|
|
10747
|
+
sourceOwner,
|
|
10748
|
+
workspaceRoot,
|
|
10749
|
+
destinationOwner,
|
|
10750
|
+
sourceClass
|
|
10751
|
+
};
|
|
10752
|
+
}
|
|
10753
|
+
function legacyFingerprint(adapter, installationType, stateKey) {
|
|
10754
|
+
const prefix = `${adapter}.${installationType}.`;
|
|
10755
|
+
const fingerprint = stateKey.startsWith(prefix) ? stateKey.slice(prefix.length) : "";
|
|
10756
|
+
return /^[a-f0-9]{64}$/u.test(fingerprint) ? fingerprint : void 0;
|
|
10757
|
+
}
|
|
10758
|
+
async function selectLegacySource(input, stateKey, fingerprint, source, desiredByPath) {
|
|
10759
|
+
if (source.adapterCode) {
|
|
10760
|
+
throw new Error(`The install manifest for ${stateKey} records programmatic adapter code; legacy ownership adoption refuses it.`);
|
|
10761
|
+
}
|
|
10762
|
+
if (source.entries.some((entry) => entry.workspaceOwner === input.destinationOwner)) {
|
|
10763
|
+
throw new Error(
|
|
10764
|
+
`Source state ${stateKey} has entries already owned by the destination owner; run a normal install to migrate them first.`
|
|
10765
|
+
);
|
|
10766
|
+
}
|
|
10767
|
+
for (const entry of source.entries) {
|
|
10768
|
+
if (entry.workspaceOwner === input.sourceOwner) assertPlainEntry(entry, input.targetRoot);
|
|
10769
|
+
}
|
|
10770
|
+
const entries = [];
|
|
10771
|
+
const retained = [];
|
|
10772
|
+
for (const entry of source.entries) {
|
|
10773
|
+
if (entry.workspaceOwner !== input.sourceOwner) {
|
|
10774
|
+
retained.push({ path: entry.path, owner: entry.workspaceOwner, reason: "other-owner" });
|
|
10775
|
+
continue;
|
|
10776
|
+
}
|
|
10777
|
+
const desired = desiredByPath.get(entry.path) ?? [];
|
|
10778
|
+
if (desired.length === 0) {
|
|
10779
|
+
retained.push({ path: entry.path, owner: entry.workspaceOwner, reason: "not-desired" });
|
|
10780
|
+
continue;
|
|
10781
|
+
}
|
|
10782
|
+
if (!desired.some((item) => desiredCovers(item, entry))) {
|
|
10783
|
+
throw new Error(`The current graph plans a different artifact at ${entry.path}; legacy ownership cannot be adopted there.`);
|
|
10784
|
+
}
|
|
10785
|
+
entries.push(entry);
|
|
10786
|
+
}
|
|
10787
|
+
if (entries.length === 0) {
|
|
10788
|
+
throw new Error(`Nothing to adopt: no entry owned by ${input.sourceOwner} in ${stateKey} is desired by this workspace.`);
|
|
10789
|
+
}
|
|
10790
|
+
const paths = /* @__PURE__ */ new Set();
|
|
10791
|
+
for (const entry of entries) {
|
|
10792
|
+
if (paths.has(entry.path)) throw new Error(`Source state ${stateKey} has more than one entry at ${entry.path}.`);
|
|
10793
|
+
paths.add(entry.path);
|
|
10794
|
+
}
|
|
10795
|
+
return { entries, retained, provenance: await resolveProvenance(input, fingerprint, entries) };
|
|
10796
|
+
}
|
|
10797
|
+
async function assertRetirableDuplicate(input, item, paths, selectedByPath, desiredByPath, transport) {
|
|
10798
|
+
const refuse = (path, reason) => new Error(
|
|
10799
|
+
`${path} is also claimed by ${input.sourceOwner} in ${item.fileName}, which a follow-up adopt-legacy run could not retire: ${reason}`
|
|
10800
|
+
);
|
|
10801
|
+
const fingerprint = legacyFingerprint(input.adapter, input.installationType, item.stateKey);
|
|
10802
|
+
if (!fingerprint) throw refuse(paths[0], `${item.stateKey} is not a legacy fingerprint state key for ${input.adapter}/${input.installationType}.`);
|
|
10803
|
+
let duplicates;
|
|
10804
|
+
try {
|
|
10805
|
+
const identity = { targetRoot: input.targetRoot, adapter: input.adapter, installationType: input.installationType };
|
|
10806
|
+
const manifest = await requireManifest(identity, item.stateKey, transport, "duplicate source");
|
|
10807
|
+
duplicates = (await selectLegacySource(input, item.stateKey, fingerprint, manifest, desiredByPath)).entries;
|
|
10808
|
+
} catch (error) {
|
|
10809
|
+
throw refuse(paths[0], error instanceof Error ? error.message : String(error));
|
|
10810
|
+
}
|
|
10811
|
+
for (const path of paths) {
|
|
10812
|
+
const duplicate = duplicates.find((entry) => entry.path === path);
|
|
10813
|
+
if (!duplicate || !sameRecordedArtifact(duplicate, selectedByPath.get(path))) {
|
|
10814
|
+
throw refuse(path, "its artifact identity or recorded hashes differ from the adopted entry.");
|
|
10815
|
+
}
|
|
10816
|
+
}
|
|
10817
|
+
}
|
|
10818
|
+
async function resolveProvenance(input, fingerprint, entries) {
|
|
10819
|
+
const locks = await findLegacyNamedLocks(input.sourceRoot, input.adapter, fingerprint);
|
|
10820
|
+
if (locks.length === 0) {
|
|
10821
|
+
throw new Error(
|
|
10822
|
+
`Source root ${input.sourceRoot} has no legacy-named graph lock for ${input.adapter}/${fingerprint}; provenance cannot be proven.`
|
|
10823
|
+
);
|
|
10824
|
+
}
|
|
10825
|
+
if (input.sourceClass === "own-legacy-owner") return locks[0];
|
|
10826
|
+
if (locks.length !== 1) {
|
|
10827
|
+
throw new Error(`Source root ${input.sourceRoot} must have exactly one legacy-named graph lock for ${fingerprint}, found ${locks.length}.`);
|
|
10828
|
+
}
|
|
10829
|
+
const lock = locks[0];
|
|
10830
|
+
for (const entry of entries) {
|
|
10831
|
+
if (entry.graphLockDigest !== lock.digest) {
|
|
10832
|
+
throw new Error(`Recorded graph-lock digest for ${entry.path} does not match ${lock.path}.`);
|
|
10833
|
+
}
|
|
10834
|
+
if (!lockCoversEntry(lock.lock, entry)) {
|
|
10835
|
+
throw new Error(`Legacy graph lock ${lock.path} does not cover ${entry.path} with its recorded node, selector, and source hash.`);
|
|
10836
|
+
}
|
|
10837
|
+
}
|
|
10838
|
+
return lock;
|
|
10839
|
+
}
|
|
10840
|
+
async function readDestination(identity, stateKey, transport) {
|
|
10841
|
+
if (!await readInstallManifest(identity.targetRoot, identity.adapter, transport, { installationType: identity.installationType, stateKey })) {
|
|
10842
|
+
return void 0;
|
|
10843
|
+
}
|
|
10844
|
+
return requireManifest(identity, stateKey, transport, "destination");
|
|
10845
|
+
}
|
|
10846
|
+
function assertPlainEntry(entry, targetRoot) {
|
|
10847
|
+
if (entry.mode || entry.mergeStrategy || entry.semanticPlugin || entry.semanticCommand || entry.executed) {
|
|
10848
|
+
throw new Error(`Legacy ownership adoption supports only plain file or directory entries; ${entry.path} is not one.`);
|
|
10849
|
+
}
|
|
10850
|
+
containedArtifactPath(targetRoot, entry.path);
|
|
10851
|
+
}
|
|
10852
|
+
function desiredCovers(item, entry) {
|
|
10853
|
+
return item.artifactType === entry.artifactType && item.artifactName === entry.artifactName && item.kind === entry.kind && (!item.packageName || !entry.packageName || item.packageName === entry.packageName);
|
|
10854
|
+
}
|
|
10855
|
+
function coversSourceEntry(destination, source, owner) {
|
|
10856
|
+
return destination.workspaceOwner === owner && !destination.mode && !destination.mergeStrategy && !destination.semanticPlugin && sameRecordedArtifact(destination, source);
|
|
10857
|
+
}
|
|
10858
|
+
function sameRecordedArtifact(left, right) {
|
|
10859
|
+
return left.artifactType === right.artifactType && left.artifactName === right.artifactName && left.kind === right.kind && left.hash === right.hash && left.sourceHash === right.sourceHash;
|
|
10860
|
+
}
|
|
10861
|
+
async function assertNoPendingJournals(targetRoot, adapter, transport) {
|
|
10862
|
+
const pending = await listAllApplyJournals(targetRoot, adapter, transport);
|
|
10863
|
+
if (pending.length > 0) {
|
|
10864
|
+
throw new Error(`Cannot adopt legacy ownership while runtime apply journal(s) are pending: ${pending.map((item) => item.path).join(", ")}`);
|
|
10865
|
+
}
|
|
10866
|
+
}
|
|
10867
|
+
function assertApplyPreconditions2(request) {
|
|
10868
|
+
for (const [label, value, allowAbsent] of [
|
|
10869
|
+
["plan digest", request.planDigest, false],
|
|
10870
|
+
["source manifest revision", request.expectedSourceRevision, false],
|
|
10871
|
+
["destination manifest revision", request.expectedDestinationRevision, true],
|
|
10872
|
+
["manifest inventory revision", request.expectedInventoryRevision, false]
|
|
10873
|
+
]) {
|
|
10874
|
+
if (allowAbsent && value === absentRevision) continue;
|
|
10875
|
+
if (!value || !/^[a-f0-9]{64}$/.test(value)) {
|
|
10876
|
+
throw new Error(`Applying legacy ownership adoption requires a reviewed lowercase SHA-256 ${label}${allowAbsent ? " or 'absent'" : ""}.`);
|
|
10877
|
+
}
|
|
10878
|
+
}
|
|
10879
|
+
}
|
|
10880
|
+
function containsPath2(parent, candidate) {
|
|
10881
|
+
const path = relative9(parent, candidate);
|
|
10882
|
+
return path === "" || !path.startsWith("..") && !isAbsolute3(path);
|
|
10883
|
+
}
|
|
10884
|
+
|
|
10469
10885
|
// src/version/policy.ts
|
|
10470
10886
|
import { execFile as execFile3 } from "child_process";
|
|
10471
10887
|
import { readFile as readFile23 } from "fs/promises";
|
|
10472
|
-
import { join as join40, resolve as
|
|
10888
|
+
import { join as join40, resolve as resolve24 } from "path";
|
|
10473
10889
|
import { promisify as promisify3 } from "util";
|
|
10474
10890
|
import { parse as parseJsonc } from "jsonc-parser";
|
|
10475
10891
|
import { z as z5 } from "zod";
|
|
@@ -10563,7 +10979,7 @@ async function discoverVersionsFromSource(pkg, workspaceRoot) {
|
|
|
10563
10979
|
if (tagged.length > 0) return tagged;
|
|
10564
10980
|
}
|
|
10565
10981
|
if (driverName === "local") {
|
|
10566
|
-
const root =
|
|
10982
|
+
const root = resolve24(workspaceRoot, pkg.source);
|
|
10567
10983
|
const manifest2 = await readPackageManifest(root);
|
|
10568
10984
|
const current = manifest2 ? [{ version: manifest2.version, ref: pkg.requestedRef ?? root }] : [];
|
|
10569
10985
|
try {
|
|
@@ -10686,7 +11102,7 @@ async function readVersionCache(path) {
|
|
|
10686
11102
|
// src/profile/members.ts
|
|
10687
11103
|
import { execFile as execFile4 } from "child_process";
|
|
10688
11104
|
import { readFile as readFile24 } from "fs/promises";
|
|
10689
|
-
import { join as join41, resolve as
|
|
11105
|
+
import { join as join41, resolve as resolve25 } from "path";
|
|
10690
11106
|
import { promisify as promisify4 } from "util";
|
|
10691
11107
|
import { z as z7 } from "zod";
|
|
10692
11108
|
|
|
@@ -10861,7 +11277,7 @@ async function invokeMemberStatus(member, parentWorkspace, chain, options, cliEn
|
|
|
10861
11277
|
let stderr = "";
|
|
10862
11278
|
try {
|
|
10863
11279
|
if (member.transport === "local") {
|
|
10864
|
-
const workspace =
|
|
11280
|
+
const workspace = resolve25(parentWorkspace, member.workspace);
|
|
10865
11281
|
const result = await execFileAsync4(process.execPath, [cliEntry, ...args], {
|
|
10866
11282
|
cwd: workspace,
|
|
10867
11283
|
env: env2,
|
|
@@ -10907,7 +11323,7 @@ async function runMemberAgentwheel(member, parentWorkspace, args, chain) {
|
|
|
10907
11323
|
process.execPath,
|
|
10908
11324
|
[process.argv[1], ...memberCommandArgs(member, args)],
|
|
10909
11325
|
{
|
|
10910
|
-
cwd:
|
|
11326
|
+
cwd: resolve25(parentWorkspace, member.workspace),
|
|
10911
11327
|
env: env2,
|
|
10912
11328
|
maxBuffer: 20 * 1024 * 1024
|
|
10913
11329
|
}
|
|
@@ -11013,7 +11429,7 @@ function assertNoCompositeCycle(workspaceRoot, profileName, chain) {
|
|
|
11013
11429
|
}
|
|
11014
11430
|
}
|
|
11015
11431
|
function compositeKey(workspaceRoot, profileName) {
|
|
11016
|
-
return `${
|
|
11432
|
+
return `${resolve25(workspaceRoot)}#${profileName}`;
|
|
11017
11433
|
}
|
|
11018
11434
|
|
|
11019
11435
|
// src/status/repository.ts
|
|
@@ -11060,7 +11476,7 @@ function valueAfter(lines, prefix) {
|
|
|
11060
11476
|
}
|
|
11061
11477
|
|
|
11062
11478
|
// src/catalogue/client.ts
|
|
11063
|
-
import { createHash as
|
|
11479
|
+
import { createHash as createHash12 } from "crypto";
|
|
11064
11480
|
import { readFile as readFile25, rm as rm8 } from "fs/promises";
|
|
11065
11481
|
import { homedir as homedir9 } from "os";
|
|
11066
11482
|
import { join as join42 } from "path";
|
|
@@ -11335,7 +11751,7 @@ var CatalogueClient = class {
|
|
|
11335
11751
|
}
|
|
11336
11752
|
return {
|
|
11337
11753
|
value: schema.parse(value),
|
|
11338
|
-
digest:
|
|
11754
|
+
digest: createHash12("sha256").update(bytes).digest("hex")
|
|
11339
11755
|
};
|
|
11340
11756
|
}
|
|
11341
11757
|
};
|
|
@@ -11346,7 +11762,7 @@ function sameSources2(a, b) {
|
|
|
11346
11762
|
return a.length === b.length && a.every((source, index) => source === b[index]);
|
|
11347
11763
|
}
|
|
11348
11764
|
function catalogueContentHash(enriched, vercel) {
|
|
11349
|
-
return
|
|
11765
|
+
return createHash12("sha256").update(JSON.stringify({ enriched, vercel })).digest("hex");
|
|
11350
11766
|
}
|
|
11351
11767
|
|
|
11352
11768
|
// src/search/index.ts
|
|
@@ -11708,7 +12124,7 @@ function includesToken(normalizedText, token) {
|
|
|
11708
12124
|
}
|
|
11709
12125
|
|
|
11710
12126
|
// src/semantic/index.ts
|
|
11711
|
-
import { createHash as
|
|
12127
|
+
import { createHash as createHash13 } from "crypto";
|
|
11712
12128
|
import { homedir as homedir10 } from "os";
|
|
11713
12129
|
import { join as join43 } from "path";
|
|
11714
12130
|
import { env, pipeline } from "@huggingface/transformers";
|
|
@@ -11798,7 +12214,7 @@ var SemanticSearchClient = class {
|
|
|
11798
12214
|
if (!response.ok) throw new Error(`Semantic index file failed (${response.status}): ${descriptor.path}`);
|
|
11799
12215
|
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
11800
12216
|
if (bytes.byteLength !== descriptor.bytes) throw new Error(`Semantic index file size does not match: ${descriptor.path}`);
|
|
11801
|
-
const digest2 =
|
|
12217
|
+
const digest2 = createHash13("sha256").update(bytes).digest("hex");
|
|
11802
12218
|
if (digest2 !== descriptor.sha256) throw new Error(`Semantic index checksum does not match: ${descriptor.path}`);
|
|
11803
12219
|
return bytes;
|
|
11804
12220
|
}
|
|
@@ -11888,7 +12304,7 @@ function ensureTrailingSlash(value) {
|
|
|
11888
12304
|
}
|
|
11889
12305
|
|
|
11890
12306
|
// src/trial/skill.ts
|
|
11891
|
-
import { createHash as
|
|
12307
|
+
import { createHash as createHash14 } from "crypto";
|
|
11892
12308
|
import { readFile as readFile26, stat as stat13 } from "fs/promises";
|
|
11893
12309
|
import { join as join44 } from "path";
|
|
11894
12310
|
import { parse as parseYaml2 } from "yaml";
|
|
@@ -11918,7 +12334,7 @@ async function createSkillTrial(driver, resolved, selectors) {
|
|
|
11918
12334
|
skill: {
|
|
11919
12335
|
name: artifact.name,
|
|
11920
12336
|
relativePath: artifact.relativePath,
|
|
11921
|
-
sha256:
|
|
12337
|
+
sha256: createHash14("sha256").update(content).digest("hex"),
|
|
11922
12338
|
frontmatter,
|
|
11923
12339
|
content
|
|
11924
12340
|
}
|
|
@@ -11937,12 +12353,12 @@ function readSkillFrontmatter(content, artifact) {
|
|
|
11937
12353
|
}
|
|
11938
12354
|
|
|
11939
12355
|
// src/lifecycle/fleet-normalize.ts
|
|
11940
|
-
import { createHash as
|
|
12356
|
+
import { createHash as createHash15 } from "crypto";
|
|
11941
12357
|
import { readFile as readFile27, readdir as readdir11, rm as rm9 } from "fs/promises";
|
|
11942
12358
|
import { homedir as homedir11 } from "os";
|
|
11943
|
-
import { dirname as dirname22, isAbsolute as
|
|
12359
|
+
import { dirname as dirname22, isAbsolute as isAbsolute4, join as join45, relative as relative10, resolve as resolve26 } from "path";
|
|
11944
12360
|
async function planFleetNormalization(request) {
|
|
11945
|
-
const normalizedRequest =
|
|
12361
|
+
const normalizedRequest = normalizeRequest3(request);
|
|
11946
12362
|
const destinationScope = await resolveWorkspaceScope({
|
|
11947
12363
|
fleet: normalizedRequest.destinationFleet,
|
|
11948
12364
|
globalRoot: normalizedRequest.globalRoot
|
|
@@ -12014,7 +12430,7 @@ async function applyFleetNormalization(request, hooks = {}) {
|
|
|
12014
12430
|
if (!request.apply) throw new Error("Fleet normalization is dry-run by default; pass --apply with --plan-digest <digest>.");
|
|
12015
12431
|
if (!request.planDigest) throw new Error("Applying fleet normalization requires --plan-digest <digest> from a reviewed dry-run.");
|
|
12016
12432
|
assertDigest(request.planDigest);
|
|
12017
|
-
const normalizedRequest =
|
|
12433
|
+
const normalizedRequest = normalizeRequest3(request);
|
|
12018
12434
|
const roots = await normalizationRoots(normalizedRequest);
|
|
12019
12435
|
const ordered = [.../* @__PURE__ */ new Set([roots.source, roots.destination])].sort((a, b) => a.localeCompare(b));
|
|
12020
12436
|
return withOrderedLocks(ordered, async () => {
|
|
@@ -12153,7 +12569,7 @@ async function assertNormalizationPostconditions(journal) {
|
|
|
12153
12569
|
}
|
|
12154
12570
|
}
|
|
12155
12571
|
async function recoverFleetNormalization(request) {
|
|
12156
|
-
const normalizedRequest =
|
|
12572
|
+
const normalizedRequest = normalizeRequest3(request);
|
|
12157
12573
|
const roots = await normalizationRoots(normalizedRequest);
|
|
12158
12574
|
const ordered = [.../* @__PURE__ */ new Set([roots.source, roots.destination])].sort((a, b) => a.localeCompare(b));
|
|
12159
12575
|
return withOrderedLocks(ordered, async () => {
|
|
@@ -12196,7 +12612,7 @@ async function assertSourceFleetPostcondition(request, source, selectedPackages)
|
|
|
12196
12612
|
);
|
|
12197
12613
|
}
|
|
12198
12614
|
}
|
|
12199
|
-
function
|
|
12615
|
+
function normalizeRequest3(request) {
|
|
12200
12616
|
const packages = request.packages === void 0 ? void 0 : sortedUnique6(request.packages);
|
|
12201
12617
|
if (request.packages !== void 0 && packages?.length === 0) throw new Error("--package requires at least one package name.");
|
|
12202
12618
|
const artifacts = request.artifacts === void 0 ? void 0 : sortedUnique6(request.artifacts.map((artifact) => normalizeArtifactSelector(artifact)));
|
|
@@ -12218,17 +12634,17 @@ function normalizeRequest2(request) {
|
|
|
12218
12634
|
...profile ? { profile } : {},
|
|
12219
12635
|
...agent ? { agent } : {},
|
|
12220
12636
|
...orphanedOwnerRoots ? { orphanedOwnerRoots } : {},
|
|
12221
|
-
...request.globalRoot ? { globalRoot:
|
|
12637
|
+
...request.globalRoot ? { globalRoot: resolve26(request.globalRoot) } : {}
|
|
12222
12638
|
};
|
|
12223
12639
|
}
|
|
12224
12640
|
function normalizeOrphanedOwnerRoot(value) {
|
|
12225
12641
|
const root = value.trim();
|
|
12226
|
-
if (!root || !
|
|
12227
|
-
return
|
|
12642
|
+
if (!root || !isAbsolute4(root)) throw new Error(`Invalid orphaned owner root '${value}'; expected an absolute workspace path.`);
|
|
12643
|
+
return resolve26(root);
|
|
12228
12644
|
}
|
|
12229
12645
|
async function normalizationRoots(request) {
|
|
12230
12646
|
const destination = await showRegisteredFleet(request.destinationFleet, { globalRoot: request.globalRoot });
|
|
12231
|
-
const source = request.from === "user" ?
|
|
12647
|
+
const source = request.from === "user" ? resolve26(request.globalRoot ?? homedir11()) : (await showRegisteredFleet(request.from.slice("fleet:".length), { globalRoot: request.globalRoot })).root;
|
|
12232
12648
|
return { source, destination: destination.root };
|
|
12233
12649
|
}
|
|
12234
12650
|
async function withOrderedLocks(roots, fn) {
|
|
@@ -12443,7 +12859,7 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
12443
12859
|
let normalizedGraphCount = 0;
|
|
12444
12860
|
for (const graph of graphCandidates) {
|
|
12445
12861
|
const destinationState = await targetStateForGraph(fleet, graph, fleet.fleetId, allowOrphanedRetiredPackages);
|
|
12446
|
-
if (
|
|
12862
|
+
if (resolve26(destinationState.graphLockPath) === resolve26(graph.path)) {
|
|
12447
12863
|
const destinationManifest = (await collectManifestPaths([destinationState.manifestPath]))[0];
|
|
12448
12864
|
if (destinationManifest?.manifest.entries.some((entry) => entry.workspaceOwner === fleetOwner && entryMatchesPackages(entry, selected))) {
|
|
12449
12865
|
assertCorrelatedGraphManifest(
|
|
@@ -12458,7 +12874,7 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
12458
12874
|
}
|
|
12459
12875
|
}
|
|
12460
12876
|
const legacyState = await legacyTargetStateForGraph(fleet, graph, allowOrphanedRetiredPackages);
|
|
12461
|
-
if (
|
|
12877
|
+
if (resolve26(legacyState.graphLockPath) !== resolve26(graph.path)) {
|
|
12462
12878
|
throw new Error(`Legacy graph lock target identity is stale or noncanonical: ${graph.path}`);
|
|
12463
12879
|
}
|
|
12464
12880
|
const legacyManifest = (await collectManifestPaths([legacyState.manifestPath]))[0];
|
|
@@ -12633,7 +13049,7 @@ function targetKeysForAgent(scope, name) {
|
|
|
12633
13049
|
async function runtimeRoots(scope) {
|
|
12634
13050
|
const roots = /* @__PURE__ */ new Set([scope.root]);
|
|
12635
13051
|
if (scope.config.packages.some((pkg) => pkg.installationType === "user") || Object.values(scope.config.agents).some((agent) => agent.installationType === "user")) {
|
|
12636
|
-
roots.add(
|
|
13052
|
+
roots.add(resolve26(process.env.AGENTWHEEL_TEST_HOME || process.env.HOME || homedir11()));
|
|
12637
13053
|
}
|
|
12638
13054
|
for (const [name, agent] of Object.entries(scope.config.agents)) {
|
|
12639
13055
|
if (agent.transport === "ssh") throw new Error(`Installed-state normalization cannot inspect SSH agent '${name}' locally.`);
|
|
@@ -12670,14 +13086,14 @@ async function collectManifests(roots) {
|
|
|
12670
13086
|
}
|
|
12671
13087
|
continue;
|
|
12672
13088
|
}
|
|
12673
|
-
if (
|
|
13089
|
+
if (resolve26(parsed.targetRoot) !== resolve26(root)) {
|
|
12674
13090
|
throw new Error(`Install manifest target root does not match its enumerated runtime root: ${path}`);
|
|
12675
13091
|
}
|
|
12676
13092
|
const expectedPath = installManifestPath(parsed.targetRoot, parsed.adapter, {
|
|
12677
13093
|
installationType: parsed.installationType,
|
|
12678
13094
|
stateKey: parsed.stateKey
|
|
12679
13095
|
});
|
|
12680
|
-
if (
|
|
13096
|
+
if (resolve26(expectedPath) !== resolve26(path)) {
|
|
12681
13097
|
throw new Error(`Install manifest state identity does not match its canonical path: ${path}`);
|
|
12682
13098
|
}
|
|
12683
13099
|
found.set(path, { path, raw, manifest: { ...parsed, revision: computeManifestRevision(raw), legacy: false } });
|
|
@@ -12698,7 +13114,7 @@ async function collectManifestPaths(paths) {
|
|
|
12698
13114
|
installationType: parsed.installationType,
|
|
12699
13115
|
stateKey: parsed.stateKey
|
|
12700
13116
|
});
|
|
12701
|
-
if (
|
|
13117
|
+
if (resolve26(expectedPath) !== resolve26(path)) {
|
|
12702
13118
|
throw new Error(`Install manifest state identity does not match its canonical path: ${path}`);
|
|
12703
13119
|
}
|
|
12704
13120
|
found.push({ path, raw, manifest: { ...parsed, revision: computeManifestRevision(raw), legacy: false } });
|
|
@@ -12724,7 +13140,7 @@ async function relevantGraphLocks(workspaceRoot, selected, targetKeys, include)
|
|
|
12724
13140
|
if (!path.endsWith(".graph-lock.json")) continue;
|
|
12725
13141
|
const lock = await readGraphLock(path);
|
|
12726
13142
|
if (!lock.canonical.roots.some((candidate2) => selected.has(candidate2.rootId))) continue;
|
|
12727
|
-
const parts =
|
|
13143
|
+
const parts = relative10(root, path).split(/[\\/]/);
|
|
12728
13144
|
if (parts.length !== 3) throw new Error(`Graph lock path is not canonical: ${path}`);
|
|
12729
13145
|
const [targetKey2, adapter] = parts;
|
|
12730
13146
|
if (targetKeys && !targetKeys.has(targetKey2)) continue;
|
|
@@ -12766,7 +13182,7 @@ async function hasRelevantLegacyManifestCandidate(scope, graph, selected) {
|
|
|
12766
13182
|
const legacyManifest = (await collectManifestPaths([legacyState.manifestPath]))[0];
|
|
12767
13183
|
if (legacyManifest?.manifest.entries.some((entry) => entryMatchesGraphPackages(entry, graph.lock, selected))) return true;
|
|
12768
13184
|
const stableState = await targetStateForGraph(scope, graph, scope.fleetId ?? null);
|
|
12769
|
-
if (
|
|
13185
|
+
if (resolve26(stableState.graphLockPath) !== resolve26(graph.path)) return false;
|
|
12770
13186
|
const stableManifest = (await collectManifestPaths([stableState.manifestPath]))[0];
|
|
12771
13187
|
return stableManifest?.manifest.entries.some((entry) => entryMatchesGraphPackages(entry, graph.lock, selected)) ?? false;
|
|
12772
13188
|
}
|
|
@@ -12787,7 +13203,7 @@ async function legacyManifestPathForConfiguredTarget(scope, graph) {
|
|
|
12787
13203
|
baseDir: scope.root
|
|
12788
13204
|
});
|
|
12789
13205
|
const targetRoot = resolveConfigPath(agent.root, scope.root);
|
|
12790
|
-
const installRoot =
|
|
13206
|
+
const installRoot = resolve26(installRootForAdapterInstallationType(adapter, targetRoot, agent.installationType, false));
|
|
12791
13207
|
const stateKey = stateKeyFor(adapter.name, {
|
|
12792
13208
|
installationType: agent.installationType,
|
|
12793
13209
|
stateKey: agent.stateKey,
|
|
@@ -12855,7 +13271,7 @@ async function targetStateForGraph(scope, graph, identityFleetId = scope.fleetId
|
|
|
12855
13271
|
ssh: void 0,
|
|
12856
13272
|
...agent?.stateKey ? { stateKey: agent.stateKey } : {}
|
|
12857
13273
|
};
|
|
12858
|
-
const installRoot =
|
|
13274
|
+
const installRoot = resolve26(installRootForAdapterInstallationType(adapter, targetRoot, installationType, false));
|
|
12859
13275
|
const targetIdentity = resolveTargetStateIdentity({
|
|
12860
13276
|
targetFingerprintParts: fingerprintParts,
|
|
12861
13277
|
workspaceRoot: scope.root,
|
|
@@ -12887,16 +13303,16 @@ async function targetStateForGraph(scope, graph, identityFleetId = scope.fleetId
|
|
|
12887
13303
|
async function installedTargetStateForGraph(scope, graph, manifests, owners, selected, identityFleetId = scope.fleetId ?? null) {
|
|
12888
13304
|
const stable = await targetStateForGraph(scope, graph, identityFleetId);
|
|
12889
13305
|
let state;
|
|
12890
|
-
if (
|
|
13306
|
+
if (resolve26(graph.path) === resolve26(stable.graphLockPath)) {
|
|
12891
13307
|
state = stable;
|
|
12892
13308
|
} else {
|
|
12893
13309
|
const released = await releasedTargetStateForGraph(scope, graph, identityFleetId);
|
|
12894
|
-
if (
|
|
13310
|
+
if (resolve26(graph.path) !== resolve26(released.graphLockPath)) {
|
|
12895
13311
|
throw new Error(`Graph lock target identity is stale or noncanonical: ${graph.path}`);
|
|
12896
13312
|
}
|
|
12897
13313
|
state = released;
|
|
12898
13314
|
}
|
|
12899
|
-
const manifest = manifests.find((candidate) =>
|
|
13315
|
+
const manifest = manifests.find((candidate) => resolve26(candidate.path) === resolve26(state.manifestPath));
|
|
12900
13316
|
if (!manifest) {
|
|
12901
13317
|
throw new Error(`Graph lock is not covered by its canonical install manifest: ${state.manifestPath}`);
|
|
12902
13318
|
}
|
|
@@ -12928,7 +13344,7 @@ async function releasedTargetStateForGraph(scope, graph, identityFleetId = scope
|
|
|
12928
13344
|
};
|
|
12929
13345
|
}
|
|
12930
13346
|
function assertCorrelatedGraphManifest(graph, state, manifest, owners, selected) {
|
|
12931
|
-
if (manifest.manifest.adapter !== state.adapter || manifest.manifest.installationType !== state.installationType || manifest.manifest.stateKey !== state.stateKey ||
|
|
13347
|
+
if (manifest.manifest.adapter !== state.adapter || manifest.manifest.installationType !== state.installationType || manifest.manifest.stateKey !== state.stateKey || resolve26(manifest.manifest.targetRoot) !== resolve26(state.installRoot)) {
|
|
12932
13348
|
throw new Error(`Install manifest identity does not match graph target state: ${manifest.path}`);
|
|
12933
13349
|
}
|
|
12934
13350
|
const entries = manifest.manifest.entries.filter((entry) => entryMatchesGraphPackages(entry, graph.lock, selected));
|
|
@@ -12985,7 +13401,7 @@ function destinationGraphFromSource(source, targetFingerprint) {
|
|
|
12985
13401
|
function mergeDestinationManifest(existing, state, generatedAt, desiredEntries, replaceOwners = /* @__PURE__ */ new Set()) {
|
|
12986
13402
|
if (existing) {
|
|
12987
13403
|
const manifest2 = existing.manifest;
|
|
12988
|
-
if (manifest2.adapter !== state.adapter || manifest2.installationType !== state.installationType || manifest2.stateKey !== state.stateKey ||
|
|
13404
|
+
if (manifest2.adapter !== state.adapter || manifest2.installationType !== state.installationType || manifest2.stateKey !== state.stateKey || resolve26(manifest2.targetRoot) !== state.installRoot) {
|
|
12989
13405
|
throw new Error(`Destination manifest identity diverges from the derived target: ${existing.path}`);
|
|
12990
13406
|
}
|
|
12991
13407
|
}
|
|
@@ -13045,7 +13461,7 @@ async function assertEquivalentRuntimeState(entry, sourcePath, destinationPath)
|
|
|
13045
13461
|
const content = await readFile27(path, "utf8");
|
|
13046
13462
|
if (hasMergeRemovalContent(entry.mergeRemoval)) {
|
|
13047
13463
|
assertExactMergeContribution(entry.mergeRemoval, entry.mergeStrategy, content);
|
|
13048
|
-
} else if (
|
|
13464
|
+
} else if (resolve26(sourcePath) === resolve26(destinationPath) && entry.mergeStrategy !== "codex-toml-mcp") {
|
|
13049
13465
|
assertExactMergeContribution({}, entry.mergeStrategy, content);
|
|
13050
13466
|
} else {
|
|
13051
13467
|
throw new Error(`Installed-state normalization cannot prove incomplete merge ownership at ${path}.`);
|
|
@@ -13056,8 +13472,8 @@ async function assertEquivalentRuntimeState(entry, sourcePath, destinationPath)
|
|
|
13056
13472
|
await assertEquivalentRuntimeBytes(sourcePath, destinationPath, entry.hash);
|
|
13057
13473
|
}
|
|
13058
13474
|
function renderedEntryPathForRoot(root, entryPath) {
|
|
13059
|
-
const normalizedRoot =
|
|
13060
|
-
const candidate =
|
|
13475
|
+
const normalizedRoot = resolve26(root);
|
|
13476
|
+
const candidate = resolve26(normalizedRoot, entryPath);
|
|
13061
13477
|
if (candidate !== normalizedRoot && !candidate.startsWith(`${normalizedRoot}/`)) {
|
|
13062
13478
|
throw new Error(`Install manifest entry escapes its target root: ${entryPath}`);
|
|
13063
13479
|
}
|
|
@@ -13148,8 +13564,8 @@ function ownerMatchesGraphPackage(lock, owner, selected) {
|
|
|
13148
13564
|
return ownerMatchesPackage(owner, selected) || lock.canonical.roots.some((root) => selected.has(root.rootId) && root.graphNodeId === owner);
|
|
13149
13565
|
}
|
|
13150
13566
|
function renderedEntryPath(manifest, entry) {
|
|
13151
|
-
const root =
|
|
13152
|
-
const candidate =
|
|
13567
|
+
const root = resolve26(manifest.targetRoot);
|
|
13568
|
+
const candidate = resolve26(root, entry.path);
|
|
13153
13569
|
if (candidate !== root && !candidate.startsWith(`${root}/`)) {
|
|
13154
13570
|
throw new Error(`Install manifest entry escapes its target root: ${entry.path}`);
|
|
13155
13571
|
}
|
|
@@ -13503,7 +13919,7 @@ function sortValue(value) {
|
|
|
13503
13919
|
return value;
|
|
13504
13920
|
}
|
|
13505
13921
|
function sha2565(value) {
|
|
13506
|
-
return
|
|
13922
|
+
return createHash15("sha256").update(value).digest("hex");
|
|
13507
13923
|
}
|
|
13508
13924
|
function assertDigest(value) {
|
|
13509
13925
|
if (!/^[a-f0-9]{64}$/.test(value)) throw new Error(`Invalid plan digest: ${value}`);
|
|
@@ -13543,6 +13959,7 @@ var commandGovernance = {
|
|
|
13543
13959
|
"ownership handoff": "governed-unless-dry-run",
|
|
13544
13960
|
"ownership recovery-plan": "incidental-cache",
|
|
13545
13961
|
"ownership retire-stale": "governed-apply",
|
|
13962
|
+
"ownership adopt-legacy": "governed-apply",
|
|
13546
13963
|
"mcp retire": "governed-apply",
|
|
13547
13964
|
eject: "governed-always",
|
|
13548
13965
|
uninstall: "governed-unless-dry-run",
|
|
@@ -13962,7 +14379,7 @@ ownershipCommand.command("handoff").description("transfer one managed artifact b
|
|
|
13962
14379
|
const toWorkspaceRoot = normalizeCliPath(options.toWorkspaceRoot);
|
|
13963
14380
|
if (options.toFleet) {
|
|
13964
14381
|
const fleet = await showRegisteredFleet(options.toFleet);
|
|
13965
|
-
if (
|
|
14382
|
+
if (resolve27(fleet.root) !== resolve27(toWorkspaceRoot)) {
|
|
13966
14383
|
throw new Error(`Destination fleet '${options.toFleet}' is registered at ${fleet.root}, not ${toWorkspaceRoot}.`);
|
|
13967
14384
|
}
|
|
13968
14385
|
}
|
|
@@ -13996,49 +14413,15 @@ ownershipCommand.command("recovery-plan").description("write a read-only per-pat
|
|
|
13996
14413
|
const adapter = await resolveAdapterForTarget(target, {});
|
|
13997
14414
|
const transport = transportForTarget(target);
|
|
13998
14415
|
const requestedReport = normalizeCliPath(options.report);
|
|
13999
|
-
const reportPath = join46(await
|
|
14000
|
-
const reportInsideTarget =
|
|
14001
|
-
if (reportInsideTarget === "" || !reportInsideTarget.startsWith("..") && !
|
|
14416
|
+
const reportPath = join46(await realpath3(dirname23(requestedReport)), basename21(requestedReport));
|
|
14417
|
+
const reportInsideTarget = relative11(resolve27(target.targetRoot), reportPath);
|
|
14418
|
+
if (reportInsideTarget === "" || !reportInsideTarget.startsWith("..") && !isAbsolute5(reportInsideTarget)) {
|
|
14002
14419
|
throw new Error("Recovery report must be outside the runtime target root.");
|
|
14003
14420
|
}
|
|
14004
14421
|
const installationType = target.installationType ?? resolveInstallationTypeForAdapter(adapter, void 0);
|
|
14005
14422
|
const config = await readMergedWorkspaceConfig(target.workspaceRoot);
|
|
14006
14423
|
if (config.packages.length === 0) throw new Error("Fleet has no configured source packages.");
|
|
14007
|
-
|
|
14008
|
-
const priorTmpdir = process.env.TMPDIR;
|
|
14009
|
-
try {
|
|
14010
|
-
process.env.TMPDIR = stage;
|
|
14011
|
-
const graphPlan = await createGraphSourcePlan({
|
|
14012
|
-
roots: config.packages.map((pkg) => ({
|
|
14013
|
-
rootId: pkg.name,
|
|
14014
|
-
source: pkg.source,
|
|
14015
|
-
mode: pkg.mode,
|
|
14016
|
-
version: pkg.version,
|
|
14017
|
-
ref: pkg.requestedRef,
|
|
14018
|
-
select: pkg.selection ? void 0 : normalizeArtifactSelectors(pkg.select, pkg.skills),
|
|
14019
|
-
selection: pkg.selection,
|
|
14020
|
-
aliases: pkg.aliases,
|
|
14021
|
-
overrides: pkg.overrides,
|
|
14022
|
-
includeSuggestions: pkg.withSuggestions,
|
|
14023
|
-
suggestionAliases: pkg.suggestions,
|
|
14024
|
-
useLock: false
|
|
14025
|
-
})),
|
|
14026
|
-
targetRoot: target.targetRoot,
|
|
14027
|
-
workspaceRoot: target.workspaceRoot,
|
|
14028
|
-
fleetId: target.fleetId,
|
|
14029
|
-
adapter,
|
|
14030
|
-
transport,
|
|
14031
|
-
targetKey: targetKeyForTarget(target, adapter.name),
|
|
14032
|
-
targetFingerprintParts: targetFingerprintParts(target, adapter, adapterOptionsForTarget(target, {}), installationType),
|
|
14033
|
-
installationType,
|
|
14034
|
-
stateKey: target.stateKey,
|
|
14035
|
-
readOnly: true,
|
|
14036
|
-
yes: true,
|
|
14037
|
-
freshGraphOnly: true,
|
|
14038
|
-
deferForeignStateCheck: true,
|
|
14039
|
-
cacheRoot: join46(stage, "source-cache"),
|
|
14040
|
-
registryCachePath: join46(stage, "registry.json")
|
|
14041
|
-
});
|
|
14424
|
+
await withFreshReadOnlyGraphPlan(target, adapter, adapterOptionsForTarget(target, {}), installationType, config.packages, async (graphPlan) => {
|
|
14042
14425
|
const installRoot = installRootForAdapterInstallationType(adapter, target.targetRoot, installationType, transport.kind === "ssh");
|
|
14043
14426
|
const report = await planLegacyRecovery({
|
|
14044
14427
|
graphPlan,
|
|
@@ -14056,17 +14439,13 @@ ownershipCommand.command("recovery-plan").description("write a read-only per-pat
|
|
|
14056
14439
|
`, { mode: 384, flag: "wx" });
|
|
14057
14440
|
console.log(`Recovery report: ${reportPath}`);
|
|
14058
14441
|
console.log(JSON.stringify(report.counts));
|
|
14059
|
-
}
|
|
14060
|
-
if (priorTmpdir === void 0) delete process.env.TMPDIR;
|
|
14061
|
-
else process.env.TMPDIR = priorTmpdir;
|
|
14062
|
-
await rm10(stage, { recursive: true, force: true });
|
|
14063
|
-
}
|
|
14442
|
+
});
|
|
14064
14443
|
});
|
|
14065
14444
|
ownershipCommand.command("retire-stale").description("retire stale source-manifest ownership already covered by one Fleet manifest").requiredOption("--from-workspace-root <path>", "exact stale workspace owner root").option("--from-fleet-id <id>", "exact stale Fleet owner id for same-Fleet duplicate state").requiredOption("--to-workspace-root <path>", "registered destination Fleet root").requiredOption("--source-state-key <key>", "exact source install-manifest state key").option("--destination-state-key <key>", "exact Fleet-owned destination install-manifest state key (defaults to the stable target state)").requiredOption("--fleet <id>", "registered destination Fleet").option("--adapter <adapter>", "built-in adapter").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named destination Fleet agent").option("--profile <name>", "destination Fleet profile resolving to one target").option("--plan-digest <sha256>", "reviewed plan digest; required with --apply").option("--expected-source-revision <sha256>", "reviewed source manifest revision; required with --apply").option("--expected-destination-revision <sha256>", "reviewed destination manifest revision; required with --apply").option("--expected-inventory-revision <sha256>", "reviewed runtime manifest inventory revision; required with --apply").option("--abandon-incomplete-merge-owner", "explicitly retire incomplete legacy merge metadata without changing runtime bytes", false).option("--apply", "apply the exact reviewed metadata-only retirement", false).option("--json", "print the complete deterministic plan as JSON", false).action(async (options) => {
|
|
14066
14445
|
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
14067
14446
|
const fleet = await showRegisteredFleet(options.fleet);
|
|
14068
14447
|
const toWorkspaceRoot = normalizeCliPath(options.toWorkspaceRoot);
|
|
14069
|
-
if (
|
|
14448
|
+
if (resolve27(fleet.root) !== resolve27(toWorkspaceRoot)) {
|
|
14070
14449
|
throw new Error(`Destination Fleet '${options.fleet}' is registered at ${fleet.root}, not ${toWorkspaceRoot}.`);
|
|
14071
14450
|
}
|
|
14072
14451
|
const targets = await resolveCliTargets(normalizedOptions);
|
|
@@ -14074,7 +14453,7 @@ ownershipCommand.command("retire-stale").description("retire stale source-manife
|
|
|
14074
14453
|
throw new Error(`Stale ownership retirement requires exactly one destination Fleet target, found ${targets.length}.`);
|
|
14075
14454
|
}
|
|
14076
14455
|
const target = targets[0];
|
|
14077
|
-
if (target.fleetId !== options.fleet ||
|
|
14456
|
+
if (target.fleetId !== options.fleet || resolve27(target.workspaceRoot) !== resolve27(fleet.root)) {
|
|
14078
14457
|
throw new Error(`Resolved target does not belong to destination Fleet '${options.fleet}'.`);
|
|
14079
14458
|
}
|
|
14080
14459
|
const adapterOptions = adapterOptionsForTarget(target, normalizedOptions);
|
|
@@ -14146,6 +14525,138 @@ ownershipCommand.command("retire-stale").description("retire stale source-manife
|
|
|
14146
14525
|
console.log(`Plan digest: ${result.planDigest}`);
|
|
14147
14526
|
if (!options.apply) console.log(`Apply: ${applyCommand}`);
|
|
14148
14527
|
});
|
|
14528
|
+
ownershipCommand.command("adopt-legacy").description("move proven legacy-keyed ownership into this workspace's stable target state without rewriting runtime artifacts").requiredOption("--source-state-key <key>", "exact legacy install-manifest state key").requiredOption("--from-workspace-root <path>", "exact plain workspace owner root of the entries to move").option("--agent <name>", "named agent of this workspace").option("--profile <name>", "workspace profile resolving to one target").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("--carry-drift", "also move entries whose runtime differs from the recorded hash; the recorded hash is kept", false).option("--plan-digest <sha256>", "reviewed plan digest; required with --apply").option("--expected-source-revision <sha256>", "reviewed source manifest revision; required with --apply").option("--expected-destination-revision <sha256|absent>", "reviewed destination manifest revision; required with --apply").option("--expected-inventory-revision <sha256>", "reviewed runtime manifest inventory revision; required with --apply").option("--apply", "apply the exact reviewed metadata-only adoption", false).option("--json", "print the complete deterministic plan as JSON", false).action(async (options) => {
|
|
14529
|
+
if (Boolean(options.agent) === Boolean(options.profile)) {
|
|
14530
|
+
throw new Error("Legacy ownership adoption requires exactly one of --agent or --profile.");
|
|
14531
|
+
}
|
|
14532
|
+
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
14533
|
+
const requestedInstallationType = normalizedOptions.installationType;
|
|
14534
|
+
const plainInstallOptions = {
|
|
14535
|
+
...normalizedOptions,
|
|
14536
|
+
installationType: void 0,
|
|
14537
|
+
adapterConfig: void 0,
|
|
14538
|
+
adapterModule: void 0
|
|
14539
|
+
};
|
|
14540
|
+
const targets = await resolveCliTargets(plainInstallOptions);
|
|
14541
|
+
if (targets.length !== 1) {
|
|
14542
|
+
throw new Error(`Legacy ownership adoption requires exactly one runtime target, found ${targets.length}.`);
|
|
14543
|
+
}
|
|
14544
|
+
const target = targets[0];
|
|
14545
|
+
if (target.fleetId) {
|
|
14546
|
+
throw new Error("Legacy ownership adoption targets a nested workspace; a Fleet target uses fleet normalize or ownership retire-stale.");
|
|
14547
|
+
}
|
|
14548
|
+
if (target.transport === "ssh") throw new Error("Legacy ownership adoption does not support SSH targets.");
|
|
14549
|
+
const config = await readMergedWorkspaceConfig(target.workspaceRoot);
|
|
14550
|
+
const groups = /* @__PURE__ */ new Map();
|
|
14551
|
+
for (const pkg of config.packages) graphGroupForPackage(groups, target, pkg, plainInstallOptions).packages.push(pkg);
|
|
14552
|
+
if (groups.size === 0) throw new Error(`No configured source packages in ${target.workspaceRoot}.`);
|
|
14553
|
+
const installationTypes = [...new Set([...groups.values()].map((candidate) => candidate.installationType))];
|
|
14554
|
+
if (installationTypes.length > 1) {
|
|
14555
|
+
throw new Error(
|
|
14556
|
+
`Configured packages use installation types ${installationTypes.join(", ")}, each with its own install state; legacy ownership adoption needs exactly one.`
|
|
14557
|
+
);
|
|
14558
|
+
}
|
|
14559
|
+
if (requestedInstallationType !== void 0 && requestedInstallationType !== installationTypes[0]) {
|
|
14560
|
+
throw new Error(
|
|
14561
|
+
`--installation-type ${requestedInstallationType} does not match installation type ${installationTypes[0]}, which install uses for the configured packages.`
|
|
14562
|
+
);
|
|
14563
|
+
}
|
|
14564
|
+
if (groups.size > 1) {
|
|
14565
|
+
throw new Error(
|
|
14566
|
+
`Configured packages resolve to ${groups.size} adapter configurations for ${installationTypes[0]}, each with its own install state; legacy ownership adoption needs exactly one.`
|
|
14567
|
+
);
|
|
14568
|
+
}
|
|
14569
|
+
const group = [...groups.values()][0];
|
|
14570
|
+
const { installationType, adapterOptions } = group;
|
|
14571
|
+
const requestedAdapterSettings = [
|
|
14572
|
+
{ flag: "--adapter-config", requested: normalizedOptions.adapterConfig, resolved: adapterOptions.adapterConfig, none: "no adapter config" },
|
|
14573
|
+
{ flag: "--adapter-module", requested: normalizedOptions.adapterModule, resolved: adapterOptions.adapterModule, none: "no adapter module" }
|
|
14574
|
+
];
|
|
14575
|
+
for (const { flag, requested, resolved, none } of requestedAdapterSettings) {
|
|
14576
|
+
if (requested === void 0) continue;
|
|
14577
|
+
if (resolved !== void 0 && resolve27(target.workspaceRoot, requested) === resolve27(target.workspaceRoot, resolved)) continue;
|
|
14578
|
+
throw new Error(`${flag} ${requested} does not match what install resolves for the configured packages: ${resolved ?? none}.`);
|
|
14579
|
+
}
|
|
14580
|
+
const adapter = await resolveAdapterForTarget(group.target, adapterOptions);
|
|
14581
|
+
const stable = installStateForTarget(group.target, adapter, adapterOptions, installationType);
|
|
14582
|
+
const stableGraphLockPath = graphLockPathForTarget(
|
|
14583
|
+
group.target.workspaceRoot,
|
|
14584
|
+
targetKeyForTarget(group.target, adapter.name),
|
|
14585
|
+
adapter.name,
|
|
14586
|
+
targetFingerprintParts(group.target, adapter, adapterOptions, installationType),
|
|
14587
|
+
targetIdentityContext(group.target, adapter, installationType)
|
|
14588
|
+
);
|
|
14589
|
+
const desiredCoverage = await withFreshReadOnlyGraphPlan(group.target, adapter, adapterOptions, installationType, group.packages, async (graphPlan) => {
|
|
14590
|
+
if (graphPlan.plan.stateKey !== stable.stateKey || graphPlan.graphLockPath !== stableGraphLockPath) {
|
|
14591
|
+
throw new Error("Current source graph resolved a different target state identity; legacy ownership adoption cannot proceed.");
|
|
14592
|
+
}
|
|
14593
|
+
return desiredCoverageFromPlan(graphPlan.plan);
|
|
14594
|
+
});
|
|
14595
|
+
const request = {
|
|
14596
|
+
targetRoot: stable.installRoot,
|
|
14597
|
+
adapter: adapter.name,
|
|
14598
|
+
installationType,
|
|
14599
|
+
sourceStateKey: options.sourceStateKey,
|
|
14600
|
+
destinationStateKey: stable.stateKey,
|
|
14601
|
+
fromWorkspaceRoot: normalizeCliPath(options.fromWorkspaceRoot),
|
|
14602
|
+
workspaceRoot: target.workspaceRoot,
|
|
14603
|
+
destinationFleetId: target.fleetId,
|
|
14604
|
+
destinationGraphLockPath: stableGraphLockPath,
|
|
14605
|
+
desiredCoverage,
|
|
14606
|
+
carryDrift: options.carryDrift === true,
|
|
14607
|
+
planDigest: options.planDigest,
|
|
14608
|
+
expectedSourceRevision: options.expectedSourceRevision,
|
|
14609
|
+
expectedDestinationRevision: options.expectedDestinationRevision,
|
|
14610
|
+
expectedInventoryRevision: options.expectedInventoryRevision,
|
|
14611
|
+
transport: transportForTarget(target)
|
|
14612
|
+
};
|
|
14613
|
+
const result = options.apply ? await applyAdoptLegacyOwnership(request) : await planAdoptLegacyOwnership(request);
|
|
14614
|
+
const destinationRevision = result.destination.revision ?? "absent";
|
|
14615
|
+
const applyArgs = [
|
|
14616
|
+
"agentwheel",
|
|
14617
|
+
"ownership",
|
|
14618
|
+
"adopt-legacy",
|
|
14619
|
+
"--source-state-key",
|
|
14620
|
+
result.source.stateKey,
|
|
14621
|
+
"--from-workspace-root",
|
|
14622
|
+
request.fromWorkspaceRoot,
|
|
14623
|
+
...options.profile ? ["--profile", options.profile] : ["--agent", options.agent],
|
|
14624
|
+
"--installation-type",
|
|
14625
|
+
installationType,
|
|
14626
|
+
...options.adapterConfig ? ["--adapter-config", options.adapterConfig] : [],
|
|
14627
|
+
...options.adapterModule ? ["--adapter-module", options.adapterModule] : [],
|
|
14628
|
+
...options.allowAdapterCode ? ["--allow-adapter-code"] : [],
|
|
14629
|
+
...request.carryDrift ? ["--carry-drift"] : [],
|
|
14630
|
+
"--plan-digest",
|
|
14631
|
+
result.planDigest,
|
|
14632
|
+
"--expected-source-revision",
|
|
14633
|
+
result.source.revision,
|
|
14634
|
+
"--expected-destination-revision",
|
|
14635
|
+
destinationRevision,
|
|
14636
|
+
"--expected-inventory-revision",
|
|
14637
|
+
result.manifestInventoryRevision,
|
|
14638
|
+
"--apply"
|
|
14639
|
+
];
|
|
14640
|
+
const applyCommand = applyArgs.map(shellQuoteArgument).join(" ");
|
|
14641
|
+
if (options.json) {
|
|
14642
|
+
console.log(JSON.stringify({ ...result, applyCommand }, null, 2));
|
|
14643
|
+
return;
|
|
14644
|
+
}
|
|
14645
|
+
console.log(`${options.apply ? "Adopted legacy ownership" : "Legacy ownership adoption plan"}: ${result.selected.length} entr${result.selected.length === 1 ? "y" : "ies"}`);
|
|
14646
|
+
console.log(`Target: ${result.adapter}/${result.installationType} at ${result.targetRoot}`);
|
|
14647
|
+
console.log(`Source: ${result.source.stateKey} revision ${result.source.revision} (${result.source.class}, ${result.source.owner})`);
|
|
14648
|
+
console.log(`Provenance: ${result.source.provenanceLock.path} (${result.source.provenanceLock.digest})`);
|
|
14649
|
+
console.log(`Destination: ${result.destination.stateKey} revision ${destinationRevision} (${result.destination.owner})`);
|
|
14650
|
+
console.log(`Manifest inventory revision: ${result.manifestInventoryRevision}`);
|
|
14651
|
+
for (const entry of result.selected) {
|
|
14652
|
+
const drift = entry.drift ? ` drift: recorded ${entry.recordedHash}, runtime ${entry.runtimeHash ?? "missing"}` : "";
|
|
14653
|
+
console.log(`- ${entry.path}: ${entry.action}${drift}`);
|
|
14654
|
+
}
|
|
14655
|
+
for (const entry of result.retained) console.log(` retained ${entry.path} (${entry.reason}, ${entry.owner})`);
|
|
14656
|
+
for (const entry of result.remainingDuplicates) console.log(` duplicate claim remains in ${entry.stateKey}: ${entry.path}`);
|
|
14657
|
+
console.log(`Plan digest: ${result.planDigest}`);
|
|
14658
|
+
if (!options.apply) console.log(`Apply from ${target.workspaceRoot}: ${applyCommand}`);
|
|
14659
|
+
});
|
|
14149
14660
|
var mcpCommand = program.command("mcp").description("operate on MCP runtime configuration");
|
|
14150
14661
|
mcpCommand.command("retire").description("remove one exact legacy MCP contribution with explicit state ownership").argument("<package>", "configured package containing exactly one legacy MCP artifact").option("--user", "use the user workspace", false).option("--local", "use the nearest local workspace", false).option("--fleet <id>", "use one registered named fleet").option("-t, --target-root <path>", "explicit runtime/project root").option("--agent <name>", "one named agent from merged config").option("--profile <name>", "workspace runtime profile resolving to one target").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--from-workspace-root <path>", "required previous owner when managed legacy state exists").option("--dry-run", "show the retirement plan without writing (default)", false).option("--apply", "apply the exact reviewed retirement plan", false).option("--json", "print the install plan as JSON", false).action(async (packageName, options) => {
|
|
14151
14662
|
if (options.apply && options.dryRun) throw new Error("--apply cannot be combined with --dry-run.");
|
|
@@ -15161,7 +15672,7 @@ function scopeUpdatePlanToDependencies(result, selectors, previousLock, manifest
|
|
|
15161
15672
|
selectedPreviousNodeIds,
|
|
15162
15673
|
selectedRootIds
|
|
15163
15674
|
);
|
|
15164
|
-
const graphLockDigest =
|
|
15675
|
+
const graphLockDigest = createHash16("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
|
|
15165
15676
|
return {
|
|
15166
15677
|
...result,
|
|
15167
15678
|
bundle: { ...result.bundle, graphLock },
|
|
@@ -15287,7 +15798,7 @@ function operationBelongsToDependencyUpdate(operation, allowedNodeIds, allowedNa
|
|
|
15287
15798
|
function scopeInstallPlanToArtifact(result, rootId, focused, manifest) {
|
|
15288
15799
|
const scoped = scopePlanOperationsToArtifact(result, rootId, focused, manifest);
|
|
15289
15800
|
const graphLock = focusedGraphLockForInstall(scoped.bundle.graphLock, rootId, focused);
|
|
15290
|
-
const graphLockDigest =
|
|
15801
|
+
const graphLockDigest = createHash16("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
|
|
15291
15802
|
return {
|
|
15292
15803
|
...scoped,
|
|
15293
15804
|
bundle: { ...scoped.bundle, graphLock },
|
|
@@ -15298,7 +15809,7 @@ function scopeInstallPlanToArtifact(result, rootId, focused, manifest) {
|
|
|
15298
15809
|
function scopeUpdatePlanToArtifact(result, rootId, focused, previousLock, manifest) {
|
|
15299
15810
|
const scoped = scopePlanOperationsToArtifact(result, rootId, focused, manifest, previousLock);
|
|
15300
15811
|
const graphLock = mergeFocusedArtifactGraphLock(scoped.bundle.graphLock, previousLock, rootId, focused);
|
|
15301
|
-
const graphLockDigest =
|
|
15812
|
+
const graphLockDigest = createHash16("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
|
|
15302
15813
|
return {
|
|
15303
15814
|
...scoped,
|
|
15304
15815
|
bundle: { ...scoped.bundle, graphLock },
|
|
@@ -15593,7 +16104,7 @@ function scopeUpdatePlanToRoot(result, rootId, previousLock, manifest) {
|
|
|
15593
16104
|
selectedPreviousNodeIds,
|
|
15594
16105
|
/* @__PURE__ */ new Set([rootId])
|
|
15595
16106
|
);
|
|
15596
|
-
const graphLockDigest =
|
|
16107
|
+
const graphLockDigest = createHash16("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
|
|
15597
16108
|
return {
|
|
15598
16109
|
...scoped,
|
|
15599
16110
|
bundle: { ...scoped.bundle, graphLock },
|
|
@@ -15855,6 +16366,49 @@ function targetFingerprintParts(target, adapter, options, installationType) {
|
|
|
15855
16366
|
stateKey: target.stateKey
|
|
15856
16367
|
};
|
|
15857
16368
|
}
|
|
16369
|
+
async function withFreshReadOnlyGraphPlan(target, adapter, adapterOptions, installationType, packages, use) {
|
|
16370
|
+
const stage = await mkdtemp4(join46(tmpdir5(), "agentwheel-recovery-"));
|
|
16371
|
+
const priorTmpdir = process.env.TMPDIR;
|
|
16372
|
+
try {
|
|
16373
|
+
process.env.TMPDIR = stage;
|
|
16374
|
+
const graphPlan = await createGraphSourcePlan({
|
|
16375
|
+
roots: packages.map((pkg) => ({
|
|
16376
|
+
rootId: pkg.name,
|
|
16377
|
+
source: pkg.source,
|
|
16378
|
+
mode: pkg.mode,
|
|
16379
|
+
version: pkg.version,
|
|
16380
|
+
ref: pkg.requestedRef,
|
|
16381
|
+
select: pkg.selection ? void 0 : normalizeArtifactSelectors(pkg.select, pkg.skills),
|
|
16382
|
+
selection: pkg.selection,
|
|
16383
|
+
aliases: pkg.aliases,
|
|
16384
|
+
overrides: pkg.overrides,
|
|
16385
|
+
includeSuggestions: pkg.withSuggestions,
|
|
16386
|
+
suggestionAliases: pkg.suggestions,
|
|
16387
|
+
useLock: false
|
|
16388
|
+
})),
|
|
16389
|
+
targetRoot: target.targetRoot,
|
|
16390
|
+
workspaceRoot: target.workspaceRoot,
|
|
16391
|
+
fleetId: target.fleetId,
|
|
16392
|
+
adapter,
|
|
16393
|
+
transport: transportForTarget(target),
|
|
16394
|
+
targetKey: targetKeyForTarget(target, adapter.name),
|
|
16395
|
+
targetFingerprintParts: targetFingerprintParts(target, adapter, adapterOptions, installationType),
|
|
16396
|
+
installationType,
|
|
16397
|
+
stateKey: target.stateKey,
|
|
16398
|
+
readOnly: true,
|
|
16399
|
+
yes: true,
|
|
16400
|
+
freshGraphOnly: true,
|
|
16401
|
+
deferForeignStateCheck: true,
|
|
16402
|
+
cacheRoot: join46(stage, "source-cache"),
|
|
16403
|
+
registryCachePath: join46(stage, "registry.json")
|
|
16404
|
+
});
|
|
16405
|
+
return await use(graphPlan);
|
|
16406
|
+
} finally {
|
|
16407
|
+
if (priorTmpdir === void 0) delete process.env.TMPDIR;
|
|
16408
|
+
else process.env.TMPDIR = priorTmpdir;
|
|
16409
|
+
await rm10(stage, { recursive: true, force: true });
|
|
16410
|
+
}
|
|
16411
|
+
}
|
|
15858
16412
|
function installStateForTarget(target, adapter, options, installationType) {
|
|
15859
16413
|
const installRoot = installRootForAdapterInstallationType(adapter, target.targetRoot, installationType, target.transport === "ssh");
|
|
15860
16414
|
const targetIdentity = resolveTargetStateIdentity({
|
|
@@ -15933,7 +16487,7 @@ async function resolveCliTargetState(target, options) {
|
|
|
15933
16487
|
migration: prior.migration,
|
|
15934
16488
|
targetStateFilePreconditions: {
|
|
15935
16489
|
graphLockPath: stableGraphLockPath,
|
|
15936
|
-
graphLockRevision: stableLock ?
|
|
16490
|
+
graphLockRevision: stableLock ? createHash16("sha256").update(canonicalGraphLockJson(stableLock)).digest("hex") : null,
|
|
15937
16491
|
sourceLockRevision: computeSourceLockRevision(stableSourceLock)
|
|
15938
16492
|
}
|
|
15939
16493
|
};
|
|
@@ -15956,7 +16510,7 @@ async function collectTargetStatus(target, options) {
|
|
|
15956
16510
|
const pending = await collectPendingInstallWork(target, options);
|
|
15957
16511
|
const foreignObservations = pending.foreignStateObservations;
|
|
15958
16512
|
const effectiveGraphLock = graphLock ?? pending.observedGraphLock;
|
|
15959
|
-
const observedManifestRevision = foreignObservations.length > 0 ?
|
|
16513
|
+
const observedManifestRevision = foreignObservations.length > 0 ? createHash16("sha256").update(JSON.stringify(foreignObservations.map((observation) => [
|
|
15960
16514
|
observation.fileName,
|
|
15961
16515
|
observation.manifestRevision
|
|
15962
16516
|
]).sort())).digest("hex") : null;
|
|
@@ -16203,7 +16757,7 @@ async function assertCompositeMutationSupported(workspaceRoot, profile, readOnly
|
|
|
16203
16757
|
`Composite-profile mutation is refused before member execution because ${member.id} uses ${member.transport}; durable remote policy and recovery preflight is not implemented.`
|
|
16204
16758
|
);
|
|
16205
16759
|
}
|
|
16206
|
-
const memberRoot =
|
|
16760
|
+
const memberRoot = resolve27(workspaceRoot, member.workspace);
|
|
16207
16761
|
const memberPolicy = await mutationPolicyForWorkspace(memberRoot);
|
|
16208
16762
|
if (memberPolicy?.revisioning.mode === "commit-after-verify") {
|
|
16209
16763
|
throw new Error(
|
|
@@ -16688,14 +17242,14 @@ function looksLikeSourceSpecifier(value) {
|
|
|
16688
17242
|
}
|
|
16689
17243
|
function normalizeCliPath(value) {
|
|
16690
17244
|
if (value === "~") return homedir12();
|
|
16691
|
-
if (value.startsWith("~/")) return
|
|
16692
|
-
return
|
|
17245
|
+
if (value.startsWith("~/")) return resolve27(homedir12(), value.slice(2));
|
|
17246
|
+
return resolve27(value);
|
|
16693
17247
|
}
|
|
16694
17248
|
function shellQuoteArgument(value) {
|
|
16695
17249
|
return /^[a-z0-9_./:@%+=,-]+$/i.test(value) ? value : `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
16696
17250
|
}
|
|
16697
17251
|
function isHomePath(path) {
|
|
16698
|
-
return
|
|
17252
|
+
return resolve27(path) === resolve27(homedir12());
|
|
16699
17253
|
}
|
|
16700
17254
|
function adapterListFromOption(adapter) {
|
|
16701
17255
|
if (!adapter) return [];
|
|
@@ -16816,7 +17370,7 @@ function withFleetExample(config) {
|
|
|
16816
17370
|
};
|
|
16817
17371
|
}
|
|
16818
17372
|
async function findAgentwheelPackageRoot(start) {
|
|
16819
|
-
let current =
|
|
17373
|
+
let current = resolve27(start);
|
|
16820
17374
|
while (true) {
|
|
16821
17375
|
if (await findPackageManifestPath(current, { warnLegacy: false })) return current;
|
|
16822
17376
|
const parent = dirname23(current);
|