agentwheel 0.20.9 → 0.20.11
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 +16 -0
- package/dist/{apply-JBULQGW5.js → apply-ZGCJLZMZ.js} +3 -3
- package/dist/{chunk-E7Z2YB2C.js → chunk-A44XYLUT.js} +3 -2
- package/dist/{chunk-SHPNLNBR.js → chunk-FP26KSBM.js} +3 -3
- package/dist/{chunk-7VPI5J5Y.js → chunk-GE7F76AC.js} +21 -0
- package/dist/{chunk-EMDIG24I.js → chunk-U2GRG4UI.js} +1 -1
- package/dist/{identify-D64HZTUT.js → identify-O54QXPQR.js} +2 -2
- package/dist/index.js +523 -189
- package/dist/{transaction-EPP7GHXD.js → transaction-4KBNHDQX.js} +2 -2
- package/openpack.json +1 -1
- package/package.json +1 -1
- package/skills/agentwheel/SKILL.md +15 -1
- package/skills/agentwheel-discovery/SKILL.md +1 -1
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
MergeAdoptionMismatchError,
|
|
4
4
|
applyCombinedInstallPlan,
|
|
5
5
|
assertExactMergeContribution,
|
|
6
|
+
assertMergedSourceContribution,
|
|
6
7
|
assertOperationContained,
|
|
7
8
|
assertSafeInstallName,
|
|
8
9
|
canonicalGraphLockJson,
|
|
@@ -36,7 +37,7 @@ import {
|
|
|
36
37
|
uninstall,
|
|
37
38
|
withManifestRevision,
|
|
38
39
|
writeInstallManifest
|
|
39
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-A44XYLUT.js";
|
|
40
41
|
import {
|
|
41
42
|
CURRENT_WORKSPACE_SCHEMA_VERSION,
|
|
42
43
|
GovernedMutation,
|
|
@@ -97,10 +98,10 @@ import {
|
|
|
97
98
|
workspaceSelectionImportSchema,
|
|
98
99
|
writeApplyJournal,
|
|
99
100
|
writeWorkspaceConfig
|
|
100
|
-
} from "./chunk-
|
|
101
|
+
} from "./chunk-FP26KSBM.js";
|
|
101
102
|
import {
|
|
102
103
|
inferSourceDriverName
|
|
103
|
-
} from "./chunk-
|
|
104
|
+
} from "./chunk-U2GRG4UI.js";
|
|
104
105
|
import {
|
|
105
106
|
hashPath,
|
|
106
107
|
isAlreadyExists,
|
|
@@ -109,14 +110,14 @@ import {
|
|
|
109
110
|
pathExists,
|
|
110
111
|
withFilesystemLock,
|
|
111
112
|
writeJsonAtomic
|
|
112
|
-
} from "./chunk-
|
|
113
|
+
} from "./chunk-GE7F76AC.js";
|
|
113
114
|
|
|
114
115
|
// src/cli/index.ts
|
|
115
|
-
import { createHash as
|
|
116
|
+
import { createHash as createHash15 } from "crypto";
|
|
116
117
|
import { existsSync } from "fs";
|
|
117
|
-
import { mkdir as mkdir16, rm as rm10, writeFile as writeFile13 } from "fs/promises";
|
|
118
|
-
import { homedir as homedir12 } from "os";
|
|
119
|
-
import { dirname as
|
|
118
|
+
import { mkdir as mkdir16, mkdtemp as mkdtemp4, realpath as realpath2, rm as rm10, writeFile as writeFile13 } from "fs/promises";
|
|
119
|
+
import { homedir as homedir12, tmpdir as tmpdir5 } from "os";
|
|
120
|
+
import { basename as basename21, dirname as dirname23, isAbsolute as isAbsolute4, join as join46, relative as relative10, resolve as resolve26 } from "path";
|
|
120
121
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
121
122
|
import { Command } from "commander";
|
|
122
123
|
|
|
@@ -3112,14 +3113,14 @@ async function writeMermaidAsset(response) {
|
|
|
3112
3113
|
"content-type": "application/javascript; charset=utf-8",
|
|
3113
3114
|
"cache-control": "public, max-age=3600"
|
|
3114
3115
|
});
|
|
3115
|
-
await new Promise((
|
|
3116
|
+
await new Promise((resolve27) => {
|
|
3116
3117
|
const stream = createReadStream(assetPath);
|
|
3117
3118
|
stream.on("error", () => {
|
|
3118
3119
|
if (!response.headersSent) writeText(response, 404, "Mermaid asset unavailable.\n");
|
|
3119
3120
|
else response.destroy();
|
|
3120
|
-
|
|
3121
|
+
resolve27();
|
|
3121
3122
|
});
|
|
3122
|
-
stream.on("end",
|
|
3123
|
+
stream.on("end", resolve27);
|
|
3123
3124
|
stream.pipe(response);
|
|
3124
3125
|
});
|
|
3125
3126
|
}
|
|
@@ -3146,14 +3147,14 @@ function escapeHtml2(value) {
|
|
|
3146
3147
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
3147
3148
|
}
|
|
3148
3149
|
function listen(server, port, bind) {
|
|
3149
|
-
return new Promise((
|
|
3150
|
+
return new Promise((resolve27, reject) => {
|
|
3150
3151
|
const onError = (error) => {
|
|
3151
3152
|
server.off("listening", onListening);
|
|
3152
3153
|
reject(error);
|
|
3153
3154
|
};
|
|
3154
3155
|
const onListening = () => {
|
|
3155
3156
|
server.off("error", onError);
|
|
3156
|
-
|
|
3157
|
+
resolve27();
|
|
3157
3158
|
};
|
|
3158
3159
|
server.once("error", onError);
|
|
3159
3160
|
server.once("listening", onListening);
|
|
@@ -3161,7 +3162,7 @@ function listen(server, port, bind) {
|
|
|
3161
3162
|
});
|
|
3162
3163
|
}
|
|
3163
3164
|
function waitForShutdown(server, beforeClose) {
|
|
3164
|
-
return new Promise((
|
|
3165
|
+
return new Promise((resolve27) => {
|
|
3165
3166
|
let closing = false;
|
|
3166
3167
|
const shutdown = () => {
|
|
3167
3168
|
if (closing) return;
|
|
@@ -3169,7 +3170,7 @@ function waitForShutdown(server, beforeClose) {
|
|
|
3169
3170
|
process.off("SIGINT", shutdown);
|
|
3170
3171
|
process.off("SIGTERM", shutdown);
|
|
3171
3172
|
beforeClose();
|
|
3172
|
-
server.close(() =>
|
|
3173
|
+
server.close(() => resolve27());
|
|
3173
3174
|
};
|
|
3174
3175
|
process.once("SIGINT", shutdown);
|
|
3175
3176
|
process.once("SIGTERM", shutdown);
|
|
@@ -3179,7 +3180,7 @@ function waitForShutdown(server, beforeClose) {
|
|
|
3179
3180
|
process.off("SIGINT", shutdown);
|
|
3180
3181
|
process.off("SIGTERM", shutdown);
|
|
3181
3182
|
beforeClose();
|
|
3182
|
-
|
|
3183
|
+
resolve27();
|
|
3183
3184
|
});
|
|
3184
3185
|
});
|
|
3185
3186
|
}
|
|
@@ -3732,7 +3733,7 @@ async function fetchClawHubPackage(fetchImpl, packageName) {
|
|
|
3732
3733
|
throw new Error(`ClawHub lookup failed for ${packageName}`);
|
|
3733
3734
|
}
|
|
3734
3735
|
async function delay(ms) {
|
|
3735
|
-
await new Promise((
|
|
3736
|
+
await new Promise((resolve27) => setTimeout(resolve27, ms));
|
|
3736
3737
|
}
|
|
3737
3738
|
function parseClawHubSource(source) {
|
|
3738
3739
|
if (!source.startsWith(sourcePrefix)) {
|
|
@@ -3909,7 +3910,7 @@ async function withGitCacheMaintenanceLock(cacheRoot, timeoutMs, fn) {
|
|
|
3909
3910
|
if (Date.now() - started > timeoutMs) {
|
|
3910
3911
|
throw new Error(`Timed out waiting for git cache maintenance lock at ${lockPath}`);
|
|
3911
3912
|
}
|
|
3912
|
-
await new Promise((
|
|
3913
|
+
await new Promise((resolve27) => setTimeout(resolve27, 50));
|
|
3913
3914
|
}
|
|
3914
3915
|
}
|
|
3915
3916
|
try {
|
|
@@ -5788,7 +5789,7 @@ var RegistryClient = class {
|
|
|
5788
5789
|
}
|
|
5789
5790
|
};
|
|
5790
5791
|
async function resolvePackageSource(source, workspaceRoot, options = {}) {
|
|
5791
|
-
const { isExplicitSource } = await import("./identify-
|
|
5792
|
+
const { isExplicitSource } = await import("./identify-O54QXPQR.js");
|
|
5792
5793
|
if (await isExplicitSource(source)) return { source };
|
|
5793
5794
|
const entry = await new RegistryClient({ workspaceRoot, offline: options.offline, warn: options.warn }).resolve(source);
|
|
5794
5795
|
if (!entry) {
|
|
@@ -6775,8 +6776,8 @@ function verifyIntegrity(integrity, sourceHash, label) {
|
|
|
6775
6776
|
async function withCachePathLock(path, fn) {
|
|
6776
6777
|
const previous = cacheLocks.get(path) ?? Promise.resolve();
|
|
6777
6778
|
let release = () => void 0;
|
|
6778
|
-
const current = previous.then(() => new Promise((
|
|
6779
|
-
release =
|
|
6779
|
+
const current = previous.then(() => new Promise((resolve27) => {
|
|
6780
|
+
release = resolve27;
|
|
6780
6781
|
}));
|
|
6781
6782
|
cacheLocks.set(path, current);
|
|
6782
6783
|
await previous;
|
|
@@ -8141,6 +8142,9 @@ async function createGraphSourcePlan(options) {
|
|
|
8141
8142
|
if (options.roots.length === 0) {
|
|
8142
8143
|
throw new Error("At least one source is required for a graph plan.");
|
|
8143
8144
|
}
|
|
8145
|
+
if (options.freshGraphOnly && options.readOnly !== true) {
|
|
8146
|
+
throw new Error("Fresh graph recovery is read-only and cannot be used for install planning.");
|
|
8147
|
+
}
|
|
8144
8148
|
const workspaceRoot = options.workspaceRoot ?? options.targetRoot;
|
|
8145
8149
|
const transport = options.transport ?? localTransport;
|
|
8146
8150
|
const warnings = [];
|
|
@@ -8229,16 +8233,18 @@ async function createGraphSourcePlan(options) {
|
|
|
8229
8233
|
graphLockPath,
|
|
8230
8234
|
stableManifest,
|
|
8231
8235
|
stableLock: stableLock2,
|
|
8232
|
-
transport
|
|
8236
|
+
transport,
|
|
8237
|
+
recoverLegacyState: options.recoverLegacyState,
|
|
8238
|
+
warn
|
|
8233
8239
|
});
|
|
8234
8240
|
return { priorState: priorState2, stableLock: stableLock2, stableSourceLock: stableSourceLock2 };
|
|
8235
8241
|
};
|
|
8236
|
-
const { priorState, stableLock, stableSourceLock } = await readPriorState();
|
|
8242
|
+
const { priorState, stableLock, stableSourceLock } = options.freshGraphOnly ? { priorState: {}, stableLock: void 0, stableSourceLock: void 0 } : await readPriorState();
|
|
8237
8243
|
const previousLock = priorState.graphLock;
|
|
8238
|
-
const registryClient = new RegistryClient({ workspaceRoot, offline: lockMode, offlineLabel: lockLabel, warn });
|
|
8244
|
+
const registryClient = new RegistryClient({ workspaceRoot, cachePath: options.registryCachePath, offline: lockMode, offlineLabel: lockLabel, warn });
|
|
8239
8245
|
const graph = await resolveDependencyGraph(options.roots, {
|
|
8240
8246
|
workspaceRoot,
|
|
8241
|
-
cacheRoot: join33(workspaceRoot, ".agentwheel", "cache"),
|
|
8247
|
+
cacheRoot: options.cacheRoot ?? join33(workspaceRoot, ".agentwheel", "cache"),
|
|
8242
8248
|
registryClient,
|
|
8243
8249
|
noDeps: options.noDeps,
|
|
8244
8250
|
includeSuggestions: options.includeSuggestions,
|
|
@@ -8491,7 +8497,10 @@ function validateJournalManifestEnvelope(value, options, stateKey, journalPath)
|
|
|
8491
8497
|
return manifest;
|
|
8492
8498
|
}
|
|
8493
8499
|
async function resolvePriorTargetState(options) {
|
|
8494
|
-
const candidates = await discoverLegacyStateCandidates(
|
|
8500
|
+
const candidates = await discoverLegacyStateCandidates({
|
|
8501
|
+
...options,
|
|
8502
|
+
recoverLegacyState: options.recoverLegacyState === true || options.stableManifest !== void 0
|
|
8503
|
+
});
|
|
8495
8504
|
const correlated = candidates.filter((candidate) => candidate.manifest);
|
|
8496
8505
|
if (options.stableManifest) {
|
|
8497
8506
|
for (const candidate of correlated) {
|
|
@@ -8559,34 +8568,28 @@ async function discoverLegacyStateCandidates(options) {
|
|
|
8559
8568
|
const fingerprint = name.slice(0, -suffix.length);
|
|
8560
8569
|
if (!/^[a-f0-9]{64}$/u.test(fingerprint)) continue;
|
|
8561
8570
|
const graphLockPath = join33(directory, name);
|
|
8562
|
-
let graphLock;
|
|
8563
8571
|
try {
|
|
8572
|
+
let graphLock;
|
|
8564
8573
|
graphLock = await readGraphLock(graphLockPath);
|
|
8565
|
-
|
|
8566
|
-
|
|
8567
|
-
|
|
8568
|
-
|
|
8569
|
-
|
|
8570
|
-
|
|
8571
|
-
|
|
8572
|
-
|
|
8573
|
-
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
|
|
8577
|
-
|
|
8578
|
-
|
|
8579
|
-
|
|
8580
|
-
|
|
8581
|
-
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
targetFingerprint: fingerprint,
|
|
8585
|
-
fleetId: options.fleetId
|
|
8586
|
-
});
|
|
8587
|
-
let manifest;
|
|
8588
|
-
let sourceLock;
|
|
8589
|
-
try {
|
|
8574
|
+
if (graphLock.canonical.targetFingerprint !== fingerprint) {
|
|
8575
|
+
throw new Error(
|
|
8576
|
+
`Legacy graph lock fingerprint does not match its filename at ${graphLockPath}.`
|
|
8577
|
+
);
|
|
8578
|
+
}
|
|
8579
|
+
if (options.transport.kind === "ssh") {
|
|
8580
|
+
throw new Error(
|
|
8581
|
+
`Legacy SSH target state at ${graphLockPath} does not record its original endpoint; Agentwheel cannot prove endpoint identity, so the graph lock was preserved.`
|
|
8582
|
+
);
|
|
8583
|
+
}
|
|
8584
|
+
const graphLockDigest = digestGraphLock(graphLock);
|
|
8585
|
+
const candidateStateKey = stateKeyFor(options.adapter, {
|
|
8586
|
+
installationType: options.installationType,
|
|
8587
|
+
stateKey: options.explicitStateKey,
|
|
8588
|
+
targetFingerprint: fingerprint,
|
|
8589
|
+
fleetId: options.fleetId
|
|
8590
|
+
});
|
|
8591
|
+
let manifest;
|
|
8592
|
+
let sourceLock;
|
|
8590
8593
|
manifest = await readInstallManifest(options.installRoot, options.adapter, options.transport, {
|
|
8591
8594
|
installationType: options.installationType,
|
|
8592
8595
|
stateKey: candidateStateKey
|
|
@@ -8595,32 +8598,32 @@ async function discoverLegacyStateCandidates(options) {
|
|
|
8595
8598
|
installationType: options.installationType,
|
|
8596
8599
|
stateKey: candidateStateKey
|
|
8597
8600
|
});
|
|
8598
|
-
|
|
8599
|
-
|
|
8600
|
-
|
|
8601
|
-
|
|
8602
|
-
|
|
8603
|
-
|
|
8604
|
-
if (sourceLock) {
|
|
8605
|
-
throw new Error(`Legacy source lock for ${graphLockPath} has no correlated install manifest.`);
|
|
8601
|
+
if (!manifest) {
|
|
8602
|
+
if (sourceLock) {
|
|
8603
|
+
throw new Error(`Legacy source lock for ${graphLockPath} has no correlated install manifest.`);
|
|
8604
|
+
}
|
|
8605
|
+
candidates.push({ stateKey: candidateStateKey, graphLockPath, graphLockDigest, graphLock });
|
|
8606
|
+
continue;
|
|
8606
8607
|
}
|
|
8607
|
-
|
|
8608
|
-
|
|
8608
|
+
const validated = validateLegacyManifestCandidate(manifest, {
|
|
8609
|
+
...options,
|
|
8610
|
+
candidateStateKey,
|
|
8611
|
+
graphLockDigest,
|
|
8612
|
+
graphLockPath
|
|
8613
|
+
});
|
|
8614
|
+
candidates.push({
|
|
8615
|
+
stateKey: candidateStateKey,
|
|
8616
|
+
graphLockPath,
|
|
8617
|
+
graphLockDigest,
|
|
8618
|
+
graphLock,
|
|
8619
|
+
manifest: validated,
|
|
8620
|
+
sourceLock
|
|
8621
|
+
});
|
|
8622
|
+
} catch (error) {
|
|
8623
|
+
const message = `Legacy target state for ${graphLockPath} is invalid: ` + (error instanceof Error ? error.message : String(error));
|
|
8624
|
+
if (!options.recoverLegacyState) throw new Error(message);
|
|
8625
|
+
options.warn?.(`${message} Preserving it outside the recovered stable state.`);
|
|
8609
8626
|
}
|
|
8610
|
-
const validated = validateLegacyManifestCandidate(manifest, {
|
|
8611
|
-
...options,
|
|
8612
|
-
candidateStateKey,
|
|
8613
|
-
graphLockDigest,
|
|
8614
|
-
graphLockPath
|
|
8615
|
-
});
|
|
8616
|
-
candidates.push({
|
|
8617
|
-
stateKey: candidateStateKey,
|
|
8618
|
-
graphLockPath,
|
|
8619
|
-
graphLockDigest,
|
|
8620
|
-
graphLock,
|
|
8621
|
-
manifest: validated,
|
|
8622
|
-
sourceLock
|
|
8623
|
-
});
|
|
8624
8627
|
}
|
|
8625
8628
|
return candidates;
|
|
8626
8629
|
}
|
|
@@ -10149,10 +10152,262 @@ function canonicalJson(value) {
|
|
|
10149
10152
|
return `{${Object.keys(record).filter((key) => record[key] !== void 0).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
|
|
10150
10153
|
}
|
|
10151
10154
|
|
|
10155
|
+
// src/lifecycle/legacy-recovery-plan.ts
|
|
10156
|
+
import { createHash as createHash10 } from "crypto";
|
|
10157
|
+
import { readdir as readdir10 } from "fs/promises";
|
|
10158
|
+
import { basename as basename20, dirname as dirname21, join as join39, posix as posix3, relative as relative8, resolve as resolve22 } from "path";
|
|
10159
|
+
async function inventoryHistoricalLocks(fleetRoot) {
|
|
10160
|
+
const root = join39(fleetRoot, ".agentwheel", "locks");
|
|
10161
|
+
const found = [];
|
|
10162
|
+
async function visit(dir) {
|
|
10163
|
+
for (const entry of await readdir10(dir, { withFileTypes: true }).catch((error) => {
|
|
10164
|
+
if (error.code === "ENOENT") return [];
|
|
10165
|
+
throw error;
|
|
10166
|
+
})) {
|
|
10167
|
+
const path = join39(dir, entry.name);
|
|
10168
|
+
if (entry.isDirectory()) await visit(path);
|
|
10169
|
+
else if (entry.isFile() && entry.name.endsWith(".graph-lock.json")) {
|
|
10170
|
+
const lock = await readGraphLock(path);
|
|
10171
|
+
found.push({ path, digest: sha2564(canonicalGraphLockJson(lock)), lock });
|
|
10172
|
+
}
|
|
10173
|
+
}
|
|
10174
|
+
}
|
|
10175
|
+
await visit(root);
|
|
10176
|
+
return found.sort((a, b) => a.path.localeCompare(b.path));
|
|
10177
|
+
}
|
|
10178
|
+
async function planLegacyRecovery(input) {
|
|
10179
|
+
const { graphPlan, installRoot, targetKey: targetKey2, adapter, installationType, historicalLocks, transport } = input;
|
|
10180
|
+
assertCompleteGraphPlan(graphPlan);
|
|
10181
|
+
const binding = {
|
|
10182
|
+
targetLockDir: dirname21(graphPlan.graphLockPath),
|
|
10183
|
+
stableLockPath: graphPlan.graphLockPath,
|
|
10184
|
+
stableStateKey: graphPlan.plan.stateKey,
|
|
10185
|
+
currentFingerprint: graphPlan.targetFingerprint,
|
|
10186
|
+
expectedOwner: workspaceOwnerForRoot(input.workspaceRoot, input.fleetId),
|
|
10187
|
+
installRoot,
|
|
10188
|
+
adapter,
|
|
10189
|
+
installationType,
|
|
10190
|
+
fleetId: input.fleetId,
|
|
10191
|
+
explicitStateKey: input.explicitStateKey,
|
|
10192
|
+
transportKind: transport.kind
|
|
10193
|
+
};
|
|
10194
|
+
const manifests = await listInstallManifests(installRoot, adapter, transport);
|
|
10195
|
+
const paths = /* @__PURE__ */ new Set();
|
|
10196
|
+
const desired = /* @__PURE__ */ new Map();
|
|
10197
|
+
const nodeHashes = new Map(graphPlan.bundle.graphLock.canonical.nodes.map((node) => [node.id, node.sourceHash]));
|
|
10198
|
+
for (const op of graphPlan.plan.operations) {
|
|
10199
|
+
if (!op.desiredHash || op.action === "remove" || op.action === "keep") continue;
|
|
10200
|
+
const path = relative8(installRoot, op.destPath);
|
|
10201
|
+
if (path.startsWith("..") || path === "") throw new Error(`Planner destination escapes target: ${op.destPath}`);
|
|
10202
|
+
paths.add(path);
|
|
10203
|
+
const items = desired.get(path) ?? [];
|
|
10204
|
+
items.push(op);
|
|
10205
|
+
desired.set(path, items);
|
|
10206
|
+
}
|
|
10207
|
+
for (const item of manifests) for (const entry of item.manifest.entries) paths.add(entry.path);
|
|
10208
|
+
const resolutionWarnings = graphPlan.warnings.filter((warning) => !/^skip (?:artifact|dependency) .+ \((?:selected but )?not targeted: runtimes=\[[^\]]+\]\)$/.test(warning));
|
|
10209
|
+
const complete = resolutionWarnings.length === 0;
|
|
10210
|
+
const output = [];
|
|
10211
|
+
for (const path of [...paths].sort()) {
|
|
10212
|
+
const allClaims = manifests.flatMap((item) => item.manifest.entries.filter((entry) => entry.path === path).map((entry) => claimFor(item, entry, historicalLocks, binding)));
|
|
10213
|
+
const claims = allClaims.filter((claim) => claim.scope !== "foreign");
|
|
10214
|
+
const foreignClaims = allClaims.filter((claim) => claim.scope === "foreign");
|
|
10215
|
+
const current = desired.get(path) ?? [];
|
|
10216
|
+
const currentHashes = [...new Set(current.map((item) => item.desiredHash))];
|
|
10217
|
+
const currentSourceHashes = [...new Set(current.map((item) => item.graphNodeId ? nodeHashes.get(item.graphNodeId) : void 0).filter((value) => Boolean(value)))].sort();
|
|
10218
|
+
const currentNodeIds = [...new Set(current.map((item) => item.graphNodeId).filter((value) => Boolean(value)))].sort();
|
|
10219
|
+
const currentSelectors = [...new Set(current.map((item) => item.logicalSelector).filter((value) => Boolean(value)))].sort();
|
|
10220
|
+
const currentOwners = [...new Set(current.flatMap((item) => item.owners ?? []))].sort();
|
|
10221
|
+
const livePath = resolve22(installRoot, path);
|
|
10222
|
+
if (relative8(installRoot, livePath).startsWith("..")) throw new Error(`Manifest path escapes target: ${path}`);
|
|
10223
|
+
const liveHash = await transport.pathExists(livePath) ? await transport.hashPath(livePath) : null;
|
|
10224
|
+
const reasons = [];
|
|
10225
|
+
const currentResults = await Promise.all(current.map((op) => verifyCurrentOperation(op, liveHash, transport)));
|
|
10226
|
+
const currentGraphStatus = current.length === 0 ? "absent" : currentResults.every((result) => result === "exact-match") ? "exact-match" : currentResults.some((result) => result === "unverifiable") ? "unverifiable" : "mismatch";
|
|
10227
|
+
if (currentHashes.length > 1 && current.some((op) => !op.mergeStrategy && op.mode !== "managed-block")) {
|
|
10228
|
+
reasons.push("current graph has divergent hashes for the destination");
|
|
10229
|
+
}
|
|
10230
|
+
if (currentGraphStatus === "mismatch") reasons.push("live contribution differs from current source");
|
|
10231
|
+
if (currentGraphStatus === "unverifiable") reasons.push("current contribution could not be verified");
|
|
10232
|
+
if (current.some((op) => op.action === "conflict" || op.action === "drift")) reasons.push("install planner has a blocking operation");
|
|
10233
|
+
if (!complete) reasons.push("current source graph has resolution warnings; completeness unproven");
|
|
10234
|
+
if (claims.some((claim) => claim.scope === "unbound" || claim.evidence.length)) reasons.push("historical provenance unresolved");
|
|
10235
|
+
if (foreignClaims.length) reasons.push("foreign manifest claim conflicts with destination");
|
|
10236
|
+
if (new Set(claims.map((claim) => `${claim.owner}:${claim.hash}:${claim.sourceHash}:${claim.graphLockDigest ?? ""}`)).size > 1) {
|
|
10237
|
+
reasons.push("competing same-target claims diverge");
|
|
10238
|
+
}
|
|
10239
|
+
if (current.length === 0 && claims.length === 0) reasons.push("no same-target historical claim");
|
|
10240
|
+
if (current.length === 0) {
|
|
10241
|
+
for (const claim of claims) {
|
|
10242
|
+
if (claim.scope !== "same-target") continue;
|
|
10243
|
+
const item = manifests.find((manifest) => manifest.path === claim.manifestPath);
|
|
10244
|
+
const entry = item.manifest.entries.find((candidate) => candidate.path === path && candidate.sourceHash === claim.sourceHash);
|
|
10245
|
+
if (!await verifyHistoricalEntry(entry, livePath, liveHash, transport)) {
|
|
10246
|
+
reasons.push("live contribution differs from same-target historical claim");
|
|
10247
|
+
break;
|
|
10248
|
+
}
|
|
10249
|
+
}
|
|
10250
|
+
}
|
|
10251
|
+
const category = reasons.length ? "unresolved" : currentGraphStatus === "exact-match" ? "current-graph-exact-match" : "retirement-candidate";
|
|
10252
|
+
output.push({
|
|
10253
|
+
path,
|
|
10254
|
+
category,
|
|
10255
|
+
reasons,
|
|
10256
|
+
currentHash: currentHashes.length === 1 ? currentHashes[0] : null,
|
|
10257
|
+
currentHashes: currentHashes.sort(),
|
|
10258
|
+
currentSourceHashes,
|
|
10259
|
+
currentNodeIds,
|
|
10260
|
+
currentSelectors,
|
|
10261
|
+
currentOwners,
|
|
10262
|
+
currentGraphStatus,
|
|
10263
|
+
liveHash,
|
|
10264
|
+
claims,
|
|
10265
|
+
foreignClaims
|
|
10266
|
+
});
|
|
10267
|
+
}
|
|
10268
|
+
const counts = { "current-graph-exact-match": 0, "retirement-candidate": 0, unresolved: 0 };
|
|
10269
|
+
const currentGraphCounts = { "exact-match": 0, mismatch: 0, absent: 0, unverifiable: 0 };
|
|
10270
|
+
for (const path of output) counts[path.category]++;
|
|
10271
|
+
for (const path of output) currentGraphCounts[path.currentGraphStatus]++;
|
|
10272
|
+
return {
|
|
10273
|
+
targetRoot: installRoot,
|
|
10274
|
+
targetKey: targetKey2,
|
|
10275
|
+
adapter,
|
|
10276
|
+
installationType,
|
|
10277
|
+
currentGraphLockDigest: graphPlan.graphLockDigest,
|
|
10278
|
+
roots: graphPlan.graph.roots.map((root) => ({ rootId: root.rootId, source: root.source, graphNodeId: root.graphNodeId, selected: root.selected })),
|
|
10279
|
+
resolutionWarnings,
|
|
10280
|
+
complete,
|
|
10281
|
+
paths: output,
|
|
10282
|
+
counts,
|
|
10283
|
+
currentGraphCounts
|
|
10284
|
+
};
|
|
10285
|
+
}
|
|
10286
|
+
function claimFor(item, entry, locks, binding) {
|
|
10287
|
+
const digest2 = "graphLockDigest" in entry ? entry.graphLockDigest : void 0;
|
|
10288
|
+
const nodeId = "graphNodeId" in entry ? entry.graphNodeId : void 0;
|
|
10289
|
+
const selector = "logicalSelector" in entry ? entry.logicalSelector : void 0;
|
|
10290
|
+
const matches = locks.filter((candidate) => candidate.digest === digest2);
|
|
10291
|
+
const evidence = [];
|
|
10292
|
+
const owner = "workspaceOwner" in entry ? entry.workspaceOwner : legacyUnownedWorkspaceOwner;
|
|
10293
|
+
const sameRoot = binding.transportKind === "ssh" ? posix3.normalize(item.manifest.targetRoot) === posix3.normalize(binding.installRoot) : resolve22(item.manifest.targetRoot) === resolve22(binding.installRoot);
|
|
10294
|
+
const inTargetDir = matches.filter((candidate) => resolve22(dirname21(candidate.path)) === resolve22(binding.targetLockDir));
|
|
10295
|
+
const boundLocks = inTargetDir.filter((candidate) => lockBindsState(candidate, item, binding));
|
|
10296
|
+
if (owner !== binding.expectedOwner) evidence.push("workspace owner differs from selected Fleet target");
|
|
10297
|
+
if (item.manifest.version !== 2 || item.manifest.installationType !== binding.installationType) evidence.push("installation type differs or manifest is legacy v1");
|
|
10298
|
+
if (!sameRoot) evidence.push("manifest target root differs from selected Fleet target");
|
|
10299
|
+
if (item.manifest.version === 2 && (item.manifest.stateKey !== item.stateKey || item.manifest.adapter !== binding.adapter)) {
|
|
10300
|
+
evidence.push("manifest state key or adapter differs from filename");
|
|
10301
|
+
}
|
|
10302
|
+
if (!digest2) evidence.push("historical graph-lock digest missing");
|
|
10303
|
+
else if (matches.length === 0) evidence.push("historical graph-lock digest has no matching lock");
|
|
10304
|
+
if (matches.length && !inTargetDir.length) evidence.push("claim belongs to another Fleet target graph");
|
|
10305
|
+
if (inTargetDir.length && !boundLocks.length) evidence.push("graph-lock fingerprint, filename, or state key does not bind claim");
|
|
10306
|
+
if (boundLocks.length && !boundLocks.some((candidate) => candidate.lock.canonical.artifacts.some((artifact) => artifact.graphNodeId === nodeId && artifact.logicalSelector === selector))) {
|
|
10307
|
+
evidence.push("historical graph-lock selector/node does not cover claim");
|
|
10308
|
+
} else if (boundLocks.length && !boundLocks.some((candidate) => candidate.lock.canonical.artifacts.some((artifact) => artifact.graphNodeId === nodeId && artifact.logicalSelector === selector && artifact.hash === entry.sourceHash))) {
|
|
10309
|
+
evidence.push("historical graph-lock artifact hash differs from manifest source hash");
|
|
10310
|
+
}
|
|
10311
|
+
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;
|
|
10312
|
+
const scope = foreign ? "foreign" : boundLocks.length && evidence.length === 0 ? "same-target" : "unbound";
|
|
10313
|
+
return {
|
|
10314
|
+
scope,
|
|
10315
|
+
manifestPath: item.path,
|
|
10316
|
+
stateKey: item.stateKey,
|
|
10317
|
+
manifestStateKey: item.manifest.version === 2 ? item.manifest.stateKey : void 0,
|
|
10318
|
+
installationType: item.manifest.version === 2 ? item.manifest.installationType : void 0,
|
|
10319
|
+
targetRoot: item.manifest.targetRoot,
|
|
10320
|
+
owner,
|
|
10321
|
+
hash: entry.hash,
|
|
10322
|
+
sourceHash: entry.sourceHash,
|
|
10323
|
+
graphLockDigest: digest2,
|
|
10324
|
+
graphNodeId: nodeId,
|
|
10325
|
+
logicalSelector: selector,
|
|
10326
|
+
owners: "owners" in entry ? entry.owners : [entry.packageName ?? "legacy"],
|
|
10327
|
+
lockPaths: matches.map((candidate) => candidate.path),
|
|
10328
|
+
evidence
|
|
10329
|
+
};
|
|
10330
|
+
}
|
|
10331
|
+
function lockBindsState(candidate, manifest, binding) {
|
|
10332
|
+
const name = basename20(candidate.path);
|
|
10333
|
+
if (!name.endsWith(".graph-lock.json")) return false;
|
|
10334
|
+
const fingerprint = name.slice(0, -".graph-lock.json".length);
|
|
10335
|
+
if (!/^[a-f0-9]{64}$/u.test(fingerprint)) return false;
|
|
10336
|
+
const stable = resolve22(candidate.path) === resolve22(binding.stableLockPath);
|
|
10337
|
+
if (stable) {
|
|
10338
|
+
return manifest.stateKey === binding.stableStateKey && candidate.lock.canonical.targetFingerprint === binding.currentFingerprint;
|
|
10339
|
+
}
|
|
10340
|
+
return candidate.lock.canonical.targetFingerprint === fingerprint && manifest.stateKey === stateKeyFor(binding.adapter, {
|
|
10341
|
+
installationType: binding.installationType,
|
|
10342
|
+
stateKey: binding.explicitStateKey,
|
|
10343
|
+
targetFingerprint: fingerprint,
|
|
10344
|
+
fleetId: binding.fleetId
|
|
10345
|
+
});
|
|
10346
|
+
}
|
|
10347
|
+
async function verifyCurrentOperation(op, liveHash, transport) {
|
|
10348
|
+
if (liveHash === null || !op.desiredHash) return "mismatch";
|
|
10349
|
+
if (op.semanticPlugin || op.programmaticOperation) return "unverifiable";
|
|
10350
|
+
if (op.mode === "managed-block") {
|
|
10351
|
+
const selector = managedInstructionSelector(op.logicalSelector, op.artifactType, op.artifactName);
|
|
10352
|
+
const state = await readManagedInstructionBlockState(op.destPath, selector, transport);
|
|
10353
|
+
return state.hasBlock && !state.drifted && state.hash === op.desiredHash ? "exact-match" : "mismatch";
|
|
10354
|
+
}
|
|
10355
|
+
if (op.mergeStrategy) {
|
|
10356
|
+
if (!op.sourcePath) return "unverifiable";
|
|
10357
|
+
try {
|
|
10358
|
+
await assertMergedSourceContribution(op.sourcePath, op.mergeStrategy, await transport.readFile(op.destPath));
|
|
10359
|
+
return "exact-match";
|
|
10360
|
+
} catch {
|
|
10361
|
+
return "mismatch";
|
|
10362
|
+
}
|
|
10363
|
+
}
|
|
10364
|
+
return liveHash === op.desiredHash ? "exact-match" : "mismatch";
|
|
10365
|
+
}
|
|
10366
|
+
async function verifyHistoricalEntry(entry, livePath, liveHash, transport) {
|
|
10367
|
+
if (liveHash === null || entry.semanticPlugin) return false;
|
|
10368
|
+
if (entry.mode === "managed-block") {
|
|
10369
|
+
const selector = managedInstructionSelector(
|
|
10370
|
+
"logicalSelector" in entry ? entry.logicalSelector : void 0,
|
|
10371
|
+
entry.artifactType,
|
|
10372
|
+
entry.artifactName
|
|
10373
|
+
);
|
|
10374
|
+
const state = await readManagedInstructionBlockState(livePath, selector, transport);
|
|
10375
|
+
return state.hasBlock && !state.drifted && state.hash === entry.hash;
|
|
10376
|
+
}
|
|
10377
|
+
if (entry.mergeStrategy) {
|
|
10378
|
+
if (!hasMergeRemovalContent(entry.mergeRemoval)) return false;
|
|
10379
|
+
try {
|
|
10380
|
+
assertExactMergeContribution(entry.mergeRemoval, entry.mergeStrategy, await transport.readFile(livePath));
|
|
10381
|
+
return true;
|
|
10382
|
+
} catch {
|
|
10383
|
+
return false;
|
|
10384
|
+
}
|
|
10385
|
+
}
|
|
10386
|
+
return entry.hash === liveHash;
|
|
10387
|
+
}
|
|
10388
|
+
function assertCompleteGraphPlan(plan) {
|
|
10389
|
+
const sourceNodes = plan.graph.nodes.filter((node) => node.selected.length > 0);
|
|
10390
|
+
const lockedNodes = new Map(plan.bundle.graphLock.canonical.nodes.map((node) => [node.id, node]));
|
|
10391
|
+
for (const node of sourceNodes) {
|
|
10392
|
+
const locked = lockedNodes.get(node.id);
|
|
10393
|
+
if (!locked || locked.sourceHash !== node.sourceHash || node.selected.some((selector) => !locked.selected.includes(selector))) {
|
|
10394
|
+
throw new Error(`Current source graph omitted selected node or dependency: ${node.id}`);
|
|
10395
|
+
}
|
|
10396
|
+
}
|
|
10397
|
+
for (const edge of plan.graph.edges) {
|
|
10398
|
+
if (!plan.bundle.graphLock.canonical.edges.some((locked) => locked.from === edge.from && locked.to === edge.to && locked.alias === edge.alias)) {
|
|
10399
|
+
throw new Error(`Current source graph omitted dependency edge: ${edge.from} -> ${edge.to}`);
|
|
10400
|
+
}
|
|
10401
|
+
}
|
|
10402
|
+
}
|
|
10403
|
+
function sha2564(value) {
|
|
10404
|
+
return createHash10("sha256").update(value).digest("hex");
|
|
10405
|
+
}
|
|
10406
|
+
|
|
10152
10407
|
// src/version/policy.ts
|
|
10153
10408
|
import { execFile as execFile3 } from "child_process";
|
|
10154
10409
|
import { readFile as readFile23 } from "fs/promises";
|
|
10155
|
-
import { join as
|
|
10410
|
+
import { join as join40, resolve as resolve23 } from "path";
|
|
10156
10411
|
import { promisify as promisify3 } from "util";
|
|
10157
10412
|
import { parse as parseJsonc } from "jsonc-parser";
|
|
10158
10413
|
import { z as z5 } from "zod";
|
|
@@ -10246,7 +10501,7 @@ async function discoverVersionsFromSource(pkg, workspaceRoot) {
|
|
|
10246
10501
|
if (tagged.length > 0) return tagged;
|
|
10247
10502
|
}
|
|
10248
10503
|
if (driverName === "local") {
|
|
10249
|
-
const root =
|
|
10504
|
+
const root = resolve23(workspaceRoot, pkg.source);
|
|
10250
10505
|
const manifest2 = await readPackageManifest(root);
|
|
10251
10506
|
const current = manifest2 ? [{ version: manifest2.version, ref: pkg.requestedRef ?? root }] : [];
|
|
10252
10507
|
try {
|
|
@@ -10261,7 +10516,7 @@ async function discoverVersionsFromSource(pkg, workspaceRoot) {
|
|
|
10261
10516
|
}
|
|
10262
10517
|
const driver = getSourceDriver(driverName);
|
|
10263
10518
|
const resolved = await driver.resolve(pkg.source, {
|
|
10264
|
-
cacheRoot:
|
|
10519
|
+
cacheRoot: join40(workspaceRoot, ".agentwheel", "cache"),
|
|
10265
10520
|
mode: "tracking",
|
|
10266
10521
|
ref: pkg.requestedRef
|
|
10267
10522
|
});
|
|
@@ -10355,7 +10610,7 @@ function gitUrlFromSource(source) {
|
|
|
10355
10610
|
throw new Error(`Version discovery does not support Git source: ${source}`);
|
|
10356
10611
|
}
|
|
10357
10612
|
function versionCachePath(workspaceRoot) {
|
|
10358
|
-
return
|
|
10613
|
+
return join40(workspaceRoot, ".agentwheel", "cache", "version-index.json");
|
|
10359
10614
|
}
|
|
10360
10615
|
async function readVersionCache(path) {
|
|
10361
10616
|
if (!await pathExists(path)) return { schemaVersion: 1, sources: {} };
|
|
@@ -10369,7 +10624,7 @@ async function readVersionCache(path) {
|
|
|
10369
10624
|
// src/profile/members.ts
|
|
10370
10625
|
import { execFile as execFile4 } from "child_process";
|
|
10371
10626
|
import { readFile as readFile24 } from "fs/promises";
|
|
10372
|
-
import { join as
|
|
10627
|
+
import { join as join41, resolve as resolve24 } from "path";
|
|
10373
10628
|
import { promisify as promisify4 } from "util";
|
|
10374
10629
|
import { z as z7 } from "zod";
|
|
10375
10630
|
|
|
@@ -10544,7 +10799,7 @@ async function invokeMemberStatus(member, parentWorkspace, chain, options, cliEn
|
|
|
10544
10799
|
let stderr = "";
|
|
10545
10800
|
try {
|
|
10546
10801
|
if (member.transport === "local") {
|
|
10547
|
-
const workspace =
|
|
10802
|
+
const workspace = resolve24(parentWorkspace, member.workspace);
|
|
10548
10803
|
const result = await execFileAsync4(process.execPath, [cliEntry, ...args], {
|
|
10549
10804
|
cwd: workspace,
|
|
10550
10805
|
env: env2,
|
|
@@ -10590,7 +10845,7 @@ async function runMemberAgentwheel(member, parentWorkspace, args, chain) {
|
|
|
10590
10845
|
process.execPath,
|
|
10591
10846
|
[process.argv[1], ...memberCommandArgs(member, args)],
|
|
10592
10847
|
{
|
|
10593
|
-
cwd:
|
|
10848
|
+
cwd: resolve24(parentWorkspace, member.workspace),
|
|
10594
10849
|
env: env2,
|
|
10595
10850
|
maxBuffer: 20 * 1024 * 1024
|
|
10596
10851
|
}
|
|
@@ -10670,7 +10925,7 @@ function memberFailure(member, health, error) {
|
|
|
10670
10925
|
};
|
|
10671
10926
|
}
|
|
10672
10927
|
function memberCachePath(workspaceRoot, profileName, memberId) {
|
|
10673
|
-
return
|
|
10928
|
+
return join41(workspaceRoot, ".agentwheel", "cache", "member-status", profileName, `${memberId}.json`);
|
|
10674
10929
|
}
|
|
10675
10930
|
async function readMemberCache(path) {
|
|
10676
10931
|
if (!await pathExists(path)) return void 0;
|
|
@@ -10696,7 +10951,7 @@ function assertNoCompositeCycle(workspaceRoot, profileName, chain) {
|
|
|
10696
10951
|
}
|
|
10697
10952
|
}
|
|
10698
10953
|
function compositeKey(workspaceRoot, profileName) {
|
|
10699
|
-
return `${
|
|
10954
|
+
return `${resolve24(workspaceRoot)}#${profileName}`;
|
|
10700
10955
|
}
|
|
10701
10956
|
|
|
10702
10957
|
// src/status/repository.ts
|
|
@@ -10743,10 +10998,10 @@ function valueAfter(lines, prefix) {
|
|
|
10743
10998
|
}
|
|
10744
10999
|
|
|
10745
11000
|
// src/catalogue/client.ts
|
|
10746
|
-
import { createHash as
|
|
11001
|
+
import { createHash as createHash11 } from "crypto";
|
|
10747
11002
|
import { readFile as readFile25, rm as rm8 } from "fs/promises";
|
|
10748
11003
|
import { homedir as homedir9 } from "os";
|
|
10749
|
-
import { join as
|
|
11004
|
+
import { join as join42 } from "path";
|
|
10750
11005
|
|
|
10751
11006
|
// src/model/catalogue.ts
|
|
10752
11007
|
import { z as z8 } from "zod";
|
|
@@ -11018,18 +11273,18 @@ var CatalogueClient = class {
|
|
|
11018
11273
|
}
|
|
11019
11274
|
return {
|
|
11020
11275
|
value: schema.parse(value),
|
|
11021
|
-
digest:
|
|
11276
|
+
digest: createHash11("sha256").update(bytes).digest("hex")
|
|
11022
11277
|
};
|
|
11023
11278
|
}
|
|
11024
11279
|
};
|
|
11025
11280
|
function defaultCatalogueCachePath() {
|
|
11026
|
-
return
|
|
11281
|
+
return join42(homedir9(), ".agentwheel", "catalogue-cache.json");
|
|
11027
11282
|
}
|
|
11028
11283
|
function sameSources2(a, b) {
|
|
11029
11284
|
return a.length === b.length && a.every((source, index) => source === b[index]);
|
|
11030
11285
|
}
|
|
11031
11286
|
function catalogueContentHash(enriched, vercel) {
|
|
11032
|
-
return
|
|
11287
|
+
return createHash11("sha256").update(JSON.stringify({ enriched, vercel })).digest("hex");
|
|
11033
11288
|
}
|
|
11034
11289
|
|
|
11035
11290
|
// src/search/index.ts
|
|
@@ -11391,9 +11646,9 @@ function includesToken(normalizedText, token) {
|
|
|
11391
11646
|
}
|
|
11392
11647
|
|
|
11393
11648
|
// src/semantic/index.ts
|
|
11394
|
-
import { createHash as
|
|
11649
|
+
import { createHash as createHash12 } from "crypto";
|
|
11395
11650
|
import { homedir as homedir10 } from "os";
|
|
11396
|
-
import { join as
|
|
11651
|
+
import { join as join43 } from "path";
|
|
11397
11652
|
import { env, pipeline } from "@huggingface/transformers";
|
|
11398
11653
|
var DEFAULT_SEMANTIC_INDEX_URL = "https://raw.githubusercontent.com/NestDevLab/agentwheel-registry/main/catalogue-semantic-index/gte-v1/";
|
|
11399
11654
|
var CONTRACT = {
|
|
@@ -11481,13 +11736,13 @@ var SemanticSearchClient = class {
|
|
|
11481
11736
|
if (!response.ok) throw new Error(`Semantic index file failed (${response.status}): ${descriptor.path}`);
|
|
11482
11737
|
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
11483
11738
|
if (bytes.byteLength !== descriptor.bytes) throw new Error(`Semantic index file size does not match: ${descriptor.path}`);
|
|
11484
|
-
const digest2 =
|
|
11739
|
+
const digest2 = createHash12("sha256").update(bytes).digest("hex");
|
|
11485
11740
|
if (digest2 !== descriptor.sha256) throw new Error(`Semantic index checksum does not match: ${descriptor.path}`);
|
|
11486
11741
|
return bytes;
|
|
11487
11742
|
}
|
|
11488
11743
|
};
|
|
11489
11744
|
async function embedQuery(query) {
|
|
11490
|
-
env.cacheDir =
|
|
11745
|
+
env.cacheDir = join43(homedir10(), ".agentwheel", "semantic-models");
|
|
11491
11746
|
const extractor = await pipeline("feature-extraction", CONTRACT.model.id, {
|
|
11492
11747
|
revision: CONTRACT.model.revision,
|
|
11493
11748
|
dtype: CONTRACT.model.dtype,
|
|
@@ -11571,9 +11826,9 @@ function ensureTrailingSlash(value) {
|
|
|
11571
11826
|
}
|
|
11572
11827
|
|
|
11573
11828
|
// src/trial/skill.ts
|
|
11574
|
-
import { createHash as
|
|
11829
|
+
import { createHash as createHash13 } from "crypto";
|
|
11575
11830
|
import { readFile as readFile26, stat as stat13 } from "fs/promises";
|
|
11576
|
-
import { join as
|
|
11831
|
+
import { join as join44 } from "path";
|
|
11577
11832
|
import { parse as parseYaml2 } from "yaml";
|
|
11578
11833
|
var MAX_TRIAL_SKILL_BYTES = 512 * 1024;
|
|
11579
11834
|
async function createSkillTrial(driver, resolved, selectors) {
|
|
@@ -11585,7 +11840,7 @@ async function createSkillTrial(driver, resolved, selectors) {
|
|
|
11585
11840
|
throw new Error("Skill trial requires exactly one selected skill. Use --skill <name> or --select skills/<name>.");
|
|
11586
11841
|
}
|
|
11587
11842
|
const artifact = skills[0];
|
|
11588
|
-
const path = artifact.kind === "dir" ?
|
|
11843
|
+
const path = artifact.kind === "dir" ? join44(artifact.sourcePath, "SKILL.md") : artifact.sourcePath;
|
|
11589
11844
|
const info = await stat13(path);
|
|
11590
11845
|
if (info.size > MAX_TRIAL_SKILL_BYTES) {
|
|
11591
11846
|
throw new Error(`Skill trial exceeds the ${MAX_TRIAL_SKILL_BYTES / 1024} KiB content limit.`);
|
|
@@ -11601,7 +11856,7 @@ async function createSkillTrial(driver, resolved, selectors) {
|
|
|
11601
11856
|
skill: {
|
|
11602
11857
|
name: artifact.name,
|
|
11603
11858
|
relativePath: artifact.relativePath,
|
|
11604
|
-
sha256:
|
|
11859
|
+
sha256: createHash13("sha256").update(content).digest("hex"),
|
|
11605
11860
|
frontmatter,
|
|
11606
11861
|
content
|
|
11607
11862
|
}
|
|
@@ -11620,10 +11875,10 @@ function readSkillFrontmatter(content, artifact) {
|
|
|
11620
11875
|
}
|
|
11621
11876
|
|
|
11622
11877
|
// src/lifecycle/fleet-normalize.ts
|
|
11623
|
-
import { createHash as
|
|
11624
|
-
import { readFile as readFile27, readdir as
|
|
11878
|
+
import { createHash as createHash14 } from "crypto";
|
|
11879
|
+
import { readFile as readFile27, readdir as readdir11, rm as rm9 } from "fs/promises";
|
|
11625
11880
|
import { homedir as homedir11 } from "os";
|
|
11626
|
-
import { dirname as
|
|
11881
|
+
import { dirname as dirname22, isAbsolute as isAbsolute3, join as join45, relative as relative9, resolve as resolve25 } from "path";
|
|
11627
11882
|
async function planFleetNormalization(request) {
|
|
11628
11883
|
const normalizedRequest = normalizeRequest2(request);
|
|
11629
11884
|
const destinationScope = await resolveWorkspaceScope({
|
|
@@ -11653,7 +11908,7 @@ async function planFleetNormalization(request) {
|
|
|
11653
11908
|
`Package '${name}' has divergent declarations. Source, driver, adapter, adapter configuration, installation type, version policy, selections, aliases, overrides, and suggestions must match before normalization.`
|
|
11654
11909
|
);
|
|
11655
11910
|
}
|
|
11656
|
-
packages.push({ name, declarationDigest:
|
|
11911
|
+
packages.push({ name, declarationDigest: sha2565(sourceDeclaration) });
|
|
11657
11912
|
}
|
|
11658
11913
|
if (!legacySelfNormalization) await assertSourceFleetPostcondition(normalizedRequest, sourceScope, candidates);
|
|
11659
11914
|
if ((normalizedRequest.profile || normalizedRequest.agent || normalizedRequest.artifacts || normalizedRequest.orphanedOwnerRoots) && !legacySelfNormalization) {
|
|
@@ -11684,7 +11939,7 @@ async function planFleetNormalization(request) {
|
|
|
11684
11939
|
packages,
|
|
11685
11940
|
installedState
|
|
11686
11941
|
};
|
|
11687
|
-
return { ...planWithoutDigest, planDigest:
|
|
11942
|
+
return { ...planWithoutDigest, planDigest: sha2565(canonicalJson2(planWithoutDigest)) };
|
|
11688
11943
|
}
|
|
11689
11944
|
async function applyFleetNormalization(request, hooks = {}) {
|
|
11690
11945
|
if (!request.apply) throw new Error("Fleet normalization is dry-run by default; pass --apply with --plan-digest <digest>.");
|
|
@@ -11822,7 +12077,7 @@ async function assertNormalizationPostconditions(journal) {
|
|
|
11822
12077
|
}
|
|
11823
12078
|
for (const graphLock of journal.graphLocks) {
|
|
11824
12079
|
const raw = await readOptionalRecord(graphLock.path);
|
|
11825
|
-
const digest2 = raw ?
|
|
12080
|
+
const digest2 = raw ? sha2565(canonicalJson2(await readGraphLock(graphLock.path))) : null;
|
|
11826
12081
|
if (digest2 !== graphLock.afterDigest) {
|
|
11827
12082
|
throw new Error(`Fleet normalization graph-lock postcheck failed: ${graphLock.path}`);
|
|
11828
12083
|
}
|
|
@@ -11894,17 +12149,17 @@ function normalizeRequest2(request) {
|
|
|
11894
12149
|
...profile ? { profile } : {},
|
|
11895
12150
|
...agent ? { agent } : {},
|
|
11896
12151
|
...orphanedOwnerRoots ? { orphanedOwnerRoots } : {},
|
|
11897
|
-
...request.globalRoot ? { globalRoot:
|
|
12152
|
+
...request.globalRoot ? { globalRoot: resolve25(request.globalRoot) } : {}
|
|
11898
12153
|
};
|
|
11899
12154
|
}
|
|
11900
12155
|
function normalizeOrphanedOwnerRoot(value) {
|
|
11901
12156
|
const root = value.trim();
|
|
11902
12157
|
if (!root || !isAbsolute3(root)) throw new Error(`Invalid orphaned owner root '${value}'; expected an absolute workspace path.`);
|
|
11903
|
-
return
|
|
12158
|
+
return resolve25(root);
|
|
11904
12159
|
}
|
|
11905
12160
|
async function normalizationRoots(request) {
|
|
11906
12161
|
const destination = await showRegisteredFleet(request.destinationFleet, { globalRoot: request.globalRoot });
|
|
11907
|
-
const source = request.from === "user" ?
|
|
12162
|
+
const source = request.from === "user" ? resolve25(request.globalRoot ?? homedir11()) : (await showRegisteredFleet(request.from.slice("fleet:".length), { globalRoot: request.globalRoot })).root;
|
|
11908
12163
|
return { source, destination: destination.root };
|
|
11909
12164
|
}
|
|
11910
12165
|
async function withOrderedLocks(roots, fn) {
|
|
@@ -11912,7 +12167,7 @@ async function withOrderedLocks(roots, fn) {
|
|
|
11912
12167
|
if (index === roots.length) return fn();
|
|
11913
12168
|
const root = roots[index];
|
|
11914
12169
|
return withFilesystemLock(
|
|
11915
|
-
|
|
12170
|
+
join45(root, ".agentwheel", "fleet-normalize.lock"),
|
|
11916
12171
|
5e3,
|
|
11917
12172
|
() => run(index + 1),
|
|
11918
12173
|
"fleet normalization"
|
|
@@ -12011,10 +12266,10 @@ async function inspectInstalledState(source, destination, packageNames) {
|
|
|
12011
12266
|
const destinationGraphAfter = destinationGraph?.lock ?? destinationGraphFromSource(sourceGraph.lock, destinationState.targetFingerprint);
|
|
12012
12267
|
graphTransfers.push({
|
|
12013
12268
|
sourceGraphLockPath: sourceGraph.path,
|
|
12014
|
-
sourceGraphLockDigest:
|
|
12269
|
+
sourceGraphLockDigest: sha2565(canonicalJson2(sourceGraph.lock)),
|
|
12015
12270
|
destinationGraphLockPath: destinationState.graphLockPath,
|
|
12016
|
-
destinationGraphLockDigest: destinationGraph ?
|
|
12017
|
-
destinationGraphLockAfterDigest:
|
|
12271
|
+
destinationGraphLockDigest: destinationGraph ? sha2565(canonicalJson2(destinationGraph.lock)) : null,
|
|
12272
|
+
destinationGraphLockAfterDigest: sha2565(canonicalJson2(destinationGraphAfter)),
|
|
12018
12273
|
targetKey: sourceGraph.targetKey,
|
|
12019
12274
|
adapter: destinationState.adapter,
|
|
12020
12275
|
targetFingerprint: destinationState.targetFingerprint
|
|
@@ -12118,7 +12373,7 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
12118
12373
|
let normalizedGraphCount = 0;
|
|
12119
12374
|
for (const graph of graphCandidates) {
|
|
12120
12375
|
const destinationState = await targetStateForGraph(fleet, graph, fleet.fleetId);
|
|
12121
|
-
if (
|
|
12376
|
+
if (resolve25(destinationState.graphLockPath) === resolve25(graph.path)) {
|
|
12122
12377
|
const destinationManifest = (await collectManifestPaths([destinationState.manifestPath]))[0];
|
|
12123
12378
|
if (destinationManifest?.manifest.entries.some((entry) => entry.workspaceOwner === fleetOwner && entryMatchesPackages(entry, selected))) {
|
|
12124
12379
|
assertCorrelatedGraphManifest(
|
|
@@ -12133,7 +12388,7 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
12133
12388
|
}
|
|
12134
12389
|
}
|
|
12135
12390
|
const legacyState = await legacyTargetStateForGraph(fleet, graph);
|
|
12136
|
-
if (
|
|
12391
|
+
if (resolve25(legacyState.graphLockPath) !== resolve25(graph.path)) {
|
|
12137
12392
|
throw new Error(`Legacy graph lock target identity is stale or noncanonical: ${graph.path}`);
|
|
12138
12393
|
}
|
|
12139
12394
|
const legacyManifest = (await collectManifestPaths([legacyState.manifestPath]))[0];
|
|
@@ -12241,7 +12496,7 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
12241
12496
|
)
|
|
12242
12497
|
});
|
|
12243
12498
|
if (!selectedArtifacts) {
|
|
12244
|
-
const fullDigest =
|
|
12499
|
+
const fullDigest = sha2565(canonicalJson2(graph.lock));
|
|
12245
12500
|
const existingDestinationGraph = graphCandidates.find((candidate) => candidate.path === destinationState.graphLockPath);
|
|
12246
12501
|
if (existingDestinationGraph) assertGraphIsSubset(graph, existingDestinationGraph);
|
|
12247
12502
|
const destinationGraph = destinationGraphFromSource(graph.lock, destinationState.targetFingerprint);
|
|
@@ -12249,8 +12504,8 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
12249
12504
|
sourceGraphLockPath: graph.path,
|
|
12250
12505
|
sourceGraphLockDigest: fullDigest,
|
|
12251
12506
|
destinationGraphLockPath: destinationState.graphLockPath,
|
|
12252
|
-
destinationGraphLockDigest: existingDestinationGraph ?
|
|
12253
|
-
destinationGraphLockAfterDigest:
|
|
12507
|
+
destinationGraphLockDigest: existingDestinationGraph ? sha2565(canonicalJson2(existingDestinationGraph.lock)) : null,
|
|
12508
|
+
destinationGraphLockAfterDigest: sha2565(canonicalJson2(destinationGraph)),
|
|
12254
12509
|
targetKey: graph.targetKey,
|
|
12255
12510
|
adapter: graph.adapter,
|
|
12256
12511
|
targetFingerprint: destinationState.targetFingerprint
|
|
@@ -12308,7 +12563,7 @@ function targetKeysForAgent(scope, name) {
|
|
|
12308
12563
|
async function runtimeRoots(scope) {
|
|
12309
12564
|
const roots = /* @__PURE__ */ new Set([scope.root]);
|
|
12310
12565
|
if (scope.config.packages.some((pkg) => pkg.installationType === "user") || Object.values(scope.config.agents).some((agent) => agent.installationType === "user")) {
|
|
12311
|
-
roots.add(
|
|
12566
|
+
roots.add(resolve25(process.env.AGENTWHEEL_TEST_HOME || process.env.HOME || homedir11()));
|
|
12312
12567
|
}
|
|
12313
12568
|
for (const [name, agent] of Object.entries(scope.config.agents)) {
|
|
12314
12569
|
if (agent.transport === "ssh") throw new Error(`Installed-state normalization cannot inspect SSH agent '${name}' locally.`);
|
|
@@ -12326,17 +12581,17 @@ async function runtimeRoots(scope) {
|
|
|
12326
12581
|
async function collectManifests(roots) {
|
|
12327
12582
|
const found = /* @__PURE__ */ new Map();
|
|
12328
12583
|
for (const root of roots) {
|
|
12329
|
-
const dir =
|
|
12584
|
+
const dir = join45(root, ".agentwheel");
|
|
12330
12585
|
let names;
|
|
12331
12586
|
try {
|
|
12332
|
-
names = await
|
|
12587
|
+
names = await readdir11(dir);
|
|
12333
12588
|
} catch (error) {
|
|
12334
12589
|
if (isMissing(error)) continue;
|
|
12335
12590
|
throw error;
|
|
12336
12591
|
}
|
|
12337
12592
|
for (const name of names.sort((a, b) => a.localeCompare(b))) {
|
|
12338
12593
|
if (!name.endsWith(".install-manifest.json")) continue;
|
|
12339
|
-
const path =
|
|
12594
|
+
const path = join45(dir, name);
|
|
12340
12595
|
const raw = JSON.parse(await readFile27(path, "utf8"));
|
|
12341
12596
|
const parsed = installManifestSchema.parse(raw);
|
|
12342
12597
|
if (parsed.version !== 2) {
|
|
@@ -12345,14 +12600,14 @@ async function collectManifests(roots) {
|
|
|
12345
12600
|
}
|
|
12346
12601
|
continue;
|
|
12347
12602
|
}
|
|
12348
|
-
if (
|
|
12603
|
+
if (resolve25(parsed.targetRoot) !== resolve25(root)) {
|
|
12349
12604
|
throw new Error(`Install manifest target root does not match its enumerated runtime root: ${path}`);
|
|
12350
12605
|
}
|
|
12351
12606
|
const expectedPath = installManifestPath(parsed.targetRoot, parsed.adapter, {
|
|
12352
12607
|
installationType: parsed.installationType,
|
|
12353
12608
|
stateKey: parsed.stateKey
|
|
12354
12609
|
});
|
|
12355
|
-
if (
|
|
12610
|
+
if (resolve25(expectedPath) !== resolve25(path)) {
|
|
12356
12611
|
throw new Error(`Install manifest state identity does not match its canonical path: ${path}`);
|
|
12357
12612
|
}
|
|
12358
12613
|
found.set(path, { path, raw, manifest: { ...parsed, revision: computeManifestRevision(raw), legacy: false } });
|
|
@@ -12373,7 +12628,7 @@ async function collectManifestPaths(paths) {
|
|
|
12373
12628
|
installationType: parsed.installationType,
|
|
12374
12629
|
stateKey: parsed.stateKey
|
|
12375
12630
|
});
|
|
12376
|
-
if (
|
|
12631
|
+
if (resolve25(expectedPath) !== resolve25(path)) {
|
|
12377
12632
|
throw new Error(`Install manifest state identity does not match its canonical path: ${path}`);
|
|
12378
12633
|
}
|
|
12379
12634
|
found.push({ path, raw, manifest: { ...parsed, revision: computeManifestRevision(raw), legacy: false } });
|
|
@@ -12392,14 +12647,14 @@ function assertNoForeignSamePathOwnership(manifests, renderedPaths, allowedOwner
|
|
|
12392
12647
|
}
|
|
12393
12648
|
}
|
|
12394
12649
|
async function relevantGraphLocks(workspaceRoot, selected, targetKeys, include) {
|
|
12395
|
-
const root =
|
|
12650
|
+
const root = join45(workspaceRoot, ".agentwheel", "locks");
|
|
12396
12651
|
if (!await pathExists(root)) return [];
|
|
12397
12652
|
const results = [];
|
|
12398
12653
|
for (const path of await listFiles(root)) {
|
|
12399
12654
|
if (!path.endsWith(".graph-lock.json")) continue;
|
|
12400
12655
|
const lock = await readGraphLock(path);
|
|
12401
12656
|
if (!lock.canonical.roots.some((candidate2) => selected.has(candidate2.rootId))) continue;
|
|
12402
|
-
const parts =
|
|
12657
|
+
const parts = relative9(root, path).split(/[\\/]/);
|
|
12403
12658
|
if (parts.length !== 3) throw new Error(`Graph lock path is not canonical: ${path}`);
|
|
12404
12659
|
const [targetKey2, adapter] = parts;
|
|
12405
12660
|
if (targetKeys && !targetKeys.has(targetKey2)) continue;
|
|
@@ -12416,7 +12671,7 @@ async function relevantGraphLocks(workspaceRoot, selected, targetKeys, include)
|
|
|
12416
12671
|
const projection = relevantGraphProjection(lock, selected);
|
|
12417
12672
|
results.push({
|
|
12418
12673
|
...candidate,
|
|
12419
|
-
digest:
|
|
12674
|
+
digest: sha2565(canonicalJson2(projection)),
|
|
12420
12675
|
artifactIdentities: lock.canonical.artifacts.filter((artifact) => artifact.owners.some((owner) => ownerMatchesGraphPackage(lock, owner, selected))).map(graphArtifactIdentity).sort()
|
|
12421
12676
|
});
|
|
12422
12677
|
}
|
|
@@ -12441,7 +12696,7 @@ async function hasRelevantLegacyManifestCandidate(scope, graph, selected) {
|
|
|
12441
12696
|
const legacyManifest = (await collectManifestPaths([legacyState.manifestPath]))[0];
|
|
12442
12697
|
if (legacyManifest?.manifest.entries.some((entry) => entryMatchesGraphPackages(entry, graph.lock, selected))) return true;
|
|
12443
12698
|
const stableState = await targetStateForGraph(scope, graph, scope.fleetId ?? null);
|
|
12444
|
-
if (
|
|
12699
|
+
if (resolve25(stableState.graphLockPath) !== resolve25(graph.path)) return false;
|
|
12445
12700
|
const stableManifest = (await collectManifestPaths([stableState.manifestPath]))[0];
|
|
12446
12701
|
return stableManifest?.manifest.entries.some((entry) => entryMatchesGraphPackages(entry, graph.lock, selected)) ?? false;
|
|
12447
12702
|
}
|
|
@@ -12462,7 +12717,7 @@ async function legacyManifestPathForConfiguredTarget(scope, graph) {
|
|
|
12462
12717
|
baseDir: scope.root
|
|
12463
12718
|
});
|
|
12464
12719
|
const targetRoot = resolveConfigPath(agent.root, scope.root);
|
|
12465
|
-
const installRoot =
|
|
12720
|
+
const installRoot = resolve25(installRootForAdapterInstallationType(adapter, targetRoot, agent.installationType, false));
|
|
12466
12721
|
const stateKey = stateKeyFor(adapter.name, {
|
|
12467
12722
|
installationType: agent.installationType,
|
|
12468
12723
|
stateKey: agent.stateKey,
|
|
@@ -12530,7 +12785,7 @@ async function targetStateForGraph(scope, graph, identityFleetId = scope.fleetId
|
|
|
12530
12785
|
ssh: void 0,
|
|
12531
12786
|
...agent?.stateKey ? { stateKey: agent.stateKey } : {}
|
|
12532
12787
|
};
|
|
12533
|
-
const installRoot =
|
|
12788
|
+
const installRoot = resolve25(installRootForAdapterInstallationType(adapter, targetRoot, installationType, false));
|
|
12534
12789
|
const targetIdentity = resolveTargetStateIdentity({
|
|
12535
12790
|
targetFingerprintParts: fingerprintParts,
|
|
12536
12791
|
workspaceRoot: scope.root,
|
|
@@ -12562,16 +12817,16 @@ async function targetStateForGraph(scope, graph, identityFleetId = scope.fleetId
|
|
|
12562
12817
|
async function installedTargetStateForGraph(scope, graph, manifests, owners, selected, identityFleetId = scope.fleetId ?? null) {
|
|
12563
12818
|
const stable = await targetStateForGraph(scope, graph, identityFleetId);
|
|
12564
12819
|
let state;
|
|
12565
|
-
if (
|
|
12820
|
+
if (resolve25(graph.path) === resolve25(stable.graphLockPath)) {
|
|
12566
12821
|
state = stable;
|
|
12567
12822
|
} else {
|
|
12568
12823
|
const released = await releasedTargetStateForGraph(scope, graph, identityFleetId);
|
|
12569
|
-
if (
|
|
12824
|
+
if (resolve25(graph.path) !== resolve25(released.graphLockPath)) {
|
|
12570
12825
|
throw new Error(`Graph lock target identity is stale or noncanonical: ${graph.path}`);
|
|
12571
12826
|
}
|
|
12572
12827
|
state = released;
|
|
12573
12828
|
}
|
|
12574
|
-
const manifest = manifests.find((candidate) =>
|
|
12829
|
+
const manifest = manifests.find((candidate) => resolve25(candidate.path) === resolve25(state.manifestPath));
|
|
12575
12830
|
if (!manifest) {
|
|
12576
12831
|
throw new Error(`Graph lock is not covered by its canonical install manifest: ${state.manifestPath}`);
|
|
12577
12832
|
}
|
|
@@ -12595,7 +12850,7 @@ async function releasedTargetStateForGraph(scope, graph, identityFleetId = scope
|
|
|
12595
12850
|
...current,
|
|
12596
12851
|
stateKey,
|
|
12597
12852
|
targetFingerprint,
|
|
12598
|
-
graphLockPath:
|
|
12853
|
+
graphLockPath: join45(dirname22(graph.path), `${targetFingerprint}.graph-lock.json`),
|
|
12599
12854
|
manifestPath: installManifestPath(current.installRoot, current.adapter, {
|
|
12600
12855
|
installationType: current.installationType,
|
|
12601
12856
|
stateKey
|
|
@@ -12603,7 +12858,7 @@ async function releasedTargetStateForGraph(scope, graph, identityFleetId = scope
|
|
|
12603
12858
|
};
|
|
12604
12859
|
}
|
|
12605
12860
|
function assertCorrelatedGraphManifest(graph, state, manifest, owners, selected) {
|
|
12606
|
-
if (manifest.manifest.adapter !== state.adapter || manifest.manifest.installationType !== state.installationType || manifest.manifest.stateKey !== state.stateKey ||
|
|
12861
|
+
if (manifest.manifest.adapter !== state.adapter || manifest.manifest.installationType !== state.installationType || manifest.manifest.stateKey !== state.stateKey || resolve25(manifest.manifest.targetRoot) !== resolve25(state.installRoot)) {
|
|
12607
12862
|
throw new Error(`Install manifest identity does not match graph target state: ${manifest.path}`);
|
|
12608
12863
|
}
|
|
12609
12864
|
const entries = manifest.manifest.entries.filter((entry) => entryMatchesGraphPackages(entry, graph.lock, selected));
|
|
@@ -12611,7 +12866,7 @@ function assertCorrelatedGraphManifest(graph, state, manifest, owners, selected)
|
|
|
12611
12866
|
throw new Error(`Graph lock is not covered by selected install-manifest contributions: ${graph.path}`);
|
|
12612
12867
|
}
|
|
12613
12868
|
const graphArtifacts = new Set(graph.artifactIdentities);
|
|
12614
|
-
const graphLockDigest =
|
|
12869
|
+
const graphLockDigest = sha2565(canonicalJson2(graph.lock));
|
|
12615
12870
|
for (const entry of entries) {
|
|
12616
12871
|
if (!owners.has(entry.workspaceOwner)) {
|
|
12617
12872
|
throw new Error(`Graph lock manifest owner mismatch at ${manifest.path}: ${entry.workspaceOwner}.`);
|
|
@@ -12660,7 +12915,7 @@ function destinationGraphFromSource(source, targetFingerprint) {
|
|
|
12660
12915
|
function mergeDestinationManifest(existing, state, generatedAt, desiredEntries, replaceOwners = /* @__PURE__ */ new Set()) {
|
|
12661
12916
|
if (existing) {
|
|
12662
12917
|
const manifest2 = existing.manifest;
|
|
12663
|
-
if (manifest2.adapter !== state.adapter || manifest2.installationType !== state.installationType || manifest2.stateKey !== state.stateKey ||
|
|
12918
|
+
if (manifest2.adapter !== state.adapter || manifest2.installationType !== state.installationType || manifest2.stateKey !== state.stateKey || resolve25(manifest2.targetRoot) !== state.installRoot) {
|
|
12664
12919
|
throw new Error(`Destination manifest identity diverges from the derived target: ${existing.path}`);
|
|
12665
12920
|
}
|
|
12666
12921
|
}
|
|
@@ -12720,7 +12975,7 @@ async function assertEquivalentRuntimeState(entry, sourcePath, destinationPath)
|
|
|
12720
12975
|
const content = await readFile27(path, "utf8");
|
|
12721
12976
|
if (hasMergeRemovalContent(entry.mergeRemoval)) {
|
|
12722
12977
|
assertExactMergeContribution(entry.mergeRemoval, entry.mergeStrategy, content);
|
|
12723
|
-
} else if (
|
|
12978
|
+
} else if (resolve25(sourcePath) === resolve25(destinationPath) && entry.mergeStrategy !== "codex-toml-mcp") {
|
|
12724
12979
|
assertExactMergeContribution({}, entry.mergeStrategy, content);
|
|
12725
12980
|
} else {
|
|
12726
12981
|
throw new Error(`Installed-state normalization cannot prove incomplete merge ownership at ${path}.`);
|
|
@@ -12731,8 +12986,8 @@ async function assertEquivalentRuntimeState(entry, sourcePath, destinationPath)
|
|
|
12731
12986
|
await assertEquivalentRuntimeBytes(sourcePath, destinationPath, entry.hash);
|
|
12732
12987
|
}
|
|
12733
12988
|
function renderedEntryPathForRoot(root, entryPath) {
|
|
12734
|
-
const normalizedRoot =
|
|
12735
|
-
const candidate =
|
|
12989
|
+
const normalizedRoot = resolve25(root);
|
|
12990
|
+
const candidate = resolve25(normalizedRoot, entryPath);
|
|
12736
12991
|
if (candidate !== normalizedRoot && !candidate.startsWith(`${normalizedRoot}/`)) {
|
|
12737
12992
|
throw new Error(`Install manifest entry escapes its target root: ${entryPath}`);
|
|
12738
12993
|
}
|
|
@@ -12823,8 +13078,8 @@ function ownerMatchesGraphPackage(lock, owner, selected) {
|
|
|
12823
13078
|
return ownerMatchesPackage(owner, selected) || lock.canonical.roots.some((root) => selected.has(root.rootId) && root.graphNodeId === owner);
|
|
12824
13079
|
}
|
|
12825
13080
|
function renderedEntryPath(manifest, entry) {
|
|
12826
|
-
const root =
|
|
12827
|
-
const candidate =
|
|
13081
|
+
const root = resolve25(manifest.targetRoot);
|
|
13082
|
+
const candidate = resolve25(root, entry.path);
|
|
12828
13083
|
if (candidate !== root && !candidate.startsWith(`${root}/`)) {
|
|
12829
13084
|
throw new Error(`Install manifest entry escapes its target root: ${entry.path}`);
|
|
12830
13085
|
}
|
|
@@ -12979,7 +13234,7 @@ async function buildJournalGraphLockStates(plan) {
|
|
|
12979
13234
|
for (const transfer of plan.installedState.graphTransfers) {
|
|
12980
13235
|
const sourceBefore = JSON.parse(await readFile27(transfer.sourceGraphLockPath, "utf8"));
|
|
12981
13236
|
const sourceLock = await readGraphLock(transfer.sourceGraphLockPath);
|
|
12982
|
-
const sourceDigest =
|
|
13237
|
+
const sourceDigest = sha2565(canonicalJson2(sourceLock));
|
|
12983
13238
|
if (sourceDigest !== transfer.sourceGraphLockDigest) {
|
|
12984
13239
|
throw new Error(`Source graph lock changed after planning: ${transfer.sourceGraphLockPath}`);
|
|
12985
13240
|
}
|
|
@@ -12997,9 +13252,9 @@ async function buildJournalGraphLockStates(plan) {
|
|
|
12997
13252
|
continue;
|
|
12998
13253
|
}
|
|
12999
13254
|
const destinationBefore = await readOptionalRecord(transfer.destinationGraphLockPath);
|
|
13000
|
-
const destinationBeforeDigest = destinationBefore ?
|
|
13255
|
+
const destinationBeforeDigest = destinationBefore ? sha2565(canonicalJson2(await readGraphLock(transfer.destinationGraphLockPath))) : null;
|
|
13001
13256
|
const destinationAfter = destinationBefore ? await readGraphLock(transfer.destinationGraphLockPath) : destinationGraphFromSource(sourceLock, transfer.targetFingerprint);
|
|
13002
|
-
const destinationAfterDigest =
|
|
13257
|
+
const destinationAfterDigest = sha2565(canonicalJson2(destinationAfter));
|
|
13003
13258
|
if (destinationBeforeDigest !== transfer.destinationGraphLockDigest || destinationAfterDigest !== transfer.destinationGraphLockAfterDigest) {
|
|
13004
13259
|
throw new Error(`Destination graph lock changed after planning: ${transfer.destinationGraphLockPath}`);
|
|
13005
13260
|
}
|
|
@@ -13109,7 +13364,7 @@ async function assertJournalRecoverable(journal) {
|
|
|
13109
13364
|
}
|
|
13110
13365
|
for (const graphLock of journal.graphLocks) {
|
|
13111
13366
|
const raw = await readOptionalRecord(graphLock.path);
|
|
13112
|
-
const digest2 = raw ?
|
|
13367
|
+
const digest2 = raw ? sha2565(canonicalJson2(await readGraphLock(graphLock.path))) : null;
|
|
13113
13368
|
if (digest2 !== graphLock.beforeDigest && digest2 !== graphLock.afterDigest) {
|
|
13114
13369
|
throw new Error(`Cannot recover fleet normalization because graph lock changed outside the transaction: ${graphLock.path}`);
|
|
13115
13370
|
}
|
|
@@ -13162,10 +13417,10 @@ function packageMap(config) {
|
|
|
13162
13417
|
return result;
|
|
13163
13418
|
}
|
|
13164
13419
|
function configRevision(config) {
|
|
13165
|
-
return
|
|
13420
|
+
return sha2565(canonicalJson2(config));
|
|
13166
13421
|
}
|
|
13167
13422
|
function normalizationJournalPath(sourceRoot, destinationFleet) {
|
|
13168
|
-
return
|
|
13423
|
+
return join45(sourceRoot, ".agentwheel", `fleet-normalize-${destinationFleet}.journal.json`);
|
|
13169
13424
|
}
|
|
13170
13425
|
function canonicalJson2(value) {
|
|
13171
13426
|
return JSON.stringify(sortValue(value));
|
|
@@ -13177,8 +13432,8 @@ function sortValue(value) {
|
|
|
13177
13432
|
}
|
|
13178
13433
|
return value;
|
|
13179
13434
|
}
|
|
13180
|
-
function
|
|
13181
|
-
return
|
|
13435
|
+
function sha2565(value) {
|
|
13436
|
+
return createHash14("sha256").update(value).digest("hex");
|
|
13182
13437
|
}
|
|
13183
13438
|
function assertDigest(value) {
|
|
13184
13439
|
if (!/^[a-f0-9]{64}$/.test(value)) throw new Error(`Invalid plan digest: ${value}`);
|
|
@@ -13216,6 +13471,7 @@ var commandGovernance = {
|
|
|
13216
13471
|
"package migrate": "governed-always",
|
|
13217
13472
|
remember: "governed-always",
|
|
13218
13473
|
"ownership handoff": "governed-unless-dry-run",
|
|
13474
|
+
"ownership recovery-plan": "incidental-cache",
|
|
13219
13475
|
"ownership retire-stale": "governed-apply",
|
|
13220
13476
|
"mcp retire": "governed-apply",
|
|
13221
13477
|
eject: "governed-always",
|
|
@@ -13322,7 +13578,7 @@ fleetCommand.command("normalize").description("plan or apply duplicate desired-s
|
|
|
13322
13578
|
});
|
|
13323
13579
|
cacheCommand.command("prune").description("remove old Git source snapshots while preserving locked commits").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 workspace root").option("--keep <count>", "newest snapshots to retain per source", parsePositiveInteger, 3).option("--apply", "delete the selected snapshots; without this flag only preview", false).action(async (options) => {
|
|
13324
13580
|
const targetRoot = await workspaceContextRootFromOptions(normalizeRuntimeScopeOptions(options));
|
|
13325
|
-
const cacheRoot =
|
|
13581
|
+
const cacheRoot = join46(targetRoot, ".agentwheel", "cache");
|
|
13326
13582
|
const result = await pruneGitCache(cacheRoot, { keepSnapshots: options.keep, dryRun: !options.apply });
|
|
13327
13583
|
const verb = options.apply ? "Removed" : "Would remove";
|
|
13328
13584
|
for (const path of result.removedPaths) console.log(`${verb} ${path}`);
|
|
@@ -13340,7 +13596,7 @@ program.command("list").description("list artifacts exposed by a package source"
|
|
|
13340
13596
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
13341
13597
|
const selectedArtifacts = selectedArtifactsFromOptionsOrRegistry(options, resolvedInput.registryEntry);
|
|
13342
13598
|
const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
|
|
13343
|
-
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot:
|
|
13599
|
+
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join46(targetRoot, ".agentwheel", "cache") }))));
|
|
13344
13600
|
try {
|
|
13345
13601
|
const artifacts = filterArtifactsBySelection(await driver.list(resolved), selectedArtifacts);
|
|
13346
13602
|
for (const artifact of artifacts) {
|
|
@@ -13409,7 +13665,7 @@ program.command("try").description("read and validate one skill for the current
|
|
|
13409
13665
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
13410
13666
|
const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
|
|
13411
13667
|
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, {
|
|
13412
|
-
cacheRoot:
|
|
13668
|
+
cacheRoot: join46(targetRoot, ".agentwheel", "cache")
|
|
13413
13669
|
}))));
|
|
13414
13670
|
try {
|
|
13415
13671
|
const trial = await createSkillTrial(driver, resolved, selectedArtifactsFromOptionsOrRegistry(options, resolvedInput.registryEntry));
|
|
@@ -13431,7 +13687,7 @@ program.command("scan").description("scan a package source for validation findin
|
|
|
13431
13687
|
const targetRoot = await workspaceContextRootFromOptions(normalizeRuntimeScopeOptions(options));
|
|
13432
13688
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
13433
13689
|
const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
|
|
13434
|
-
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot:
|
|
13690
|
+
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join46(targetRoot, ".agentwheel", "cache") }))));
|
|
13435
13691
|
try {
|
|
13436
13692
|
const result = await driver.scan(resolved);
|
|
13437
13693
|
if (result.findings.length === 0) {
|
|
@@ -13446,10 +13702,10 @@ program.command("scan").description("scan a package source for validation findin
|
|
|
13446
13702
|
await releaseGitSnapshotLease(resolved.cacheLeasePath);
|
|
13447
13703
|
}
|
|
13448
13704
|
});
|
|
13449
|
-
program.command("plan").description("preview what install would reconcile without writing").argument("[name-or-source]", "configured package name/source or package source to preview").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "use the user workspace", false).option("--local", "use the nearest local workspace", false).option("--fleet <id>", "use one registered named fleet").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 agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--profile <name>", "workspace runtime profile").option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--override <source-or-package::type/name>", "for source previews, allow the source to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--dry-run", "accepted for symmetry; plan never writes", false).option("--format <fmt>", "output format: human|json|mermaid|html", "human").option("--json", "print the resolved plan as JSON", false).option("--force-drift", "replace drifted managed artifacts during install planning", false).option("--force-foreign-state", "plan even when another workspace owns install state at the same paths", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "exclude unrelated configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--refresh", "refresh available package versions even when the version-index TTL is fresh", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
|
|
13705
|
+
program.command("plan").description("preview what install would reconcile without writing").argument("[name-or-source]", "configured package name/source or package source to preview").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "use the user workspace", false).option("--local", "use the nearest local workspace", false).option("--fleet <id>", "use one registered named fleet").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 agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--profile <name>", "workspace runtime profile").option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--override <source-or-package::type/name>", "for source previews, allow the source to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--dry-run", "accepted for symmetry; plan never writes", false).option("--format <fmt>", "output format: human|json|mermaid|html", "human").option("--json", "print the resolved plan as JSON", false).option("--force-drift", "replace drifted managed artifacts during install planning", false).option("--force-foreign-state", "plan even when another workspace owns install state at the same paths", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--recover-legacy-state", "build a stable state while preserving invalid legacy candidates", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "exclude unrelated configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--refresh", "refresh available package versions even when the version-index TTL is fresh", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
|
|
13450
13706
|
await runInstallCommand(source, { ...options, dryRun: true }, { apply: false });
|
|
13451
13707
|
});
|
|
13452
|
-
program.command("install").description("install configured packages into runtime targets").argument("[name-or-source]", "configured package name/source or package source to add and install").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "use the user workspace", false).option("--local", "use the nearest local workspace", false).option("--fleet <id>", "use one registered named fleet").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 agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--override <source-or-package::type/name>", "when adding a source, allow it to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--format <fmt>", "output format: human|json|mermaid|html", "human").option("--json", "print the resolved plan as JSON", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-foreign-state", "plan even when another workspace owns install state at the same paths", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--reload-runtimes", "run configured runtime reload commands after executed semantic plugin changes", false).option("--restart-runtimes", "alias for --reload-runtimes", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "exclude unrelated configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--refresh", "refresh available package versions even when the version-index TTL is fresh", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).addHelpText("after", "\nScoped install never removes files owned only by other configured packages; run a full install to reconcile those removals.\n").action(async (source, options) => {
|
|
13708
|
+
program.command("install").description("install configured packages into runtime targets").argument("[name-or-source]", "configured package name/source or package source to add and install").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "use the user workspace", false).option("--local", "use the nearest local workspace", false).option("--fleet <id>", "use one registered named fleet").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 agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--override <source-or-package::type/name>", "when adding a source, allow it to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--format <fmt>", "output format: human|json|mermaid|html", "human").option("--json", "print the resolved plan as JSON", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-foreign-state", "plan even when another workspace owns install state at the same paths", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--recover-legacy-state", "build a stable state while preserving invalid legacy candidates", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--reload-runtimes", "run configured runtime reload commands after executed semantic plugin changes", false).option("--restart-runtimes", "alias for --reload-runtimes", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "exclude unrelated configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--refresh", "refresh available package versions even when the version-index TTL is fresh", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).addHelpText("after", "\nScoped install never removes files owned only by other configured packages; run a full install to reconcile those removals.\n").action(async (source, options) => {
|
|
13453
13709
|
await runInstallCommand(source, options, { apply: !options.dryRun });
|
|
13454
13710
|
});
|
|
13455
13711
|
program.command("serve").description("serve a read-only live dashboard for the resolved install plan").argument("[name-or-source]", "configured package name/source or package source to preview").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "use the user workspace", false).option("--local", "use the nearest local workspace", false).option("--fleet <id>", "use one registered named fleet").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 agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--profile <name>", "workspace runtime profile").option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--override <source-or-package::type/name>", "for source previews, allow the source to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--force-drift", "replace drifted managed artifacts during install planning", false).option("--force-foreign-state", "plan even when another workspace owns install state at the same paths", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "exclude unrelated configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--refresh", "refresh available package versions even when the version-index TTL is fresh", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).option("--bind <addr>", "interface to bind", "127.0.0.1").option("--port <n>", "TCP port (0 selects an ephemeral port)", "8765").option("--interval <seconds>", "background re-render cadence in seconds", "60").option("--once", "render once and skip the background re-render loop", false).action(async (source, options) => {
|
|
@@ -13636,7 +13892,7 @@ ownershipCommand.command("handoff").description("transfer one managed artifact b
|
|
|
13636
13892
|
const toWorkspaceRoot = normalizeCliPath(options.toWorkspaceRoot);
|
|
13637
13893
|
if (options.toFleet) {
|
|
13638
13894
|
const fleet = await showRegisteredFleet(options.toFleet);
|
|
13639
|
-
if (
|
|
13895
|
+
if (resolve26(fleet.root) !== resolve26(toWorkspaceRoot)) {
|
|
13640
13896
|
throw new Error(`Destination fleet '${options.toFleet}' is registered at ${fleet.root}, not ${toWorkspaceRoot}.`);
|
|
13641
13897
|
}
|
|
13642
13898
|
}
|
|
@@ -13662,11 +13918,85 @@ ownershipCommand.command("handoff").description("transfer one managed artifact b
|
|
|
13662
13918
|
console.log(`Manifest revision: ${result.manifestRevision}`);
|
|
13663
13919
|
console.log(`Owner: ${result.fromOwner} -> ${result.toOwner}`);
|
|
13664
13920
|
});
|
|
13921
|
+
ownershipCommand.command("recovery-plan").description("write a read-only per-path legacy ownership evidence report").requiredOption("--fleet <id>", "registered Fleet containing the current source graph").requiredOption("--report <path>", "new private JSON report path").option("--agent <name>", "one named Fleet target").option("--profile <name>", "Fleet profile resolving to one target").action(async (options) => {
|
|
13922
|
+
const targets = await resolveCliTargets({ fleet: options.fleet, agent: options.agent, profile: options.profile });
|
|
13923
|
+
if (targets.length !== 1) throw new Error(`Recovery planning requires one Fleet target, found ${targets.length}.`);
|
|
13924
|
+
const target = targets[0];
|
|
13925
|
+
if (target.fleetId !== options.fleet) throw new Error("Resolved target does not belong to the selected Fleet.");
|
|
13926
|
+
const adapter = await resolveAdapterForTarget(target, {});
|
|
13927
|
+
const transport = transportForTarget(target);
|
|
13928
|
+
const requestedReport = normalizeCliPath(options.report);
|
|
13929
|
+
const reportPath = join46(await realpath2(dirname23(requestedReport)), basename21(requestedReport));
|
|
13930
|
+
const reportInsideTarget = relative10(resolve26(target.targetRoot), reportPath);
|
|
13931
|
+
if (reportInsideTarget === "" || !reportInsideTarget.startsWith("..") && !isAbsolute4(reportInsideTarget)) {
|
|
13932
|
+
throw new Error("Recovery report must be outside the runtime target root.");
|
|
13933
|
+
}
|
|
13934
|
+
const installationType = target.installationType ?? resolveInstallationTypeForAdapter(adapter, void 0);
|
|
13935
|
+
const config = await readMergedWorkspaceConfig(target.workspaceRoot);
|
|
13936
|
+
if (config.packages.length === 0) throw new Error("Fleet has no configured source packages.");
|
|
13937
|
+
const stage = await mkdtemp4(join46(tmpdir5(), "agentwheel-recovery-"));
|
|
13938
|
+
const priorTmpdir = process.env.TMPDIR;
|
|
13939
|
+
try {
|
|
13940
|
+
process.env.TMPDIR = stage;
|
|
13941
|
+
const graphPlan = await createGraphSourcePlan({
|
|
13942
|
+
roots: config.packages.map((pkg) => ({
|
|
13943
|
+
rootId: pkg.name,
|
|
13944
|
+
source: pkg.source,
|
|
13945
|
+
mode: pkg.mode,
|
|
13946
|
+
version: pkg.version,
|
|
13947
|
+
ref: pkg.requestedRef,
|
|
13948
|
+
select: pkg.selection ? void 0 : normalizeArtifactSelectors(pkg.select, pkg.skills),
|
|
13949
|
+
selection: pkg.selection,
|
|
13950
|
+
aliases: pkg.aliases,
|
|
13951
|
+
overrides: pkg.overrides,
|
|
13952
|
+
includeSuggestions: pkg.withSuggestions,
|
|
13953
|
+
suggestionAliases: pkg.suggestions,
|
|
13954
|
+
useLock: false
|
|
13955
|
+
})),
|
|
13956
|
+
targetRoot: target.targetRoot,
|
|
13957
|
+
workspaceRoot: target.workspaceRoot,
|
|
13958
|
+
fleetId: target.fleetId,
|
|
13959
|
+
adapter,
|
|
13960
|
+
transport,
|
|
13961
|
+
targetKey: targetKeyForTarget(target, adapter.name),
|
|
13962
|
+
targetFingerprintParts: targetFingerprintParts(target, adapter, adapterOptionsForTarget(target, {}), installationType),
|
|
13963
|
+
installationType,
|
|
13964
|
+
stateKey: target.stateKey,
|
|
13965
|
+
readOnly: true,
|
|
13966
|
+
yes: true,
|
|
13967
|
+
freshGraphOnly: true,
|
|
13968
|
+
deferForeignStateCheck: true,
|
|
13969
|
+
cacheRoot: join46(stage, "source-cache"),
|
|
13970
|
+
registryCachePath: join46(stage, "registry.json")
|
|
13971
|
+
});
|
|
13972
|
+
const installRoot = installRootForAdapterInstallationType(adapter, target.targetRoot, installationType, transport.kind === "ssh");
|
|
13973
|
+
const report = await planLegacyRecovery({
|
|
13974
|
+
graphPlan,
|
|
13975
|
+
installRoot,
|
|
13976
|
+
workspaceRoot: target.workspaceRoot,
|
|
13977
|
+
fleetId: target.fleetId,
|
|
13978
|
+
explicitStateKey: target.stateKey,
|
|
13979
|
+
targetKey: targetKeyForTarget(target, adapter.name),
|
|
13980
|
+
adapter: adapter.name,
|
|
13981
|
+
installationType,
|
|
13982
|
+
historicalLocks: await inventoryHistoricalLocks(target.workspaceRoot),
|
|
13983
|
+
transport
|
|
13984
|
+
});
|
|
13985
|
+
await writeFile13(reportPath, `${JSON.stringify(report, null, 2)}
|
|
13986
|
+
`, { mode: 384, flag: "wx" });
|
|
13987
|
+
console.log(`Recovery report: ${reportPath}`);
|
|
13988
|
+
console.log(JSON.stringify(report.counts));
|
|
13989
|
+
} finally {
|
|
13990
|
+
if (priorTmpdir === void 0) delete process.env.TMPDIR;
|
|
13991
|
+
else process.env.TMPDIR = priorTmpdir;
|
|
13992
|
+
await rm10(stage, { recursive: true, force: true });
|
|
13993
|
+
}
|
|
13994
|
+
});
|
|
13665
13995
|
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").requiredOption("--to-workspace-root <path>", "registered destination Fleet root").requiredOption("--source-state-key <key>", "exact source install-manifest state key").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) => {
|
|
13666
13996
|
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
13667
13997
|
const fleet = await showRegisteredFleet(options.fleet);
|
|
13668
13998
|
const toWorkspaceRoot = normalizeCliPath(options.toWorkspaceRoot);
|
|
13669
|
-
if (
|
|
13999
|
+
if (resolve26(fleet.root) !== resolve26(toWorkspaceRoot)) {
|
|
13670
14000
|
throw new Error(`Destination Fleet '${options.fleet}' is registered at ${fleet.root}, not ${toWorkspaceRoot}.`);
|
|
13671
14001
|
}
|
|
13672
14002
|
const targets = await resolveCliTargets(normalizedOptions);
|
|
@@ -13674,7 +14004,7 @@ ownershipCommand.command("retire-stale").description("retire stale source-manife
|
|
|
13674
14004
|
throw new Error(`Stale ownership retirement requires exactly one destination Fleet target, found ${targets.length}.`);
|
|
13675
14005
|
}
|
|
13676
14006
|
const target = targets[0];
|
|
13677
|
-
if (target.fleetId !== options.fleet ||
|
|
14007
|
+
if (target.fleetId !== options.fleet || resolve26(target.workspaceRoot) !== resolve26(fleet.root)) {
|
|
13678
14008
|
throw new Error(`Resolved target does not belong to destination Fleet '${options.fleet}'.`);
|
|
13679
14009
|
}
|
|
13680
14010
|
const state = await resolveCliTargetState(target, normalizedOptions);
|
|
@@ -13832,7 +14162,7 @@ journalCommand.command("list").description("show pending apply journals for reso
|
|
|
13832
14162
|
if (!journal) continue;
|
|
13833
14163
|
pending += 1;
|
|
13834
14164
|
console.log(`PENDING ${state.adapter.name}/${state.installationType} at ${state.installRoot}`);
|
|
13835
|
-
console.log(` journal: ${
|
|
14165
|
+
console.log(` journal: ${join46(state.installRoot, ".agentwheel", `${state.state.stateKey}.apply-journal.json`)}`);
|
|
13836
14166
|
console.log(` stateKey: ${state.state.stateKey}`);
|
|
13837
14167
|
console.log(` createdAt: ${journal.createdAt}`);
|
|
13838
14168
|
console.log(` updatedAt: ${journal.updatedAt}`);
|
|
@@ -14168,14 +14498,14 @@ async function packageEntryFromSource(source, targetRoot, options) {
|
|
|
14168
14498
|
);
|
|
14169
14499
|
}
|
|
14170
14500
|
const bundle = options.deferGraphRendering ? await stageSourceRaw(driver, resolvedSource, {
|
|
14171
|
-
cacheRoot:
|
|
14501
|
+
cacheRoot: join46(targetRoot, ".agentwheel", "cache"),
|
|
14172
14502
|
mode: options.mode,
|
|
14173
14503
|
ref: initialVersion?.ref,
|
|
14174
14504
|
frozenLock: lockMode
|
|
14175
14505
|
}) : await stageSource(driver, resolvedSource, {
|
|
14176
14506
|
workspaceRoot: targetRoot,
|
|
14177
14507
|
adapter,
|
|
14178
|
-
cacheRoot:
|
|
14508
|
+
cacheRoot: join46(targetRoot, ".agentwheel", "cache"),
|
|
14179
14509
|
mode: options.mode,
|
|
14180
14510
|
ref: initialVersion?.ref,
|
|
14181
14511
|
frozenLock: lockMode,
|
|
@@ -14576,7 +14906,8 @@ async function buildGraphPlansForTarget(target, source, options, behavior) {
|
|
|
14576
14906
|
}
|
|
14577
14907
|
const priorGroupState = await resolveCliTargetState(group.target, {
|
|
14578
14908
|
...targetOptions,
|
|
14579
|
-
installationType: group.installationType
|
|
14909
|
+
installationType: group.installationType,
|
|
14910
|
+
recoverLegacyState: targetOptions.recoverLegacyState
|
|
14580
14911
|
});
|
|
14581
14912
|
const previousGroupLock = priorGroupState.graphLock;
|
|
14582
14913
|
const dependencyUpdateRootNames = /* @__PURE__ */ new Set();
|
|
@@ -14685,7 +15016,8 @@ async function buildGraphPlansForTarget(target, source, options, behavior) {
|
|
|
14685
15016
|
deferForeignStateCheck: targetOptions.deferForeignStateCheck === true || targetOptions.focusedArtifact !== void 0,
|
|
14686
15017
|
replaceConflict: targetOptions.replaceConflict,
|
|
14687
15018
|
retireExactMcp: targetOptions.retireExactMcp,
|
|
14688
|
-
expectedFromWorkspaceOwner: targetOptions.expectedFromWorkspaceOwner
|
|
15019
|
+
expectedFromWorkspaceOwner: targetOptions.expectedFromWorkspaceOwner,
|
|
15020
|
+
recoverLegacyState: targetOptions.recoverLegacyState
|
|
14689
15021
|
});
|
|
14690
15022
|
if ((behavior.mode === "install" || behavior.mode === "update") && scopedRootId) {
|
|
14691
15023
|
const manifest = result.previousManifest;
|
|
@@ -14756,7 +15088,7 @@ function scopeUpdatePlanToDependencies(result, selectors, previousLock, manifest
|
|
|
14756
15088
|
selectedPreviousNodeIds,
|
|
14757
15089
|
selectedRootIds
|
|
14758
15090
|
);
|
|
14759
|
-
const graphLockDigest =
|
|
15091
|
+
const graphLockDigest = createHash15("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
|
|
14760
15092
|
return {
|
|
14761
15093
|
...result,
|
|
14762
15094
|
bundle: { ...result.bundle, graphLock },
|
|
@@ -14882,7 +15214,7 @@ function operationBelongsToDependencyUpdate(operation, allowedNodeIds, allowedNa
|
|
|
14882
15214
|
function scopeInstallPlanToArtifact(result, rootId, focused, manifest) {
|
|
14883
15215
|
const scoped = scopePlanOperationsToArtifact(result, rootId, focused, manifest);
|
|
14884
15216
|
const graphLock = focusedGraphLockForInstall(scoped.bundle.graphLock, rootId, focused);
|
|
14885
|
-
const graphLockDigest =
|
|
15217
|
+
const graphLockDigest = createHash15("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
|
|
14886
15218
|
return {
|
|
14887
15219
|
...scoped,
|
|
14888
15220
|
bundle: { ...scoped.bundle, graphLock },
|
|
@@ -14893,7 +15225,7 @@ function scopeInstallPlanToArtifact(result, rootId, focused, manifest) {
|
|
|
14893
15225
|
function scopeUpdatePlanToArtifact(result, rootId, focused, previousLock, manifest) {
|
|
14894
15226
|
const scoped = scopePlanOperationsToArtifact(result, rootId, focused, manifest, previousLock);
|
|
14895
15227
|
const graphLock = mergeFocusedArtifactGraphLock(scoped.bundle.graphLock, previousLock, rootId, focused);
|
|
14896
|
-
const graphLockDigest =
|
|
15228
|
+
const graphLockDigest = createHash15("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
|
|
14897
15229
|
return {
|
|
14898
15230
|
...scoped,
|
|
14899
15231
|
bundle: { ...scoped.bundle, graphLock },
|
|
@@ -15188,7 +15520,7 @@ function scopeUpdatePlanToRoot(result, rootId, previousLock, manifest) {
|
|
|
15188
15520
|
selectedPreviousNodeIds,
|
|
15189
15521
|
/* @__PURE__ */ new Set([rootId])
|
|
15190
15522
|
);
|
|
15191
|
-
const graphLockDigest =
|
|
15523
|
+
const graphLockDigest = createHash15("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
|
|
15192
15524
|
return {
|
|
15193
15525
|
...scoped,
|
|
15194
15526
|
bundle: { ...scoped.bundle, graphLock },
|
|
@@ -15260,7 +15592,7 @@ function keepManifestEntryOperation(entry, targetRoot, scopeDescription, operati
|
|
|
15260
15592
|
artifactType: entry.artifactType,
|
|
15261
15593
|
artifactName: entry.artifactName,
|
|
15262
15594
|
kind: entry.kind,
|
|
15263
|
-
destPath: operation?.destPath ??
|
|
15595
|
+
destPath: operation?.destPath ?? join46(targetRoot, entry.path),
|
|
15264
15596
|
relativeDestPath: entry.path,
|
|
15265
15597
|
desiredHash: entry.sourceHash,
|
|
15266
15598
|
currentHash: operation?.currentHash ?? entry.hash,
|
|
@@ -15511,7 +15843,9 @@ async function resolveCliTargetState(target, options) {
|
|
|
15511
15843
|
graphLockPath: stableGraphLockPath,
|
|
15512
15844
|
stableManifest,
|
|
15513
15845
|
stableLock,
|
|
15514
|
-
transport
|
|
15846
|
+
transport,
|
|
15847
|
+
recoverLegacyState: options.recoverLegacyState,
|
|
15848
|
+
warn: options.warn
|
|
15515
15849
|
});
|
|
15516
15850
|
return {
|
|
15517
15851
|
adapter,
|
|
@@ -15526,7 +15860,7 @@ async function resolveCliTargetState(target, options) {
|
|
|
15526
15860
|
migration: prior.migration,
|
|
15527
15861
|
targetStateFilePreconditions: {
|
|
15528
15862
|
graphLockPath: stableGraphLockPath,
|
|
15529
|
-
graphLockRevision: stableLock ?
|
|
15863
|
+
graphLockRevision: stableLock ? createHash15("sha256").update(canonicalGraphLockJson(stableLock)).digest("hex") : null,
|
|
15530
15864
|
sourceLockRevision: computeSourceLockRevision(stableSourceLock)
|
|
15531
15865
|
}
|
|
15532
15866
|
};
|
|
@@ -15549,7 +15883,7 @@ async function collectTargetStatus(target, options) {
|
|
|
15549
15883
|
const pending = await collectPendingInstallWork(target, options);
|
|
15550
15884
|
const foreignObservations = pending.foreignStateObservations;
|
|
15551
15885
|
const effectiveGraphLock = graphLock ?? pending.observedGraphLock;
|
|
15552
|
-
const observedManifestRevision = foreignObservations.length > 0 ?
|
|
15886
|
+
const observedManifestRevision = foreignObservations.length > 0 ? createHash15("sha256").update(JSON.stringify(foreignObservations.map((observation) => [
|
|
15553
15887
|
observation.fileName,
|
|
15554
15888
|
observation.manifestRevision
|
|
15555
15889
|
]).sort())).digest("hex") : null;
|
|
@@ -15796,7 +16130,7 @@ async function assertCompositeMutationSupported(workspaceRoot, profile, readOnly
|
|
|
15796
16130
|
`Composite-profile mutation is refused before member execution because ${member.id} uses ${member.transport}; durable remote policy and recovery preflight is not implemented.`
|
|
15797
16131
|
);
|
|
15798
16132
|
}
|
|
15799
|
-
const memberRoot =
|
|
16133
|
+
const memberRoot = resolve26(workspaceRoot, member.workspace);
|
|
15800
16134
|
const memberPolicy = await mutationPolicyForWorkspace(memberRoot);
|
|
15801
16135
|
if (memberPolicy?.revisioning.mode === "commit-after-verify") {
|
|
15802
16136
|
throw new Error(
|
|
@@ -16032,12 +16366,12 @@ async function printDoctor(target, options) {
|
|
|
16032
16366
|
const requestedSkills = doctorSkillRequests(target, options);
|
|
16033
16367
|
const skills = [];
|
|
16034
16368
|
for (const request of requestedSkills) {
|
|
16035
|
-
const skillPath =
|
|
16369
|
+
const skillPath = join46(state.installRoot, targetMapping.dest, request.name);
|
|
16036
16370
|
const exists = await pathExists(skillPath);
|
|
16037
16371
|
const manifestEntry = manifest?.entries.find((entry) => {
|
|
16038
16372
|
if (entry.artifactType !== "skills") return false;
|
|
16039
16373
|
const legacyInstallName = "installName" in entry && typeof entry.installName === "string" ? entry.installName : void 0;
|
|
16040
|
-
return entry.artifactName === request.name || legacyInstallName === request.name || entry.path ===
|
|
16374
|
+
return entry.artifactName === request.name || legacyInstallName === request.name || entry.path === join46(targetMapping.dest, request.name);
|
|
16041
16375
|
});
|
|
16042
16376
|
const status = manifestEntry ? "managed" : exists ? "present-unmanaged" : "missing";
|
|
16043
16377
|
skills.push({
|
|
@@ -16117,7 +16451,7 @@ function doctorSkillLabel(name) {
|
|
|
16117
16451
|
return `${name} skill`;
|
|
16118
16452
|
}
|
|
16119
16453
|
function isSyncwheelWorkspace(targetRoot) {
|
|
16120
|
-
return existsSync(
|
|
16454
|
+
return existsSync(join46(targetRoot, ".syncwheel", "manifest.json"));
|
|
16121
16455
|
}
|
|
16122
16456
|
function skillInstallCommand(adapter, installationType, options, skill, behavior = {}) {
|
|
16123
16457
|
const args = [
|
|
@@ -16280,14 +16614,14 @@ function looksLikeSourceSpecifier(value) {
|
|
|
16280
16614
|
}
|
|
16281
16615
|
function normalizeCliPath(value) {
|
|
16282
16616
|
if (value === "~") return homedir12();
|
|
16283
|
-
if (value.startsWith("~/")) return
|
|
16284
|
-
return
|
|
16617
|
+
if (value.startsWith("~/")) return resolve26(homedir12(), value.slice(2));
|
|
16618
|
+
return resolve26(value);
|
|
16285
16619
|
}
|
|
16286
16620
|
function shellQuoteArgument(value) {
|
|
16287
16621
|
return /^[a-z0-9_./:@%+=,-]+$/i.test(value) ? value : `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
16288
16622
|
}
|
|
16289
16623
|
function isHomePath(path) {
|
|
16290
|
-
return
|
|
16624
|
+
return resolve26(path) === resolve26(homedir12());
|
|
16291
16625
|
}
|
|
16292
16626
|
function adapterListFromOption(adapter) {
|
|
16293
16627
|
if (!adapter) return [];
|
|
@@ -16342,11 +16676,11 @@ function filterUninstallPlanBySelection(plan, selected) {
|
|
|
16342
16676
|
};
|
|
16343
16677
|
}
|
|
16344
16678
|
async function initPackage(root) {
|
|
16345
|
-
await mkdir16(
|
|
16346
|
-
await mkdir16(
|
|
16347
|
-
await mkdir16(
|
|
16348
|
-
const manifestPath =
|
|
16349
|
-
const instructionsPath =
|
|
16679
|
+
await mkdir16(join46(root, "instructions"), { recursive: true });
|
|
16680
|
+
await mkdir16(join46(root, "rules"), { recursive: true });
|
|
16681
|
+
await mkdir16(join46(root, "skills"), { recursive: true });
|
|
16682
|
+
const manifestPath = join46(root, "openpack.json");
|
|
16683
|
+
const instructionsPath = join46(root, "instructions", "AGENTS.md");
|
|
16350
16684
|
const manifest = {
|
|
16351
16685
|
schemaVersion: CURRENT_OPENPACK_SCHEMA_VERSION,
|
|
16352
16686
|
name: "example/agentwheel-package",
|
|
@@ -16364,7 +16698,7 @@ async function initPackage(root) {
|
|
|
16364
16698
|
await writeFile13(instructionsPath, "# Agent Instructions\n", "utf8");
|
|
16365
16699
|
}
|
|
16366
16700
|
async function defaultBootstrapPackage(_root) {
|
|
16367
|
-
const packageRoot = await findAgentwheelPackageRoot(
|
|
16701
|
+
const packageRoot = await findAgentwheelPackageRoot(dirname23(fileURLToPath3(import.meta.url)));
|
|
16368
16702
|
if (!packageRoot) return void 0;
|
|
16369
16703
|
return {
|
|
16370
16704
|
name: "agentwheel",
|
|
@@ -16408,10 +16742,10 @@ function withFleetExample(config) {
|
|
|
16408
16742
|
};
|
|
16409
16743
|
}
|
|
16410
16744
|
async function findAgentwheelPackageRoot(start) {
|
|
16411
|
-
let current =
|
|
16745
|
+
let current = resolve26(start);
|
|
16412
16746
|
while (true) {
|
|
16413
16747
|
if (await findPackageManifestPath(current, { warnLegacy: false })) return current;
|
|
16414
|
-
const parent =
|
|
16748
|
+
const parent = dirname23(current);
|
|
16415
16749
|
if (parent === current) return void 0;
|
|
16416
16750
|
current = parent;
|
|
16417
16751
|
}
|
|
@@ -16536,14 +16870,14 @@ async function governedCommandContext(command, options) {
|
|
|
16536
16870
|
const kind = String(command.args[0] ?? "workspace");
|
|
16537
16871
|
return {
|
|
16538
16872
|
workspaceRoot: workspaceRoot2,
|
|
16539
|
-
anticipatedPaths: kind === "package" ? [
|
|
16873
|
+
anticipatedPaths: kind === "package" ? [join46(targetRoot, "openpack.json"), join46(targetRoot, "instructions", "AGENTS.md")] : [workspaceConfigPath(targetRoot)]
|
|
16540
16874
|
};
|
|
16541
16875
|
}
|
|
16542
16876
|
if (path === "package migrate") {
|
|
16543
16877
|
const packageRoot = normalizeTargetRoot(String(command.args[0] ?? "."));
|
|
16544
16878
|
return {
|
|
16545
16879
|
workspaceRoot: await findExistingWorkspaceRoot(packageRoot) ?? packageRoot,
|
|
16546
|
-
anticipatedPaths: ["agentwheel.json", "agentwheel.jsonc", "openpack.json", "openpack.jsonc"].map((name) =>
|
|
16880
|
+
anticipatedPaths: ["agentwheel.json", "agentwheel.jsonc", "openpack.json", "openpack.jsonc"].map((name) => join46(packageRoot, name))
|
|
16547
16881
|
};
|
|
16548
16882
|
}
|
|
16549
16883
|
const source = typeof command.args[0] === "string" ? command.args[0] : void 0;
|
|
@@ -16555,7 +16889,7 @@ async function governedCommandContext(command, options) {
|
|
|
16555
16889
|
const anticipatedPaths = [];
|
|
16556
16890
|
if (path === "add") anticipatedPaths.push(workspaceConfigPath(workspaceRoot));
|
|
16557
16891
|
if (path === "remember" && options.runtime) {
|
|
16558
|
-
anticipatedPaths.push(
|
|
16892
|
+
anticipatedPaths.push(join46(workspaceRoot, ".agentwheel", "overlays", options.runtime, "instructions.local.md"));
|
|
16559
16893
|
}
|
|
16560
16894
|
if (path === "uninstall" && source) anticipatedPaths.push(workspaceConfigPath(workspaceRoot));
|
|
16561
16895
|
return {
|