agentwheel 0.20.9 → 0.20.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{apply-JBULQGW5.js → apply-ZNEUBJU3.js} +2 -2
- package/dist/{chunk-SHPNLNBR.js → chunk-J7DKPDS7.js} +2 -2
- package/dist/{chunk-E7Z2YB2C.js → chunk-P2JK34W7.js} +2 -1
- package/dist/index.js +460 -129
- package/dist/{transaction-EPP7GHXD.js → transaction-4ERCJXG7.js} +1 -1
- package/openpack.json +1 -1
- package/package.json +1 -1
- package/skills/agentwheel/SKILL.md +1 -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-P2JK34W7.js";
|
|
40
41
|
import {
|
|
41
42
|
CURRENT_WORKSPACE_SCHEMA_VERSION,
|
|
42
43
|
GovernedMutation,
|
|
@@ -97,7 +98,7 @@ import {
|
|
|
97
98
|
workspaceSelectionImportSchema,
|
|
98
99
|
writeApplyJournal,
|
|
99
100
|
writeWorkspaceConfig
|
|
100
|
-
} from "./chunk-
|
|
101
|
+
} from "./chunk-J7DKPDS7.js";
|
|
101
102
|
import {
|
|
102
103
|
inferSourceDriverName
|
|
103
104
|
} from "./chunk-EMDIG24I.js";
|
|
@@ -112,11 +113,11 @@ import {
|
|
|
112
113
|
} from "./chunk-7VPI5J5Y.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 {
|
|
@@ -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 = [];
|
|
@@ -8233,12 +8237,12 @@ async function createGraphSourcePlan(options) {
|
|
|
8233
8237
|
});
|
|
8234
8238
|
return { priorState: priorState2, stableLock: stableLock2, stableSourceLock: stableSourceLock2 };
|
|
8235
8239
|
};
|
|
8236
|
-
const { priorState, stableLock, stableSourceLock } = await readPriorState();
|
|
8240
|
+
const { priorState, stableLock, stableSourceLock } = options.freshGraphOnly ? { priorState: {}, stableLock: void 0, stableSourceLock: void 0 } : await readPriorState();
|
|
8237
8241
|
const previousLock = priorState.graphLock;
|
|
8238
|
-
const registryClient = new RegistryClient({ workspaceRoot, offline: lockMode, offlineLabel: lockLabel, warn });
|
|
8242
|
+
const registryClient = new RegistryClient({ workspaceRoot, cachePath: options.registryCachePath, offline: lockMode, offlineLabel: lockLabel, warn });
|
|
8239
8243
|
const graph = await resolveDependencyGraph(options.roots, {
|
|
8240
8244
|
workspaceRoot,
|
|
8241
|
-
cacheRoot: join33(workspaceRoot, ".agentwheel", "cache"),
|
|
8245
|
+
cacheRoot: options.cacheRoot ?? join33(workspaceRoot, ".agentwheel", "cache"),
|
|
8242
8246
|
registryClient,
|
|
8243
8247
|
noDeps: options.noDeps,
|
|
8244
8248
|
includeSuggestions: options.includeSuggestions,
|
|
@@ -10149,10 +10153,262 @@ function canonicalJson(value) {
|
|
|
10149
10153
|
return `{${Object.keys(record).filter((key) => record[key] !== void 0).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
|
|
10150
10154
|
}
|
|
10151
10155
|
|
|
10156
|
+
// src/lifecycle/legacy-recovery-plan.ts
|
|
10157
|
+
import { createHash as createHash10 } from "crypto";
|
|
10158
|
+
import { readdir as readdir10 } from "fs/promises";
|
|
10159
|
+
import { basename as basename20, dirname as dirname21, join as join39, posix as posix3, relative as relative8, resolve as resolve22 } from "path";
|
|
10160
|
+
async function inventoryHistoricalLocks(fleetRoot) {
|
|
10161
|
+
const root = join39(fleetRoot, ".agentwheel", "locks");
|
|
10162
|
+
const found = [];
|
|
10163
|
+
async function visit(dir) {
|
|
10164
|
+
for (const entry of await readdir10(dir, { withFileTypes: true }).catch((error) => {
|
|
10165
|
+
if (error.code === "ENOENT") return [];
|
|
10166
|
+
throw error;
|
|
10167
|
+
})) {
|
|
10168
|
+
const path = join39(dir, entry.name);
|
|
10169
|
+
if (entry.isDirectory()) await visit(path);
|
|
10170
|
+
else if (entry.isFile() && entry.name.endsWith(".graph-lock.json")) {
|
|
10171
|
+
const lock = await readGraphLock(path);
|
|
10172
|
+
found.push({ path, digest: sha2564(canonicalGraphLockJson(lock)), lock });
|
|
10173
|
+
}
|
|
10174
|
+
}
|
|
10175
|
+
}
|
|
10176
|
+
await visit(root);
|
|
10177
|
+
return found.sort((a, b) => a.path.localeCompare(b.path));
|
|
10178
|
+
}
|
|
10179
|
+
async function planLegacyRecovery(input) {
|
|
10180
|
+
const { graphPlan, installRoot, targetKey: targetKey2, adapter, installationType, historicalLocks, transport } = input;
|
|
10181
|
+
assertCompleteGraphPlan(graphPlan);
|
|
10182
|
+
const binding = {
|
|
10183
|
+
targetLockDir: dirname21(graphPlan.graphLockPath),
|
|
10184
|
+
stableLockPath: graphPlan.graphLockPath,
|
|
10185
|
+
stableStateKey: graphPlan.plan.stateKey,
|
|
10186
|
+
currentFingerprint: graphPlan.targetFingerprint,
|
|
10187
|
+
expectedOwner: workspaceOwnerForRoot(input.workspaceRoot, input.fleetId),
|
|
10188
|
+
installRoot,
|
|
10189
|
+
adapter,
|
|
10190
|
+
installationType,
|
|
10191
|
+
fleetId: input.fleetId,
|
|
10192
|
+
explicitStateKey: input.explicitStateKey,
|
|
10193
|
+
transportKind: transport.kind
|
|
10194
|
+
};
|
|
10195
|
+
const manifests = await listInstallManifests(installRoot, adapter, transport);
|
|
10196
|
+
const paths = /* @__PURE__ */ new Set();
|
|
10197
|
+
const desired = /* @__PURE__ */ new Map();
|
|
10198
|
+
const nodeHashes = new Map(graphPlan.bundle.graphLock.canonical.nodes.map((node) => [node.id, node.sourceHash]));
|
|
10199
|
+
for (const op of graphPlan.plan.operations) {
|
|
10200
|
+
if (!op.desiredHash || op.action === "remove" || op.action === "keep") continue;
|
|
10201
|
+
const path = relative8(installRoot, op.destPath);
|
|
10202
|
+
if (path.startsWith("..") || path === "") throw new Error(`Planner destination escapes target: ${op.destPath}`);
|
|
10203
|
+
paths.add(path);
|
|
10204
|
+
const items = desired.get(path) ?? [];
|
|
10205
|
+
items.push(op);
|
|
10206
|
+
desired.set(path, items);
|
|
10207
|
+
}
|
|
10208
|
+
for (const item of manifests) for (const entry of item.manifest.entries) paths.add(entry.path);
|
|
10209
|
+
const resolutionWarnings = graphPlan.warnings.filter((warning) => !/^skip (?:artifact|dependency) .+ \((?:selected but )?not targeted: runtimes=\[[^\]]+\]\)$/.test(warning));
|
|
10210
|
+
const complete = resolutionWarnings.length === 0;
|
|
10211
|
+
const output = [];
|
|
10212
|
+
for (const path of [...paths].sort()) {
|
|
10213
|
+
const allClaims = manifests.flatMap((item) => item.manifest.entries.filter((entry) => entry.path === path).map((entry) => claimFor(item, entry, historicalLocks, binding)));
|
|
10214
|
+
const claims = allClaims.filter((claim) => claim.scope !== "foreign");
|
|
10215
|
+
const foreignClaims = allClaims.filter((claim) => claim.scope === "foreign");
|
|
10216
|
+
const current = desired.get(path) ?? [];
|
|
10217
|
+
const currentHashes = [...new Set(current.map((item) => item.desiredHash))];
|
|
10218
|
+
const currentSourceHashes = [...new Set(current.map((item) => item.graphNodeId ? nodeHashes.get(item.graphNodeId) : void 0).filter((value) => Boolean(value)))].sort();
|
|
10219
|
+
const currentNodeIds = [...new Set(current.map((item) => item.graphNodeId).filter((value) => Boolean(value)))].sort();
|
|
10220
|
+
const currentSelectors = [...new Set(current.map((item) => item.logicalSelector).filter((value) => Boolean(value)))].sort();
|
|
10221
|
+
const currentOwners = [...new Set(current.flatMap((item) => item.owners ?? []))].sort();
|
|
10222
|
+
const livePath = resolve22(installRoot, path);
|
|
10223
|
+
if (relative8(installRoot, livePath).startsWith("..")) throw new Error(`Manifest path escapes target: ${path}`);
|
|
10224
|
+
const liveHash = await transport.pathExists(livePath) ? await transport.hashPath(livePath) : null;
|
|
10225
|
+
const reasons = [];
|
|
10226
|
+
const currentResults = await Promise.all(current.map((op) => verifyCurrentOperation(op, liveHash, transport)));
|
|
10227
|
+
const currentGraphStatus = current.length === 0 ? "absent" : currentResults.every((result) => result === "exact-match") ? "exact-match" : currentResults.some((result) => result === "unverifiable") ? "unverifiable" : "mismatch";
|
|
10228
|
+
if (currentHashes.length > 1 && current.some((op) => !op.mergeStrategy && op.mode !== "managed-block")) {
|
|
10229
|
+
reasons.push("current graph has divergent hashes for the destination");
|
|
10230
|
+
}
|
|
10231
|
+
if (currentGraphStatus === "mismatch") reasons.push("live contribution differs from current source");
|
|
10232
|
+
if (currentGraphStatus === "unverifiable") reasons.push("current contribution could not be verified");
|
|
10233
|
+
if (current.some((op) => op.action === "conflict" || op.action === "drift")) reasons.push("install planner has a blocking operation");
|
|
10234
|
+
if (!complete) reasons.push("current source graph has resolution warnings; completeness unproven");
|
|
10235
|
+
if (claims.some((claim) => claim.scope === "unbound" || claim.evidence.length)) reasons.push("historical provenance unresolved");
|
|
10236
|
+
if (foreignClaims.length) reasons.push("foreign manifest claim conflicts with destination");
|
|
10237
|
+
if (new Set(claims.map((claim) => `${claim.owner}:${claim.hash}:${claim.sourceHash}:${claim.graphLockDigest ?? ""}`)).size > 1) {
|
|
10238
|
+
reasons.push("competing same-target claims diverge");
|
|
10239
|
+
}
|
|
10240
|
+
if (current.length === 0 && claims.length === 0) reasons.push("no same-target historical claim");
|
|
10241
|
+
if (current.length === 0) {
|
|
10242
|
+
for (const claim of claims) {
|
|
10243
|
+
if (claim.scope !== "same-target") continue;
|
|
10244
|
+
const item = manifests.find((manifest) => manifest.path === claim.manifestPath);
|
|
10245
|
+
const entry = item.manifest.entries.find((candidate) => candidate.path === path && candidate.sourceHash === claim.sourceHash);
|
|
10246
|
+
if (!await verifyHistoricalEntry(entry, livePath, liveHash, transport)) {
|
|
10247
|
+
reasons.push("live contribution differs from same-target historical claim");
|
|
10248
|
+
break;
|
|
10249
|
+
}
|
|
10250
|
+
}
|
|
10251
|
+
}
|
|
10252
|
+
const category = reasons.length ? "unresolved" : currentGraphStatus === "exact-match" ? "current-graph-exact-match" : "retirement-candidate";
|
|
10253
|
+
output.push({
|
|
10254
|
+
path,
|
|
10255
|
+
category,
|
|
10256
|
+
reasons,
|
|
10257
|
+
currentHash: currentHashes.length === 1 ? currentHashes[0] : null,
|
|
10258
|
+
currentHashes: currentHashes.sort(),
|
|
10259
|
+
currentSourceHashes,
|
|
10260
|
+
currentNodeIds,
|
|
10261
|
+
currentSelectors,
|
|
10262
|
+
currentOwners,
|
|
10263
|
+
currentGraphStatus,
|
|
10264
|
+
liveHash,
|
|
10265
|
+
claims,
|
|
10266
|
+
foreignClaims
|
|
10267
|
+
});
|
|
10268
|
+
}
|
|
10269
|
+
const counts = { "current-graph-exact-match": 0, "retirement-candidate": 0, unresolved: 0 };
|
|
10270
|
+
const currentGraphCounts = { "exact-match": 0, mismatch: 0, absent: 0, unverifiable: 0 };
|
|
10271
|
+
for (const path of output) counts[path.category]++;
|
|
10272
|
+
for (const path of output) currentGraphCounts[path.currentGraphStatus]++;
|
|
10273
|
+
return {
|
|
10274
|
+
targetRoot: installRoot,
|
|
10275
|
+
targetKey: targetKey2,
|
|
10276
|
+
adapter,
|
|
10277
|
+
installationType,
|
|
10278
|
+
currentGraphLockDigest: graphPlan.graphLockDigest,
|
|
10279
|
+
roots: graphPlan.graph.roots.map((root) => ({ rootId: root.rootId, source: root.source, graphNodeId: root.graphNodeId, selected: root.selected })),
|
|
10280
|
+
resolutionWarnings,
|
|
10281
|
+
complete,
|
|
10282
|
+
paths: output,
|
|
10283
|
+
counts,
|
|
10284
|
+
currentGraphCounts
|
|
10285
|
+
};
|
|
10286
|
+
}
|
|
10287
|
+
function claimFor(item, entry, locks, binding) {
|
|
10288
|
+
const digest2 = "graphLockDigest" in entry ? entry.graphLockDigest : void 0;
|
|
10289
|
+
const nodeId = "graphNodeId" in entry ? entry.graphNodeId : void 0;
|
|
10290
|
+
const selector = "logicalSelector" in entry ? entry.logicalSelector : void 0;
|
|
10291
|
+
const matches = locks.filter((candidate) => candidate.digest === digest2);
|
|
10292
|
+
const evidence = [];
|
|
10293
|
+
const owner = "workspaceOwner" in entry ? entry.workspaceOwner : legacyUnownedWorkspaceOwner;
|
|
10294
|
+
const sameRoot = binding.transportKind === "ssh" ? posix3.normalize(item.manifest.targetRoot) === posix3.normalize(binding.installRoot) : resolve22(item.manifest.targetRoot) === resolve22(binding.installRoot);
|
|
10295
|
+
const inTargetDir = matches.filter((candidate) => resolve22(dirname21(candidate.path)) === resolve22(binding.targetLockDir));
|
|
10296
|
+
const boundLocks = inTargetDir.filter((candidate) => lockBindsState(candidate, item, binding));
|
|
10297
|
+
if (owner !== binding.expectedOwner) evidence.push("workspace owner differs from selected Fleet target");
|
|
10298
|
+
if (item.manifest.version !== 2 || item.manifest.installationType !== binding.installationType) evidence.push("installation type differs or manifest is legacy v1");
|
|
10299
|
+
if (!sameRoot) evidence.push("manifest target root differs from selected Fleet target");
|
|
10300
|
+
if (item.manifest.version === 2 && (item.manifest.stateKey !== item.stateKey || item.manifest.adapter !== binding.adapter)) {
|
|
10301
|
+
evidence.push("manifest state key or adapter differs from filename");
|
|
10302
|
+
}
|
|
10303
|
+
if (!digest2) evidence.push("historical graph-lock digest missing");
|
|
10304
|
+
else if (matches.length === 0) evidence.push("historical graph-lock digest has no matching lock");
|
|
10305
|
+
if (matches.length && !inTargetDir.length) evidence.push("claim belongs to another Fleet target graph");
|
|
10306
|
+
if (inTargetDir.length && !boundLocks.length) evidence.push("graph-lock fingerprint, filename, or state key does not bind claim");
|
|
10307
|
+
if (boundLocks.length && !boundLocks.some((candidate) => candidate.lock.canonical.artifacts.some((artifact) => artifact.graphNodeId === nodeId && artifact.logicalSelector === selector))) {
|
|
10308
|
+
evidence.push("historical graph-lock selector/node does not cover claim");
|
|
10309
|
+
} else if (boundLocks.length && !boundLocks.some((candidate) => candidate.lock.canonical.artifacts.some((artifact) => artifact.graphNodeId === nodeId && artifact.logicalSelector === selector && artifact.hash === entry.sourceHash))) {
|
|
10310
|
+
evidence.push("historical graph-lock artifact hash differs from manifest source hash");
|
|
10311
|
+
}
|
|
10312
|
+
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;
|
|
10313
|
+
const scope = foreign ? "foreign" : boundLocks.length && evidence.length === 0 ? "same-target" : "unbound";
|
|
10314
|
+
return {
|
|
10315
|
+
scope,
|
|
10316
|
+
manifestPath: item.path,
|
|
10317
|
+
stateKey: item.stateKey,
|
|
10318
|
+
manifestStateKey: item.manifest.version === 2 ? item.manifest.stateKey : void 0,
|
|
10319
|
+
installationType: item.manifest.version === 2 ? item.manifest.installationType : void 0,
|
|
10320
|
+
targetRoot: item.manifest.targetRoot,
|
|
10321
|
+
owner,
|
|
10322
|
+
hash: entry.hash,
|
|
10323
|
+
sourceHash: entry.sourceHash,
|
|
10324
|
+
graphLockDigest: digest2,
|
|
10325
|
+
graphNodeId: nodeId,
|
|
10326
|
+
logicalSelector: selector,
|
|
10327
|
+
owners: "owners" in entry ? entry.owners : [entry.packageName ?? "legacy"],
|
|
10328
|
+
lockPaths: matches.map((candidate) => candidate.path),
|
|
10329
|
+
evidence
|
|
10330
|
+
};
|
|
10331
|
+
}
|
|
10332
|
+
function lockBindsState(candidate, manifest, binding) {
|
|
10333
|
+
const name = basename20(candidate.path);
|
|
10334
|
+
if (!name.endsWith(".graph-lock.json")) return false;
|
|
10335
|
+
const fingerprint = name.slice(0, -".graph-lock.json".length);
|
|
10336
|
+
if (!/^[a-f0-9]{64}$/u.test(fingerprint)) return false;
|
|
10337
|
+
const stable = resolve22(candidate.path) === resolve22(binding.stableLockPath);
|
|
10338
|
+
if (stable) {
|
|
10339
|
+
return manifest.stateKey === binding.stableStateKey && candidate.lock.canonical.targetFingerprint === binding.currentFingerprint;
|
|
10340
|
+
}
|
|
10341
|
+
return candidate.lock.canonical.targetFingerprint === fingerprint && manifest.stateKey === stateKeyFor(binding.adapter, {
|
|
10342
|
+
installationType: binding.installationType,
|
|
10343
|
+
stateKey: binding.explicitStateKey,
|
|
10344
|
+
targetFingerprint: fingerprint,
|
|
10345
|
+
fleetId: binding.fleetId
|
|
10346
|
+
});
|
|
10347
|
+
}
|
|
10348
|
+
async function verifyCurrentOperation(op, liveHash, transport) {
|
|
10349
|
+
if (liveHash === null || !op.desiredHash) return "mismatch";
|
|
10350
|
+
if (op.semanticPlugin || op.programmaticOperation) return "unverifiable";
|
|
10351
|
+
if (op.mode === "managed-block") {
|
|
10352
|
+
const selector = managedInstructionSelector(op.logicalSelector, op.artifactType, op.artifactName);
|
|
10353
|
+
const state = await readManagedInstructionBlockState(op.destPath, selector, transport);
|
|
10354
|
+
return state.hasBlock && !state.drifted && state.hash === op.desiredHash ? "exact-match" : "mismatch";
|
|
10355
|
+
}
|
|
10356
|
+
if (op.mergeStrategy) {
|
|
10357
|
+
if (!op.sourcePath) return "unverifiable";
|
|
10358
|
+
try {
|
|
10359
|
+
await assertMergedSourceContribution(op.sourcePath, op.mergeStrategy, await transport.readFile(op.destPath));
|
|
10360
|
+
return "exact-match";
|
|
10361
|
+
} catch {
|
|
10362
|
+
return "mismatch";
|
|
10363
|
+
}
|
|
10364
|
+
}
|
|
10365
|
+
return liveHash === op.desiredHash ? "exact-match" : "mismatch";
|
|
10366
|
+
}
|
|
10367
|
+
async function verifyHistoricalEntry(entry, livePath, liveHash, transport) {
|
|
10368
|
+
if (liveHash === null || entry.semanticPlugin) return false;
|
|
10369
|
+
if (entry.mode === "managed-block") {
|
|
10370
|
+
const selector = managedInstructionSelector(
|
|
10371
|
+
"logicalSelector" in entry ? entry.logicalSelector : void 0,
|
|
10372
|
+
entry.artifactType,
|
|
10373
|
+
entry.artifactName
|
|
10374
|
+
);
|
|
10375
|
+
const state = await readManagedInstructionBlockState(livePath, selector, transport);
|
|
10376
|
+
return state.hasBlock && !state.drifted && state.hash === entry.hash;
|
|
10377
|
+
}
|
|
10378
|
+
if (entry.mergeStrategy) {
|
|
10379
|
+
if (!hasMergeRemovalContent(entry.mergeRemoval)) return false;
|
|
10380
|
+
try {
|
|
10381
|
+
assertExactMergeContribution(entry.mergeRemoval, entry.mergeStrategy, await transport.readFile(livePath));
|
|
10382
|
+
return true;
|
|
10383
|
+
} catch {
|
|
10384
|
+
return false;
|
|
10385
|
+
}
|
|
10386
|
+
}
|
|
10387
|
+
return entry.hash === liveHash;
|
|
10388
|
+
}
|
|
10389
|
+
function assertCompleteGraphPlan(plan) {
|
|
10390
|
+
const sourceNodes = plan.graph.nodes.filter((node) => node.selected.length > 0);
|
|
10391
|
+
const lockedNodes = new Map(plan.bundle.graphLock.canonical.nodes.map((node) => [node.id, node]));
|
|
10392
|
+
for (const node of sourceNodes) {
|
|
10393
|
+
const locked = lockedNodes.get(node.id);
|
|
10394
|
+
if (!locked || locked.sourceHash !== node.sourceHash || node.selected.some((selector) => !locked.selected.includes(selector))) {
|
|
10395
|
+
throw new Error(`Current source graph omitted selected node or dependency: ${node.id}`);
|
|
10396
|
+
}
|
|
10397
|
+
}
|
|
10398
|
+
for (const edge of plan.graph.edges) {
|
|
10399
|
+
if (!plan.bundle.graphLock.canonical.edges.some((locked) => locked.from === edge.from && locked.to === edge.to && locked.alias === edge.alias)) {
|
|
10400
|
+
throw new Error(`Current source graph omitted dependency edge: ${edge.from} -> ${edge.to}`);
|
|
10401
|
+
}
|
|
10402
|
+
}
|
|
10403
|
+
}
|
|
10404
|
+
function sha2564(value) {
|
|
10405
|
+
return createHash10("sha256").update(value).digest("hex");
|
|
10406
|
+
}
|
|
10407
|
+
|
|
10152
10408
|
// src/version/policy.ts
|
|
10153
10409
|
import { execFile as execFile3 } from "child_process";
|
|
10154
10410
|
import { readFile as readFile23 } from "fs/promises";
|
|
10155
|
-
import { join as
|
|
10411
|
+
import { join as join40, resolve as resolve23 } from "path";
|
|
10156
10412
|
import { promisify as promisify3 } from "util";
|
|
10157
10413
|
import { parse as parseJsonc } from "jsonc-parser";
|
|
10158
10414
|
import { z as z5 } from "zod";
|
|
@@ -10246,7 +10502,7 @@ async function discoverVersionsFromSource(pkg, workspaceRoot) {
|
|
|
10246
10502
|
if (tagged.length > 0) return tagged;
|
|
10247
10503
|
}
|
|
10248
10504
|
if (driverName === "local") {
|
|
10249
|
-
const root =
|
|
10505
|
+
const root = resolve23(workspaceRoot, pkg.source);
|
|
10250
10506
|
const manifest2 = await readPackageManifest(root);
|
|
10251
10507
|
const current = manifest2 ? [{ version: manifest2.version, ref: pkg.requestedRef ?? root }] : [];
|
|
10252
10508
|
try {
|
|
@@ -10261,7 +10517,7 @@ async function discoverVersionsFromSource(pkg, workspaceRoot) {
|
|
|
10261
10517
|
}
|
|
10262
10518
|
const driver = getSourceDriver(driverName);
|
|
10263
10519
|
const resolved = await driver.resolve(pkg.source, {
|
|
10264
|
-
cacheRoot:
|
|
10520
|
+
cacheRoot: join40(workspaceRoot, ".agentwheel", "cache"),
|
|
10265
10521
|
mode: "tracking",
|
|
10266
10522
|
ref: pkg.requestedRef
|
|
10267
10523
|
});
|
|
@@ -10355,7 +10611,7 @@ function gitUrlFromSource(source) {
|
|
|
10355
10611
|
throw new Error(`Version discovery does not support Git source: ${source}`);
|
|
10356
10612
|
}
|
|
10357
10613
|
function versionCachePath(workspaceRoot) {
|
|
10358
|
-
return
|
|
10614
|
+
return join40(workspaceRoot, ".agentwheel", "cache", "version-index.json");
|
|
10359
10615
|
}
|
|
10360
10616
|
async function readVersionCache(path) {
|
|
10361
10617
|
if (!await pathExists(path)) return { schemaVersion: 1, sources: {} };
|
|
@@ -10369,7 +10625,7 @@ async function readVersionCache(path) {
|
|
|
10369
10625
|
// src/profile/members.ts
|
|
10370
10626
|
import { execFile as execFile4 } from "child_process";
|
|
10371
10627
|
import { readFile as readFile24 } from "fs/promises";
|
|
10372
|
-
import { join as
|
|
10628
|
+
import { join as join41, resolve as resolve24 } from "path";
|
|
10373
10629
|
import { promisify as promisify4 } from "util";
|
|
10374
10630
|
import { z as z7 } from "zod";
|
|
10375
10631
|
|
|
@@ -10544,7 +10800,7 @@ async function invokeMemberStatus(member, parentWorkspace, chain, options, cliEn
|
|
|
10544
10800
|
let stderr = "";
|
|
10545
10801
|
try {
|
|
10546
10802
|
if (member.transport === "local") {
|
|
10547
|
-
const workspace =
|
|
10803
|
+
const workspace = resolve24(parentWorkspace, member.workspace);
|
|
10548
10804
|
const result = await execFileAsync4(process.execPath, [cliEntry, ...args], {
|
|
10549
10805
|
cwd: workspace,
|
|
10550
10806
|
env: env2,
|
|
@@ -10590,7 +10846,7 @@ async function runMemberAgentwheel(member, parentWorkspace, args, chain) {
|
|
|
10590
10846
|
process.execPath,
|
|
10591
10847
|
[process.argv[1], ...memberCommandArgs(member, args)],
|
|
10592
10848
|
{
|
|
10593
|
-
cwd:
|
|
10849
|
+
cwd: resolve24(parentWorkspace, member.workspace),
|
|
10594
10850
|
env: env2,
|
|
10595
10851
|
maxBuffer: 20 * 1024 * 1024
|
|
10596
10852
|
}
|
|
@@ -10670,7 +10926,7 @@ function memberFailure(member, health, error) {
|
|
|
10670
10926
|
};
|
|
10671
10927
|
}
|
|
10672
10928
|
function memberCachePath(workspaceRoot, profileName, memberId) {
|
|
10673
|
-
return
|
|
10929
|
+
return join41(workspaceRoot, ".agentwheel", "cache", "member-status", profileName, `${memberId}.json`);
|
|
10674
10930
|
}
|
|
10675
10931
|
async function readMemberCache(path) {
|
|
10676
10932
|
if (!await pathExists(path)) return void 0;
|
|
@@ -10696,7 +10952,7 @@ function assertNoCompositeCycle(workspaceRoot, profileName, chain) {
|
|
|
10696
10952
|
}
|
|
10697
10953
|
}
|
|
10698
10954
|
function compositeKey(workspaceRoot, profileName) {
|
|
10699
|
-
return `${
|
|
10955
|
+
return `${resolve24(workspaceRoot)}#${profileName}`;
|
|
10700
10956
|
}
|
|
10701
10957
|
|
|
10702
10958
|
// src/status/repository.ts
|
|
@@ -10743,10 +10999,10 @@ function valueAfter(lines, prefix) {
|
|
|
10743
10999
|
}
|
|
10744
11000
|
|
|
10745
11001
|
// src/catalogue/client.ts
|
|
10746
|
-
import { createHash as
|
|
11002
|
+
import { createHash as createHash11 } from "crypto";
|
|
10747
11003
|
import { readFile as readFile25, rm as rm8 } from "fs/promises";
|
|
10748
11004
|
import { homedir as homedir9 } from "os";
|
|
10749
|
-
import { join as
|
|
11005
|
+
import { join as join42 } from "path";
|
|
10750
11006
|
|
|
10751
11007
|
// src/model/catalogue.ts
|
|
10752
11008
|
import { z as z8 } from "zod";
|
|
@@ -11018,18 +11274,18 @@ var CatalogueClient = class {
|
|
|
11018
11274
|
}
|
|
11019
11275
|
return {
|
|
11020
11276
|
value: schema.parse(value),
|
|
11021
|
-
digest:
|
|
11277
|
+
digest: createHash11("sha256").update(bytes).digest("hex")
|
|
11022
11278
|
};
|
|
11023
11279
|
}
|
|
11024
11280
|
};
|
|
11025
11281
|
function defaultCatalogueCachePath() {
|
|
11026
|
-
return
|
|
11282
|
+
return join42(homedir9(), ".agentwheel", "catalogue-cache.json");
|
|
11027
11283
|
}
|
|
11028
11284
|
function sameSources2(a, b) {
|
|
11029
11285
|
return a.length === b.length && a.every((source, index) => source === b[index]);
|
|
11030
11286
|
}
|
|
11031
11287
|
function catalogueContentHash(enriched, vercel) {
|
|
11032
|
-
return
|
|
11288
|
+
return createHash11("sha256").update(JSON.stringify({ enriched, vercel })).digest("hex");
|
|
11033
11289
|
}
|
|
11034
11290
|
|
|
11035
11291
|
// src/search/index.ts
|
|
@@ -11391,9 +11647,9 @@ function includesToken(normalizedText, token) {
|
|
|
11391
11647
|
}
|
|
11392
11648
|
|
|
11393
11649
|
// src/semantic/index.ts
|
|
11394
|
-
import { createHash as
|
|
11650
|
+
import { createHash as createHash12 } from "crypto";
|
|
11395
11651
|
import { homedir as homedir10 } from "os";
|
|
11396
|
-
import { join as
|
|
11652
|
+
import { join as join43 } from "path";
|
|
11397
11653
|
import { env, pipeline } from "@huggingface/transformers";
|
|
11398
11654
|
var DEFAULT_SEMANTIC_INDEX_URL = "https://raw.githubusercontent.com/NestDevLab/agentwheel-registry/main/catalogue-semantic-index/gte-v1/";
|
|
11399
11655
|
var CONTRACT = {
|
|
@@ -11481,13 +11737,13 @@ var SemanticSearchClient = class {
|
|
|
11481
11737
|
if (!response.ok) throw new Error(`Semantic index file failed (${response.status}): ${descriptor.path}`);
|
|
11482
11738
|
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
11483
11739
|
if (bytes.byteLength !== descriptor.bytes) throw new Error(`Semantic index file size does not match: ${descriptor.path}`);
|
|
11484
|
-
const digest2 =
|
|
11740
|
+
const digest2 = createHash12("sha256").update(bytes).digest("hex");
|
|
11485
11741
|
if (digest2 !== descriptor.sha256) throw new Error(`Semantic index checksum does not match: ${descriptor.path}`);
|
|
11486
11742
|
return bytes;
|
|
11487
11743
|
}
|
|
11488
11744
|
};
|
|
11489
11745
|
async function embedQuery(query) {
|
|
11490
|
-
env.cacheDir =
|
|
11746
|
+
env.cacheDir = join43(homedir10(), ".agentwheel", "semantic-models");
|
|
11491
11747
|
const extractor = await pipeline("feature-extraction", CONTRACT.model.id, {
|
|
11492
11748
|
revision: CONTRACT.model.revision,
|
|
11493
11749
|
dtype: CONTRACT.model.dtype,
|
|
@@ -11571,9 +11827,9 @@ function ensureTrailingSlash(value) {
|
|
|
11571
11827
|
}
|
|
11572
11828
|
|
|
11573
11829
|
// src/trial/skill.ts
|
|
11574
|
-
import { createHash as
|
|
11830
|
+
import { createHash as createHash13 } from "crypto";
|
|
11575
11831
|
import { readFile as readFile26, stat as stat13 } from "fs/promises";
|
|
11576
|
-
import { join as
|
|
11832
|
+
import { join as join44 } from "path";
|
|
11577
11833
|
import { parse as parseYaml2 } from "yaml";
|
|
11578
11834
|
var MAX_TRIAL_SKILL_BYTES = 512 * 1024;
|
|
11579
11835
|
async function createSkillTrial(driver, resolved, selectors) {
|
|
@@ -11585,7 +11841,7 @@ async function createSkillTrial(driver, resolved, selectors) {
|
|
|
11585
11841
|
throw new Error("Skill trial requires exactly one selected skill. Use --skill <name> or --select skills/<name>.");
|
|
11586
11842
|
}
|
|
11587
11843
|
const artifact = skills[0];
|
|
11588
|
-
const path = artifact.kind === "dir" ?
|
|
11844
|
+
const path = artifact.kind === "dir" ? join44(artifact.sourcePath, "SKILL.md") : artifact.sourcePath;
|
|
11589
11845
|
const info = await stat13(path);
|
|
11590
11846
|
if (info.size > MAX_TRIAL_SKILL_BYTES) {
|
|
11591
11847
|
throw new Error(`Skill trial exceeds the ${MAX_TRIAL_SKILL_BYTES / 1024} KiB content limit.`);
|
|
@@ -11601,7 +11857,7 @@ async function createSkillTrial(driver, resolved, selectors) {
|
|
|
11601
11857
|
skill: {
|
|
11602
11858
|
name: artifact.name,
|
|
11603
11859
|
relativePath: artifact.relativePath,
|
|
11604
|
-
sha256:
|
|
11860
|
+
sha256: createHash13("sha256").update(content).digest("hex"),
|
|
11605
11861
|
frontmatter,
|
|
11606
11862
|
content
|
|
11607
11863
|
}
|
|
@@ -11620,10 +11876,10 @@ function readSkillFrontmatter(content, artifact) {
|
|
|
11620
11876
|
}
|
|
11621
11877
|
|
|
11622
11878
|
// src/lifecycle/fleet-normalize.ts
|
|
11623
|
-
import { createHash as
|
|
11624
|
-
import { readFile as readFile27, readdir as
|
|
11879
|
+
import { createHash as createHash14 } from "crypto";
|
|
11880
|
+
import { readFile as readFile27, readdir as readdir11, rm as rm9 } from "fs/promises";
|
|
11625
11881
|
import { homedir as homedir11 } from "os";
|
|
11626
|
-
import { dirname as
|
|
11882
|
+
import { dirname as dirname22, isAbsolute as isAbsolute3, join as join45, relative as relative9, resolve as resolve25 } from "path";
|
|
11627
11883
|
async function planFleetNormalization(request) {
|
|
11628
11884
|
const normalizedRequest = normalizeRequest2(request);
|
|
11629
11885
|
const destinationScope = await resolveWorkspaceScope({
|
|
@@ -11653,7 +11909,7 @@ async function planFleetNormalization(request) {
|
|
|
11653
11909
|
`Package '${name}' has divergent declarations. Source, driver, adapter, adapter configuration, installation type, version policy, selections, aliases, overrides, and suggestions must match before normalization.`
|
|
11654
11910
|
);
|
|
11655
11911
|
}
|
|
11656
|
-
packages.push({ name, declarationDigest:
|
|
11912
|
+
packages.push({ name, declarationDigest: sha2565(sourceDeclaration) });
|
|
11657
11913
|
}
|
|
11658
11914
|
if (!legacySelfNormalization) await assertSourceFleetPostcondition(normalizedRequest, sourceScope, candidates);
|
|
11659
11915
|
if ((normalizedRequest.profile || normalizedRequest.agent || normalizedRequest.artifacts || normalizedRequest.orphanedOwnerRoots) && !legacySelfNormalization) {
|
|
@@ -11684,7 +11940,7 @@ async function planFleetNormalization(request) {
|
|
|
11684
11940
|
packages,
|
|
11685
11941
|
installedState
|
|
11686
11942
|
};
|
|
11687
|
-
return { ...planWithoutDigest, planDigest:
|
|
11943
|
+
return { ...planWithoutDigest, planDigest: sha2565(canonicalJson2(planWithoutDigest)) };
|
|
11688
11944
|
}
|
|
11689
11945
|
async function applyFleetNormalization(request, hooks = {}) {
|
|
11690
11946
|
if (!request.apply) throw new Error("Fleet normalization is dry-run by default; pass --apply with --plan-digest <digest>.");
|
|
@@ -11822,7 +12078,7 @@ async function assertNormalizationPostconditions(journal) {
|
|
|
11822
12078
|
}
|
|
11823
12079
|
for (const graphLock of journal.graphLocks) {
|
|
11824
12080
|
const raw = await readOptionalRecord(graphLock.path);
|
|
11825
|
-
const digest2 = raw ?
|
|
12081
|
+
const digest2 = raw ? sha2565(canonicalJson2(await readGraphLock(graphLock.path))) : null;
|
|
11826
12082
|
if (digest2 !== graphLock.afterDigest) {
|
|
11827
12083
|
throw new Error(`Fleet normalization graph-lock postcheck failed: ${graphLock.path}`);
|
|
11828
12084
|
}
|
|
@@ -11894,17 +12150,17 @@ function normalizeRequest2(request) {
|
|
|
11894
12150
|
...profile ? { profile } : {},
|
|
11895
12151
|
...agent ? { agent } : {},
|
|
11896
12152
|
...orphanedOwnerRoots ? { orphanedOwnerRoots } : {},
|
|
11897
|
-
...request.globalRoot ? { globalRoot:
|
|
12153
|
+
...request.globalRoot ? { globalRoot: resolve25(request.globalRoot) } : {}
|
|
11898
12154
|
};
|
|
11899
12155
|
}
|
|
11900
12156
|
function normalizeOrphanedOwnerRoot(value) {
|
|
11901
12157
|
const root = value.trim();
|
|
11902
12158
|
if (!root || !isAbsolute3(root)) throw new Error(`Invalid orphaned owner root '${value}'; expected an absolute workspace path.`);
|
|
11903
|
-
return
|
|
12159
|
+
return resolve25(root);
|
|
11904
12160
|
}
|
|
11905
12161
|
async function normalizationRoots(request) {
|
|
11906
12162
|
const destination = await showRegisteredFleet(request.destinationFleet, { globalRoot: request.globalRoot });
|
|
11907
|
-
const source = request.from === "user" ?
|
|
12163
|
+
const source = request.from === "user" ? resolve25(request.globalRoot ?? homedir11()) : (await showRegisteredFleet(request.from.slice("fleet:".length), { globalRoot: request.globalRoot })).root;
|
|
11908
12164
|
return { source, destination: destination.root };
|
|
11909
12165
|
}
|
|
11910
12166
|
async function withOrderedLocks(roots, fn) {
|
|
@@ -11912,7 +12168,7 @@ async function withOrderedLocks(roots, fn) {
|
|
|
11912
12168
|
if (index === roots.length) return fn();
|
|
11913
12169
|
const root = roots[index];
|
|
11914
12170
|
return withFilesystemLock(
|
|
11915
|
-
|
|
12171
|
+
join45(root, ".agentwheel", "fleet-normalize.lock"),
|
|
11916
12172
|
5e3,
|
|
11917
12173
|
() => run(index + 1),
|
|
11918
12174
|
"fleet normalization"
|
|
@@ -12011,10 +12267,10 @@ async function inspectInstalledState(source, destination, packageNames) {
|
|
|
12011
12267
|
const destinationGraphAfter = destinationGraph?.lock ?? destinationGraphFromSource(sourceGraph.lock, destinationState.targetFingerprint);
|
|
12012
12268
|
graphTransfers.push({
|
|
12013
12269
|
sourceGraphLockPath: sourceGraph.path,
|
|
12014
|
-
sourceGraphLockDigest:
|
|
12270
|
+
sourceGraphLockDigest: sha2565(canonicalJson2(sourceGraph.lock)),
|
|
12015
12271
|
destinationGraphLockPath: destinationState.graphLockPath,
|
|
12016
|
-
destinationGraphLockDigest: destinationGraph ?
|
|
12017
|
-
destinationGraphLockAfterDigest:
|
|
12272
|
+
destinationGraphLockDigest: destinationGraph ? sha2565(canonicalJson2(destinationGraph.lock)) : null,
|
|
12273
|
+
destinationGraphLockAfterDigest: sha2565(canonicalJson2(destinationGraphAfter)),
|
|
12018
12274
|
targetKey: sourceGraph.targetKey,
|
|
12019
12275
|
adapter: destinationState.adapter,
|
|
12020
12276
|
targetFingerprint: destinationState.targetFingerprint
|
|
@@ -12118,7 +12374,7 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
12118
12374
|
let normalizedGraphCount = 0;
|
|
12119
12375
|
for (const graph of graphCandidates) {
|
|
12120
12376
|
const destinationState = await targetStateForGraph(fleet, graph, fleet.fleetId);
|
|
12121
|
-
if (
|
|
12377
|
+
if (resolve25(destinationState.graphLockPath) === resolve25(graph.path)) {
|
|
12122
12378
|
const destinationManifest = (await collectManifestPaths([destinationState.manifestPath]))[0];
|
|
12123
12379
|
if (destinationManifest?.manifest.entries.some((entry) => entry.workspaceOwner === fleetOwner && entryMatchesPackages(entry, selected))) {
|
|
12124
12380
|
assertCorrelatedGraphManifest(
|
|
@@ -12133,7 +12389,7 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
12133
12389
|
}
|
|
12134
12390
|
}
|
|
12135
12391
|
const legacyState = await legacyTargetStateForGraph(fleet, graph);
|
|
12136
|
-
if (
|
|
12392
|
+
if (resolve25(legacyState.graphLockPath) !== resolve25(graph.path)) {
|
|
12137
12393
|
throw new Error(`Legacy graph lock target identity is stale or noncanonical: ${graph.path}`);
|
|
12138
12394
|
}
|
|
12139
12395
|
const legacyManifest = (await collectManifestPaths([legacyState.manifestPath]))[0];
|
|
@@ -12241,7 +12497,7 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
12241
12497
|
)
|
|
12242
12498
|
});
|
|
12243
12499
|
if (!selectedArtifacts) {
|
|
12244
|
-
const fullDigest =
|
|
12500
|
+
const fullDigest = sha2565(canonicalJson2(graph.lock));
|
|
12245
12501
|
const existingDestinationGraph = graphCandidates.find((candidate) => candidate.path === destinationState.graphLockPath);
|
|
12246
12502
|
if (existingDestinationGraph) assertGraphIsSubset(graph, existingDestinationGraph);
|
|
12247
12503
|
const destinationGraph = destinationGraphFromSource(graph.lock, destinationState.targetFingerprint);
|
|
@@ -12249,8 +12505,8 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
12249
12505
|
sourceGraphLockPath: graph.path,
|
|
12250
12506
|
sourceGraphLockDigest: fullDigest,
|
|
12251
12507
|
destinationGraphLockPath: destinationState.graphLockPath,
|
|
12252
|
-
destinationGraphLockDigest: existingDestinationGraph ?
|
|
12253
|
-
destinationGraphLockAfterDigest:
|
|
12508
|
+
destinationGraphLockDigest: existingDestinationGraph ? sha2565(canonicalJson2(existingDestinationGraph.lock)) : null,
|
|
12509
|
+
destinationGraphLockAfterDigest: sha2565(canonicalJson2(destinationGraph)),
|
|
12254
12510
|
targetKey: graph.targetKey,
|
|
12255
12511
|
adapter: graph.adapter,
|
|
12256
12512
|
targetFingerprint: destinationState.targetFingerprint
|
|
@@ -12308,7 +12564,7 @@ function targetKeysForAgent(scope, name) {
|
|
|
12308
12564
|
async function runtimeRoots(scope) {
|
|
12309
12565
|
const roots = /* @__PURE__ */ new Set([scope.root]);
|
|
12310
12566
|
if (scope.config.packages.some((pkg) => pkg.installationType === "user") || Object.values(scope.config.agents).some((agent) => agent.installationType === "user")) {
|
|
12311
|
-
roots.add(
|
|
12567
|
+
roots.add(resolve25(process.env.AGENTWHEEL_TEST_HOME || process.env.HOME || homedir11()));
|
|
12312
12568
|
}
|
|
12313
12569
|
for (const [name, agent] of Object.entries(scope.config.agents)) {
|
|
12314
12570
|
if (agent.transport === "ssh") throw new Error(`Installed-state normalization cannot inspect SSH agent '${name}' locally.`);
|
|
@@ -12326,17 +12582,17 @@ async function runtimeRoots(scope) {
|
|
|
12326
12582
|
async function collectManifests(roots) {
|
|
12327
12583
|
const found = /* @__PURE__ */ new Map();
|
|
12328
12584
|
for (const root of roots) {
|
|
12329
|
-
const dir =
|
|
12585
|
+
const dir = join45(root, ".agentwheel");
|
|
12330
12586
|
let names;
|
|
12331
12587
|
try {
|
|
12332
|
-
names = await
|
|
12588
|
+
names = await readdir11(dir);
|
|
12333
12589
|
} catch (error) {
|
|
12334
12590
|
if (isMissing(error)) continue;
|
|
12335
12591
|
throw error;
|
|
12336
12592
|
}
|
|
12337
12593
|
for (const name of names.sort((a, b) => a.localeCompare(b))) {
|
|
12338
12594
|
if (!name.endsWith(".install-manifest.json")) continue;
|
|
12339
|
-
const path =
|
|
12595
|
+
const path = join45(dir, name);
|
|
12340
12596
|
const raw = JSON.parse(await readFile27(path, "utf8"));
|
|
12341
12597
|
const parsed = installManifestSchema.parse(raw);
|
|
12342
12598
|
if (parsed.version !== 2) {
|
|
@@ -12345,14 +12601,14 @@ async function collectManifests(roots) {
|
|
|
12345
12601
|
}
|
|
12346
12602
|
continue;
|
|
12347
12603
|
}
|
|
12348
|
-
if (
|
|
12604
|
+
if (resolve25(parsed.targetRoot) !== resolve25(root)) {
|
|
12349
12605
|
throw new Error(`Install manifest target root does not match its enumerated runtime root: ${path}`);
|
|
12350
12606
|
}
|
|
12351
12607
|
const expectedPath = installManifestPath(parsed.targetRoot, parsed.adapter, {
|
|
12352
12608
|
installationType: parsed.installationType,
|
|
12353
12609
|
stateKey: parsed.stateKey
|
|
12354
12610
|
});
|
|
12355
|
-
if (
|
|
12611
|
+
if (resolve25(expectedPath) !== resolve25(path)) {
|
|
12356
12612
|
throw new Error(`Install manifest state identity does not match its canonical path: ${path}`);
|
|
12357
12613
|
}
|
|
12358
12614
|
found.set(path, { path, raw, manifest: { ...parsed, revision: computeManifestRevision(raw), legacy: false } });
|
|
@@ -12373,7 +12629,7 @@ async function collectManifestPaths(paths) {
|
|
|
12373
12629
|
installationType: parsed.installationType,
|
|
12374
12630
|
stateKey: parsed.stateKey
|
|
12375
12631
|
});
|
|
12376
|
-
if (
|
|
12632
|
+
if (resolve25(expectedPath) !== resolve25(path)) {
|
|
12377
12633
|
throw new Error(`Install manifest state identity does not match its canonical path: ${path}`);
|
|
12378
12634
|
}
|
|
12379
12635
|
found.push({ path, raw, manifest: { ...parsed, revision: computeManifestRevision(raw), legacy: false } });
|
|
@@ -12392,14 +12648,14 @@ function assertNoForeignSamePathOwnership(manifests, renderedPaths, allowedOwner
|
|
|
12392
12648
|
}
|
|
12393
12649
|
}
|
|
12394
12650
|
async function relevantGraphLocks(workspaceRoot, selected, targetKeys, include) {
|
|
12395
|
-
const root =
|
|
12651
|
+
const root = join45(workspaceRoot, ".agentwheel", "locks");
|
|
12396
12652
|
if (!await pathExists(root)) return [];
|
|
12397
12653
|
const results = [];
|
|
12398
12654
|
for (const path of await listFiles(root)) {
|
|
12399
12655
|
if (!path.endsWith(".graph-lock.json")) continue;
|
|
12400
12656
|
const lock = await readGraphLock(path);
|
|
12401
12657
|
if (!lock.canonical.roots.some((candidate2) => selected.has(candidate2.rootId))) continue;
|
|
12402
|
-
const parts =
|
|
12658
|
+
const parts = relative9(root, path).split(/[\\/]/);
|
|
12403
12659
|
if (parts.length !== 3) throw new Error(`Graph lock path is not canonical: ${path}`);
|
|
12404
12660
|
const [targetKey2, adapter] = parts;
|
|
12405
12661
|
if (targetKeys && !targetKeys.has(targetKey2)) continue;
|
|
@@ -12416,7 +12672,7 @@ async function relevantGraphLocks(workspaceRoot, selected, targetKeys, include)
|
|
|
12416
12672
|
const projection = relevantGraphProjection(lock, selected);
|
|
12417
12673
|
results.push({
|
|
12418
12674
|
...candidate,
|
|
12419
|
-
digest:
|
|
12675
|
+
digest: sha2565(canonicalJson2(projection)),
|
|
12420
12676
|
artifactIdentities: lock.canonical.artifacts.filter((artifact) => artifact.owners.some((owner) => ownerMatchesGraphPackage(lock, owner, selected))).map(graphArtifactIdentity).sort()
|
|
12421
12677
|
});
|
|
12422
12678
|
}
|
|
@@ -12441,7 +12697,7 @@ async function hasRelevantLegacyManifestCandidate(scope, graph, selected) {
|
|
|
12441
12697
|
const legacyManifest = (await collectManifestPaths([legacyState.manifestPath]))[0];
|
|
12442
12698
|
if (legacyManifest?.manifest.entries.some((entry) => entryMatchesGraphPackages(entry, graph.lock, selected))) return true;
|
|
12443
12699
|
const stableState = await targetStateForGraph(scope, graph, scope.fleetId ?? null);
|
|
12444
|
-
if (
|
|
12700
|
+
if (resolve25(stableState.graphLockPath) !== resolve25(graph.path)) return false;
|
|
12445
12701
|
const stableManifest = (await collectManifestPaths([stableState.manifestPath]))[0];
|
|
12446
12702
|
return stableManifest?.manifest.entries.some((entry) => entryMatchesGraphPackages(entry, graph.lock, selected)) ?? false;
|
|
12447
12703
|
}
|
|
@@ -12462,7 +12718,7 @@ async function legacyManifestPathForConfiguredTarget(scope, graph) {
|
|
|
12462
12718
|
baseDir: scope.root
|
|
12463
12719
|
});
|
|
12464
12720
|
const targetRoot = resolveConfigPath(agent.root, scope.root);
|
|
12465
|
-
const installRoot =
|
|
12721
|
+
const installRoot = resolve25(installRootForAdapterInstallationType(adapter, targetRoot, agent.installationType, false));
|
|
12466
12722
|
const stateKey = stateKeyFor(adapter.name, {
|
|
12467
12723
|
installationType: agent.installationType,
|
|
12468
12724
|
stateKey: agent.stateKey,
|
|
@@ -12530,7 +12786,7 @@ async function targetStateForGraph(scope, graph, identityFleetId = scope.fleetId
|
|
|
12530
12786
|
ssh: void 0,
|
|
12531
12787
|
...agent?.stateKey ? { stateKey: agent.stateKey } : {}
|
|
12532
12788
|
};
|
|
12533
|
-
const installRoot =
|
|
12789
|
+
const installRoot = resolve25(installRootForAdapterInstallationType(adapter, targetRoot, installationType, false));
|
|
12534
12790
|
const targetIdentity = resolveTargetStateIdentity({
|
|
12535
12791
|
targetFingerprintParts: fingerprintParts,
|
|
12536
12792
|
workspaceRoot: scope.root,
|
|
@@ -12562,16 +12818,16 @@ async function targetStateForGraph(scope, graph, identityFleetId = scope.fleetId
|
|
|
12562
12818
|
async function installedTargetStateForGraph(scope, graph, manifests, owners, selected, identityFleetId = scope.fleetId ?? null) {
|
|
12563
12819
|
const stable = await targetStateForGraph(scope, graph, identityFleetId);
|
|
12564
12820
|
let state;
|
|
12565
|
-
if (
|
|
12821
|
+
if (resolve25(graph.path) === resolve25(stable.graphLockPath)) {
|
|
12566
12822
|
state = stable;
|
|
12567
12823
|
} else {
|
|
12568
12824
|
const released = await releasedTargetStateForGraph(scope, graph, identityFleetId);
|
|
12569
|
-
if (
|
|
12825
|
+
if (resolve25(graph.path) !== resolve25(released.graphLockPath)) {
|
|
12570
12826
|
throw new Error(`Graph lock target identity is stale or noncanonical: ${graph.path}`);
|
|
12571
12827
|
}
|
|
12572
12828
|
state = released;
|
|
12573
12829
|
}
|
|
12574
|
-
const manifest = manifests.find((candidate) =>
|
|
12830
|
+
const manifest = manifests.find((candidate) => resolve25(candidate.path) === resolve25(state.manifestPath));
|
|
12575
12831
|
if (!manifest) {
|
|
12576
12832
|
throw new Error(`Graph lock is not covered by its canonical install manifest: ${state.manifestPath}`);
|
|
12577
12833
|
}
|
|
@@ -12595,7 +12851,7 @@ async function releasedTargetStateForGraph(scope, graph, identityFleetId = scope
|
|
|
12595
12851
|
...current,
|
|
12596
12852
|
stateKey,
|
|
12597
12853
|
targetFingerprint,
|
|
12598
|
-
graphLockPath:
|
|
12854
|
+
graphLockPath: join45(dirname22(graph.path), `${targetFingerprint}.graph-lock.json`),
|
|
12599
12855
|
manifestPath: installManifestPath(current.installRoot, current.adapter, {
|
|
12600
12856
|
installationType: current.installationType,
|
|
12601
12857
|
stateKey
|
|
@@ -12603,7 +12859,7 @@ async function releasedTargetStateForGraph(scope, graph, identityFleetId = scope
|
|
|
12603
12859
|
};
|
|
12604
12860
|
}
|
|
12605
12861
|
function assertCorrelatedGraphManifest(graph, state, manifest, owners, selected) {
|
|
12606
|
-
if (manifest.manifest.adapter !== state.adapter || manifest.manifest.installationType !== state.installationType || manifest.manifest.stateKey !== state.stateKey ||
|
|
12862
|
+
if (manifest.manifest.adapter !== state.adapter || manifest.manifest.installationType !== state.installationType || manifest.manifest.stateKey !== state.stateKey || resolve25(manifest.manifest.targetRoot) !== resolve25(state.installRoot)) {
|
|
12607
12863
|
throw new Error(`Install manifest identity does not match graph target state: ${manifest.path}`);
|
|
12608
12864
|
}
|
|
12609
12865
|
const entries = manifest.manifest.entries.filter((entry) => entryMatchesGraphPackages(entry, graph.lock, selected));
|
|
@@ -12611,7 +12867,7 @@ function assertCorrelatedGraphManifest(graph, state, manifest, owners, selected)
|
|
|
12611
12867
|
throw new Error(`Graph lock is not covered by selected install-manifest contributions: ${graph.path}`);
|
|
12612
12868
|
}
|
|
12613
12869
|
const graphArtifacts = new Set(graph.artifactIdentities);
|
|
12614
|
-
const graphLockDigest =
|
|
12870
|
+
const graphLockDigest = sha2565(canonicalJson2(graph.lock));
|
|
12615
12871
|
for (const entry of entries) {
|
|
12616
12872
|
if (!owners.has(entry.workspaceOwner)) {
|
|
12617
12873
|
throw new Error(`Graph lock manifest owner mismatch at ${manifest.path}: ${entry.workspaceOwner}.`);
|
|
@@ -12660,7 +12916,7 @@ function destinationGraphFromSource(source, targetFingerprint) {
|
|
|
12660
12916
|
function mergeDestinationManifest(existing, state, generatedAt, desiredEntries, replaceOwners = /* @__PURE__ */ new Set()) {
|
|
12661
12917
|
if (existing) {
|
|
12662
12918
|
const manifest2 = existing.manifest;
|
|
12663
|
-
if (manifest2.adapter !== state.adapter || manifest2.installationType !== state.installationType || manifest2.stateKey !== state.stateKey ||
|
|
12919
|
+
if (manifest2.adapter !== state.adapter || manifest2.installationType !== state.installationType || manifest2.stateKey !== state.stateKey || resolve25(manifest2.targetRoot) !== state.installRoot) {
|
|
12664
12920
|
throw new Error(`Destination manifest identity diverges from the derived target: ${existing.path}`);
|
|
12665
12921
|
}
|
|
12666
12922
|
}
|
|
@@ -12720,7 +12976,7 @@ async function assertEquivalentRuntimeState(entry, sourcePath, destinationPath)
|
|
|
12720
12976
|
const content = await readFile27(path, "utf8");
|
|
12721
12977
|
if (hasMergeRemovalContent(entry.mergeRemoval)) {
|
|
12722
12978
|
assertExactMergeContribution(entry.mergeRemoval, entry.mergeStrategy, content);
|
|
12723
|
-
} else if (
|
|
12979
|
+
} else if (resolve25(sourcePath) === resolve25(destinationPath) && entry.mergeStrategy !== "codex-toml-mcp") {
|
|
12724
12980
|
assertExactMergeContribution({}, entry.mergeStrategy, content);
|
|
12725
12981
|
} else {
|
|
12726
12982
|
throw new Error(`Installed-state normalization cannot prove incomplete merge ownership at ${path}.`);
|
|
@@ -12731,8 +12987,8 @@ async function assertEquivalentRuntimeState(entry, sourcePath, destinationPath)
|
|
|
12731
12987
|
await assertEquivalentRuntimeBytes(sourcePath, destinationPath, entry.hash);
|
|
12732
12988
|
}
|
|
12733
12989
|
function renderedEntryPathForRoot(root, entryPath) {
|
|
12734
|
-
const normalizedRoot =
|
|
12735
|
-
const candidate =
|
|
12990
|
+
const normalizedRoot = resolve25(root);
|
|
12991
|
+
const candidate = resolve25(normalizedRoot, entryPath);
|
|
12736
12992
|
if (candidate !== normalizedRoot && !candidate.startsWith(`${normalizedRoot}/`)) {
|
|
12737
12993
|
throw new Error(`Install manifest entry escapes its target root: ${entryPath}`);
|
|
12738
12994
|
}
|
|
@@ -12823,8 +13079,8 @@ function ownerMatchesGraphPackage(lock, owner, selected) {
|
|
|
12823
13079
|
return ownerMatchesPackage(owner, selected) || lock.canonical.roots.some((root) => selected.has(root.rootId) && root.graphNodeId === owner);
|
|
12824
13080
|
}
|
|
12825
13081
|
function renderedEntryPath(manifest, entry) {
|
|
12826
|
-
const root =
|
|
12827
|
-
const candidate =
|
|
13082
|
+
const root = resolve25(manifest.targetRoot);
|
|
13083
|
+
const candidate = resolve25(root, entry.path);
|
|
12828
13084
|
if (candidate !== root && !candidate.startsWith(`${root}/`)) {
|
|
12829
13085
|
throw new Error(`Install manifest entry escapes its target root: ${entry.path}`);
|
|
12830
13086
|
}
|
|
@@ -12979,7 +13235,7 @@ async function buildJournalGraphLockStates(plan) {
|
|
|
12979
13235
|
for (const transfer of plan.installedState.graphTransfers) {
|
|
12980
13236
|
const sourceBefore = JSON.parse(await readFile27(transfer.sourceGraphLockPath, "utf8"));
|
|
12981
13237
|
const sourceLock = await readGraphLock(transfer.sourceGraphLockPath);
|
|
12982
|
-
const sourceDigest =
|
|
13238
|
+
const sourceDigest = sha2565(canonicalJson2(sourceLock));
|
|
12983
13239
|
if (sourceDigest !== transfer.sourceGraphLockDigest) {
|
|
12984
13240
|
throw new Error(`Source graph lock changed after planning: ${transfer.sourceGraphLockPath}`);
|
|
12985
13241
|
}
|
|
@@ -12997,9 +13253,9 @@ async function buildJournalGraphLockStates(plan) {
|
|
|
12997
13253
|
continue;
|
|
12998
13254
|
}
|
|
12999
13255
|
const destinationBefore = await readOptionalRecord(transfer.destinationGraphLockPath);
|
|
13000
|
-
const destinationBeforeDigest = destinationBefore ?
|
|
13256
|
+
const destinationBeforeDigest = destinationBefore ? sha2565(canonicalJson2(await readGraphLock(transfer.destinationGraphLockPath))) : null;
|
|
13001
13257
|
const destinationAfter = destinationBefore ? await readGraphLock(transfer.destinationGraphLockPath) : destinationGraphFromSource(sourceLock, transfer.targetFingerprint);
|
|
13002
|
-
const destinationAfterDigest =
|
|
13258
|
+
const destinationAfterDigest = sha2565(canonicalJson2(destinationAfter));
|
|
13003
13259
|
if (destinationBeforeDigest !== transfer.destinationGraphLockDigest || destinationAfterDigest !== transfer.destinationGraphLockAfterDigest) {
|
|
13004
13260
|
throw new Error(`Destination graph lock changed after planning: ${transfer.destinationGraphLockPath}`);
|
|
13005
13261
|
}
|
|
@@ -13109,7 +13365,7 @@ async function assertJournalRecoverable(journal) {
|
|
|
13109
13365
|
}
|
|
13110
13366
|
for (const graphLock of journal.graphLocks) {
|
|
13111
13367
|
const raw = await readOptionalRecord(graphLock.path);
|
|
13112
|
-
const digest2 = raw ?
|
|
13368
|
+
const digest2 = raw ? sha2565(canonicalJson2(await readGraphLock(graphLock.path))) : null;
|
|
13113
13369
|
if (digest2 !== graphLock.beforeDigest && digest2 !== graphLock.afterDigest) {
|
|
13114
13370
|
throw new Error(`Cannot recover fleet normalization because graph lock changed outside the transaction: ${graphLock.path}`);
|
|
13115
13371
|
}
|
|
@@ -13162,10 +13418,10 @@ function packageMap(config) {
|
|
|
13162
13418
|
return result;
|
|
13163
13419
|
}
|
|
13164
13420
|
function configRevision(config) {
|
|
13165
|
-
return
|
|
13421
|
+
return sha2565(canonicalJson2(config));
|
|
13166
13422
|
}
|
|
13167
13423
|
function normalizationJournalPath(sourceRoot, destinationFleet) {
|
|
13168
|
-
return
|
|
13424
|
+
return join45(sourceRoot, ".agentwheel", `fleet-normalize-${destinationFleet}.journal.json`);
|
|
13169
13425
|
}
|
|
13170
13426
|
function canonicalJson2(value) {
|
|
13171
13427
|
return JSON.stringify(sortValue(value));
|
|
@@ -13177,8 +13433,8 @@ function sortValue(value) {
|
|
|
13177
13433
|
}
|
|
13178
13434
|
return value;
|
|
13179
13435
|
}
|
|
13180
|
-
function
|
|
13181
|
-
return
|
|
13436
|
+
function sha2565(value) {
|
|
13437
|
+
return createHash14("sha256").update(value).digest("hex");
|
|
13182
13438
|
}
|
|
13183
13439
|
function assertDigest(value) {
|
|
13184
13440
|
if (!/^[a-f0-9]{64}$/.test(value)) throw new Error(`Invalid plan digest: ${value}`);
|
|
@@ -13216,6 +13472,7 @@ var commandGovernance = {
|
|
|
13216
13472
|
"package migrate": "governed-always",
|
|
13217
13473
|
remember: "governed-always",
|
|
13218
13474
|
"ownership handoff": "governed-unless-dry-run",
|
|
13475
|
+
"ownership recovery-plan": "incidental-cache",
|
|
13219
13476
|
"ownership retire-stale": "governed-apply",
|
|
13220
13477
|
"mcp retire": "governed-apply",
|
|
13221
13478
|
eject: "governed-always",
|
|
@@ -13322,7 +13579,7 @@ fleetCommand.command("normalize").description("plan or apply duplicate desired-s
|
|
|
13322
13579
|
});
|
|
13323
13580
|
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
13581
|
const targetRoot = await workspaceContextRootFromOptions(normalizeRuntimeScopeOptions(options));
|
|
13325
|
-
const cacheRoot =
|
|
13582
|
+
const cacheRoot = join46(targetRoot, ".agentwheel", "cache");
|
|
13326
13583
|
const result = await pruneGitCache(cacheRoot, { keepSnapshots: options.keep, dryRun: !options.apply });
|
|
13327
13584
|
const verb = options.apply ? "Removed" : "Would remove";
|
|
13328
13585
|
for (const path of result.removedPaths) console.log(`${verb} ${path}`);
|
|
@@ -13340,7 +13597,7 @@ program.command("list").description("list artifacts exposed by a package source"
|
|
|
13340
13597
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
13341
13598
|
const selectedArtifacts = selectedArtifactsFromOptionsOrRegistry(options, resolvedInput.registryEntry);
|
|
13342
13599
|
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:
|
|
13600
|
+
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join46(targetRoot, ".agentwheel", "cache") }))));
|
|
13344
13601
|
try {
|
|
13345
13602
|
const artifacts = filterArtifactsBySelection(await driver.list(resolved), selectedArtifacts);
|
|
13346
13603
|
for (const artifact of artifacts) {
|
|
@@ -13409,7 +13666,7 @@ program.command("try").description("read and validate one skill for the current
|
|
|
13409
13666
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
13410
13667
|
const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
|
|
13411
13668
|
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, {
|
|
13412
|
-
cacheRoot:
|
|
13669
|
+
cacheRoot: join46(targetRoot, ".agentwheel", "cache")
|
|
13413
13670
|
}))));
|
|
13414
13671
|
try {
|
|
13415
13672
|
const trial = await createSkillTrial(driver, resolved, selectedArtifactsFromOptionsOrRegistry(options, resolvedInput.registryEntry));
|
|
@@ -13431,7 +13688,7 @@ program.command("scan").description("scan a package source for validation findin
|
|
|
13431
13688
|
const targetRoot = await workspaceContextRootFromOptions(normalizeRuntimeScopeOptions(options));
|
|
13432
13689
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
13433
13690
|
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:
|
|
13691
|
+
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join46(targetRoot, ".agentwheel", "cache") }))));
|
|
13435
13692
|
try {
|
|
13436
13693
|
const result = await driver.scan(resolved);
|
|
13437
13694
|
if (result.findings.length === 0) {
|
|
@@ -13636,7 +13893,7 @@ ownershipCommand.command("handoff").description("transfer one managed artifact b
|
|
|
13636
13893
|
const toWorkspaceRoot = normalizeCliPath(options.toWorkspaceRoot);
|
|
13637
13894
|
if (options.toFleet) {
|
|
13638
13895
|
const fleet = await showRegisteredFleet(options.toFleet);
|
|
13639
|
-
if (
|
|
13896
|
+
if (resolve26(fleet.root) !== resolve26(toWorkspaceRoot)) {
|
|
13640
13897
|
throw new Error(`Destination fleet '${options.toFleet}' is registered at ${fleet.root}, not ${toWorkspaceRoot}.`);
|
|
13641
13898
|
}
|
|
13642
13899
|
}
|
|
@@ -13662,11 +13919,85 @@ ownershipCommand.command("handoff").description("transfer one managed artifact b
|
|
|
13662
13919
|
console.log(`Manifest revision: ${result.manifestRevision}`);
|
|
13663
13920
|
console.log(`Owner: ${result.fromOwner} -> ${result.toOwner}`);
|
|
13664
13921
|
});
|
|
13922
|
+
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) => {
|
|
13923
|
+
const targets = await resolveCliTargets({ fleet: options.fleet, agent: options.agent, profile: options.profile });
|
|
13924
|
+
if (targets.length !== 1) throw new Error(`Recovery planning requires one Fleet target, found ${targets.length}.`);
|
|
13925
|
+
const target = targets[0];
|
|
13926
|
+
if (target.fleetId !== options.fleet) throw new Error("Resolved target does not belong to the selected Fleet.");
|
|
13927
|
+
const adapter = await resolveAdapterForTarget(target, {});
|
|
13928
|
+
const transport = transportForTarget(target);
|
|
13929
|
+
const requestedReport = normalizeCliPath(options.report);
|
|
13930
|
+
const reportPath = join46(await realpath2(dirname23(requestedReport)), basename21(requestedReport));
|
|
13931
|
+
const reportInsideTarget = relative10(resolve26(target.targetRoot), reportPath);
|
|
13932
|
+
if (reportInsideTarget === "" || !reportInsideTarget.startsWith("..") && !isAbsolute4(reportInsideTarget)) {
|
|
13933
|
+
throw new Error("Recovery report must be outside the runtime target root.");
|
|
13934
|
+
}
|
|
13935
|
+
const installationType = target.installationType ?? resolveInstallationTypeForAdapter(adapter, void 0);
|
|
13936
|
+
const config = await readMergedWorkspaceConfig(target.workspaceRoot);
|
|
13937
|
+
if (config.packages.length === 0) throw new Error("Fleet has no configured source packages.");
|
|
13938
|
+
const stage = await mkdtemp4(join46(tmpdir5(), "agentwheel-recovery-"));
|
|
13939
|
+
const priorTmpdir = process.env.TMPDIR;
|
|
13940
|
+
try {
|
|
13941
|
+
process.env.TMPDIR = stage;
|
|
13942
|
+
const graphPlan = await createGraphSourcePlan({
|
|
13943
|
+
roots: config.packages.map((pkg) => ({
|
|
13944
|
+
rootId: pkg.name,
|
|
13945
|
+
source: pkg.source,
|
|
13946
|
+
mode: pkg.mode,
|
|
13947
|
+
version: pkg.version,
|
|
13948
|
+
ref: pkg.requestedRef,
|
|
13949
|
+
select: pkg.selection ? void 0 : normalizeArtifactSelectors(pkg.select, pkg.skills),
|
|
13950
|
+
selection: pkg.selection,
|
|
13951
|
+
aliases: pkg.aliases,
|
|
13952
|
+
overrides: pkg.overrides,
|
|
13953
|
+
includeSuggestions: pkg.withSuggestions,
|
|
13954
|
+
suggestionAliases: pkg.suggestions,
|
|
13955
|
+
useLock: false
|
|
13956
|
+
})),
|
|
13957
|
+
targetRoot: target.targetRoot,
|
|
13958
|
+
workspaceRoot: target.workspaceRoot,
|
|
13959
|
+
fleetId: target.fleetId,
|
|
13960
|
+
adapter,
|
|
13961
|
+
transport,
|
|
13962
|
+
targetKey: targetKeyForTarget(target, adapter.name),
|
|
13963
|
+
targetFingerprintParts: targetFingerprintParts(target, adapter, adapterOptionsForTarget(target, {}), installationType),
|
|
13964
|
+
installationType,
|
|
13965
|
+
stateKey: target.stateKey,
|
|
13966
|
+
readOnly: true,
|
|
13967
|
+
yes: true,
|
|
13968
|
+
freshGraphOnly: true,
|
|
13969
|
+
deferForeignStateCheck: true,
|
|
13970
|
+
cacheRoot: join46(stage, "source-cache"),
|
|
13971
|
+
registryCachePath: join46(stage, "registry.json")
|
|
13972
|
+
});
|
|
13973
|
+
const installRoot = installRootForAdapterInstallationType(adapter, target.targetRoot, installationType, transport.kind === "ssh");
|
|
13974
|
+
const report = await planLegacyRecovery({
|
|
13975
|
+
graphPlan,
|
|
13976
|
+
installRoot,
|
|
13977
|
+
workspaceRoot: target.workspaceRoot,
|
|
13978
|
+
fleetId: target.fleetId,
|
|
13979
|
+
explicitStateKey: target.stateKey,
|
|
13980
|
+
targetKey: targetKeyForTarget(target, adapter.name),
|
|
13981
|
+
adapter: adapter.name,
|
|
13982
|
+
installationType,
|
|
13983
|
+
historicalLocks: await inventoryHistoricalLocks(target.workspaceRoot),
|
|
13984
|
+
transport
|
|
13985
|
+
});
|
|
13986
|
+
await writeFile13(reportPath, `${JSON.stringify(report, null, 2)}
|
|
13987
|
+
`, { mode: 384, flag: "wx" });
|
|
13988
|
+
console.log(`Recovery report: ${reportPath}`);
|
|
13989
|
+
console.log(JSON.stringify(report.counts));
|
|
13990
|
+
} finally {
|
|
13991
|
+
if (priorTmpdir === void 0) delete process.env.TMPDIR;
|
|
13992
|
+
else process.env.TMPDIR = priorTmpdir;
|
|
13993
|
+
await rm10(stage, { recursive: true, force: true });
|
|
13994
|
+
}
|
|
13995
|
+
});
|
|
13665
13996
|
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
13997
|
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
13667
13998
|
const fleet = await showRegisteredFleet(options.fleet);
|
|
13668
13999
|
const toWorkspaceRoot = normalizeCliPath(options.toWorkspaceRoot);
|
|
13669
|
-
if (
|
|
14000
|
+
if (resolve26(fleet.root) !== resolve26(toWorkspaceRoot)) {
|
|
13670
14001
|
throw new Error(`Destination Fleet '${options.fleet}' is registered at ${fleet.root}, not ${toWorkspaceRoot}.`);
|
|
13671
14002
|
}
|
|
13672
14003
|
const targets = await resolveCliTargets(normalizedOptions);
|
|
@@ -13674,7 +14005,7 @@ ownershipCommand.command("retire-stale").description("retire stale source-manife
|
|
|
13674
14005
|
throw new Error(`Stale ownership retirement requires exactly one destination Fleet target, found ${targets.length}.`);
|
|
13675
14006
|
}
|
|
13676
14007
|
const target = targets[0];
|
|
13677
|
-
if (target.fleetId !== options.fleet ||
|
|
14008
|
+
if (target.fleetId !== options.fleet || resolve26(target.workspaceRoot) !== resolve26(fleet.root)) {
|
|
13678
14009
|
throw new Error(`Resolved target does not belong to destination Fleet '${options.fleet}'.`);
|
|
13679
14010
|
}
|
|
13680
14011
|
const state = await resolveCliTargetState(target, normalizedOptions);
|
|
@@ -13832,7 +14163,7 @@ journalCommand.command("list").description("show pending apply journals for reso
|
|
|
13832
14163
|
if (!journal) continue;
|
|
13833
14164
|
pending += 1;
|
|
13834
14165
|
console.log(`PENDING ${state.adapter.name}/${state.installationType} at ${state.installRoot}`);
|
|
13835
|
-
console.log(` journal: ${
|
|
14166
|
+
console.log(` journal: ${join46(state.installRoot, ".agentwheel", `${state.state.stateKey}.apply-journal.json`)}`);
|
|
13836
14167
|
console.log(` stateKey: ${state.state.stateKey}`);
|
|
13837
14168
|
console.log(` createdAt: ${journal.createdAt}`);
|
|
13838
14169
|
console.log(` updatedAt: ${journal.updatedAt}`);
|
|
@@ -14168,14 +14499,14 @@ async function packageEntryFromSource(source, targetRoot, options) {
|
|
|
14168
14499
|
);
|
|
14169
14500
|
}
|
|
14170
14501
|
const bundle = options.deferGraphRendering ? await stageSourceRaw(driver, resolvedSource, {
|
|
14171
|
-
cacheRoot:
|
|
14502
|
+
cacheRoot: join46(targetRoot, ".agentwheel", "cache"),
|
|
14172
14503
|
mode: options.mode,
|
|
14173
14504
|
ref: initialVersion?.ref,
|
|
14174
14505
|
frozenLock: lockMode
|
|
14175
14506
|
}) : await stageSource(driver, resolvedSource, {
|
|
14176
14507
|
workspaceRoot: targetRoot,
|
|
14177
14508
|
adapter,
|
|
14178
|
-
cacheRoot:
|
|
14509
|
+
cacheRoot: join46(targetRoot, ".agentwheel", "cache"),
|
|
14179
14510
|
mode: options.mode,
|
|
14180
14511
|
ref: initialVersion?.ref,
|
|
14181
14512
|
frozenLock: lockMode,
|
|
@@ -14756,7 +15087,7 @@ function scopeUpdatePlanToDependencies(result, selectors, previousLock, manifest
|
|
|
14756
15087
|
selectedPreviousNodeIds,
|
|
14757
15088
|
selectedRootIds
|
|
14758
15089
|
);
|
|
14759
|
-
const graphLockDigest =
|
|
15090
|
+
const graphLockDigest = createHash15("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
|
|
14760
15091
|
return {
|
|
14761
15092
|
...result,
|
|
14762
15093
|
bundle: { ...result.bundle, graphLock },
|
|
@@ -14882,7 +15213,7 @@ function operationBelongsToDependencyUpdate(operation, allowedNodeIds, allowedNa
|
|
|
14882
15213
|
function scopeInstallPlanToArtifact(result, rootId, focused, manifest) {
|
|
14883
15214
|
const scoped = scopePlanOperationsToArtifact(result, rootId, focused, manifest);
|
|
14884
15215
|
const graphLock = focusedGraphLockForInstall(scoped.bundle.graphLock, rootId, focused);
|
|
14885
|
-
const graphLockDigest =
|
|
15216
|
+
const graphLockDigest = createHash15("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
|
|
14886
15217
|
return {
|
|
14887
15218
|
...scoped,
|
|
14888
15219
|
bundle: { ...scoped.bundle, graphLock },
|
|
@@ -14893,7 +15224,7 @@ function scopeInstallPlanToArtifact(result, rootId, focused, manifest) {
|
|
|
14893
15224
|
function scopeUpdatePlanToArtifact(result, rootId, focused, previousLock, manifest) {
|
|
14894
15225
|
const scoped = scopePlanOperationsToArtifact(result, rootId, focused, manifest, previousLock);
|
|
14895
15226
|
const graphLock = mergeFocusedArtifactGraphLock(scoped.bundle.graphLock, previousLock, rootId, focused);
|
|
14896
|
-
const graphLockDigest =
|
|
15227
|
+
const graphLockDigest = createHash15("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
|
|
14897
15228
|
return {
|
|
14898
15229
|
...scoped,
|
|
14899
15230
|
bundle: { ...scoped.bundle, graphLock },
|
|
@@ -15188,7 +15519,7 @@ function scopeUpdatePlanToRoot(result, rootId, previousLock, manifest) {
|
|
|
15188
15519
|
selectedPreviousNodeIds,
|
|
15189
15520
|
/* @__PURE__ */ new Set([rootId])
|
|
15190
15521
|
);
|
|
15191
|
-
const graphLockDigest =
|
|
15522
|
+
const graphLockDigest = createHash15("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
|
|
15192
15523
|
return {
|
|
15193
15524
|
...scoped,
|
|
15194
15525
|
bundle: { ...scoped.bundle, graphLock },
|
|
@@ -15260,7 +15591,7 @@ function keepManifestEntryOperation(entry, targetRoot, scopeDescription, operati
|
|
|
15260
15591
|
artifactType: entry.artifactType,
|
|
15261
15592
|
artifactName: entry.artifactName,
|
|
15262
15593
|
kind: entry.kind,
|
|
15263
|
-
destPath: operation?.destPath ??
|
|
15594
|
+
destPath: operation?.destPath ?? join46(targetRoot, entry.path),
|
|
15264
15595
|
relativeDestPath: entry.path,
|
|
15265
15596
|
desiredHash: entry.sourceHash,
|
|
15266
15597
|
currentHash: operation?.currentHash ?? entry.hash,
|
|
@@ -15526,7 +15857,7 @@ async function resolveCliTargetState(target, options) {
|
|
|
15526
15857
|
migration: prior.migration,
|
|
15527
15858
|
targetStateFilePreconditions: {
|
|
15528
15859
|
graphLockPath: stableGraphLockPath,
|
|
15529
|
-
graphLockRevision: stableLock ?
|
|
15860
|
+
graphLockRevision: stableLock ? createHash15("sha256").update(canonicalGraphLockJson(stableLock)).digest("hex") : null,
|
|
15530
15861
|
sourceLockRevision: computeSourceLockRevision(stableSourceLock)
|
|
15531
15862
|
}
|
|
15532
15863
|
};
|
|
@@ -15549,7 +15880,7 @@ async function collectTargetStatus(target, options) {
|
|
|
15549
15880
|
const pending = await collectPendingInstallWork(target, options);
|
|
15550
15881
|
const foreignObservations = pending.foreignStateObservations;
|
|
15551
15882
|
const effectiveGraphLock = graphLock ?? pending.observedGraphLock;
|
|
15552
|
-
const observedManifestRevision = foreignObservations.length > 0 ?
|
|
15883
|
+
const observedManifestRevision = foreignObservations.length > 0 ? createHash15("sha256").update(JSON.stringify(foreignObservations.map((observation) => [
|
|
15553
15884
|
observation.fileName,
|
|
15554
15885
|
observation.manifestRevision
|
|
15555
15886
|
]).sort())).digest("hex") : null;
|
|
@@ -15796,7 +16127,7 @@ async function assertCompositeMutationSupported(workspaceRoot, profile, readOnly
|
|
|
15796
16127
|
`Composite-profile mutation is refused before member execution because ${member.id} uses ${member.transport}; durable remote policy and recovery preflight is not implemented.`
|
|
15797
16128
|
);
|
|
15798
16129
|
}
|
|
15799
|
-
const memberRoot =
|
|
16130
|
+
const memberRoot = resolve26(workspaceRoot, member.workspace);
|
|
15800
16131
|
const memberPolicy = await mutationPolicyForWorkspace(memberRoot);
|
|
15801
16132
|
if (memberPolicy?.revisioning.mode === "commit-after-verify") {
|
|
15802
16133
|
throw new Error(
|
|
@@ -16032,12 +16363,12 @@ async function printDoctor(target, options) {
|
|
|
16032
16363
|
const requestedSkills = doctorSkillRequests(target, options);
|
|
16033
16364
|
const skills = [];
|
|
16034
16365
|
for (const request of requestedSkills) {
|
|
16035
|
-
const skillPath =
|
|
16366
|
+
const skillPath = join46(state.installRoot, targetMapping.dest, request.name);
|
|
16036
16367
|
const exists = await pathExists(skillPath);
|
|
16037
16368
|
const manifestEntry = manifest?.entries.find((entry) => {
|
|
16038
16369
|
if (entry.artifactType !== "skills") return false;
|
|
16039
16370
|
const legacyInstallName = "installName" in entry && typeof entry.installName === "string" ? entry.installName : void 0;
|
|
16040
|
-
return entry.artifactName === request.name || legacyInstallName === request.name || entry.path ===
|
|
16371
|
+
return entry.artifactName === request.name || legacyInstallName === request.name || entry.path === join46(targetMapping.dest, request.name);
|
|
16041
16372
|
});
|
|
16042
16373
|
const status = manifestEntry ? "managed" : exists ? "present-unmanaged" : "missing";
|
|
16043
16374
|
skills.push({
|
|
@@ -16117,7 +16448,7 @@ function doctorSkillLabel(name) {
|
|
|
16117
16448
|
return `${name} skill`;
|
|
16118
16449
|
}
|
|
16119
16450
|
function isSyncwheelWorkspace(targetRoot) {
|
|
16120
|
-
return existsSync(
|
|
16451
|
+
return existsSync(join46(targetRoot, ".syncwheel", "manifest.json"));
|
|
16121
16452
|
}
|
|
16122
16453
|
function skillInstallCommand(adapter, installationType, options, skill, behavior = {}) {
|
|
16123
16454
|
const args = [
|
|
@@ -16280,14 +16611,14 @@ function looksLikeSourceSpecifier(value) {
|
|
|
16280
16611
|
}
|
|
16281
16612
|
function normalizeCliPath(value) {
|
|
16282
16613
|
if (value === "~") return homedir12();
|
|
16283
|
-
if (value.startsWith("~/")) return
|
|
16284
|
-
return
|
|
16614
|
+
if (value.startsWith("~/")) return resolve26(homedir12(), value.slice(2));
|
|
16615
|
+
return resolve26(value);
|
|
16285
16616
|
}
|
|
16286
16617
|
function shellQuoteArgument(value) {
|
|
16287
16618
|
return /^[a-z0-9_./:@%+=,-]+$/i.test(value) ? value : `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
16288
16619
|
}
|
|
16289
16620
|
function isHomePath(path) {
|
|
16290
|
-
return
|
|
16621
|
+
return resolve26(path) === resolve26(homedir12());
|
|
16291
16622
|
}
|
|
16292
16623
|
function adapterListFromOption(adapter) {
|
|
16293
16624
|
if (!adapter) return [];
|
|
@@ -16342,11 +16673,11 @@ function filterUninstallPlanBySelection(plan, selected) {
|
|
|
16342
16673
|
};
|
|
16343
16674
|
}
|
|
16344
16675
|
async function initPackage(root) {
|
|
16345
|
-
await mkdir16(
|
|
16346
|
-
await mkdir16(
|
|
16347
|
-
await mkdir16(
|
|
16348
|
-
const manifestPath =
|
|
16349
|
-
const instructionsPath =
|
|
16676
|
+
await mkdir16(join46(root, "instructions"), { recursive: true });
|
|
16677
|
+
await mkdir16(join46(root, "rules"), { recursive: true });
|
|
16678
|
+
await mkdir16(join46(root, "skills"), { recursive: true });
|
|
16679
|
+
const manifestPath = join46(root, "openpack.json");
|
|
16680
|
+
const instructionsPath = join46(root, "instructions", "AGENTS.md");
|
|
16350
16681
|
const manifest = {
|
|
16351
16682
|
schemaVersion: CURRENT_OPENPACK_SCHEMA_VERSION,
|
|
16352
16683
|
name: "example/agentwheel-package",
|
|
@@ -16364,7 +16695,7 @@ async function initPackage(root) {
|
|
|
16364
16695
|
await writeFile13(instructionsPath, "# Agent Instructions\n", "utf8");
|
|
16365
16696
|
}
|
|
16366
16697
|
async function defaultBootstrapPackage(_root) {
|
|
16367
|
-
const packageRoot = await findAgentwheelPackageRoot(
|
|
16698
|
+
const packageRoot = await findAgentwheelPackageRoot(dirname23(fileURLToPath3(import.meta.url)));
|
|
16368
16699
|
if (!packageRoot) return void 0;
|
|
16369
16700
|
return {
|
|
16370
16701
|
name: "agentwheel",
|
|
@@ -16408,10 +16739,10 @@ function withFleetExample(config) {
|
|
|
16408
16739
|
};
|
|
16409
16740
|
}
|
|
16410
16741
|
async function findAgentwheelPackageRoot(start) {
|
|
16411
|
-
let current =
|
|
16742
|
+
let current = resolve26(start);
|
|
16412
16743
|
while (true) {
|
|
16413
16744
|
if (await findPackageManifestPath(current, { warnLegacy: false })) return current;
|
|
16414
|
-
const parent =
|
|
16745
|
+
const parent = dirname23(current);
|
|
16415
16746
|
if (parent === current) return void 0;
|
|
16416
16747
|
current = parent;
|
|
16417
16748
|
}
|
|
@@ -16536,14 +16867,14 @@ async function governedCommandContext(command, options) {
|
|
|
16536
16867
|
const kind = String(command.args[0] ?? "workspace");
|
|
16537
16868
|
return {
|
|
16538
16869
|
workspaceRoot: workspaceRoot2,
|
|
16539
|
-
anticipatedPaths: kind === "package" ? [
|
|
16870
|
+
anticipatedPaths: kind === "package" ? [join46(targetRoot, "openpack.json"), join46(targetRoot, "instructions", "AGENTS.md")] : [workspaceConfigPath(targetRoot)]
|
|
16540
16871
|
};
|
|
16541
16872
|
}
|
|
16542
16873
|
if (path === "package migrate") {
|
|
16543
16874
|
const packageRoot = normalizeTargetRoot(String(command.args[0] ?? "."));
|
|
16544
16875
|
return {
|
|
16545
16876
|
workspaceRoot: await findExistingWorkspaceRoot(packageRoot) ?? packageRoot,
|
|
16546
|
-
anticipatedPaths: ["agentwheel.json", "agentwheel.jsonc", "openpack.json", "openpack.jsonc"].map((name) =>
|
|
16877
|
+
anticipatedPaths: ["agentwheel.json", "agentwheel.jsonc", "openpack.json", "openpack.jsonc"].map((name) => join46(packageRoot, name))
|
|
16547
16878
|
};
|
|
16548
16879
|
}
|
|
16549
16880
|
const source = typeof command.args[0] === "string" ? command.args[0] : void 0;
|
|
@@ -16555,7 +16886,7 @@ async function governedCommandContext(command, options) {
|
|
|
16555
16886
|
const anticipatedPaths = [];
|
|
16556
16887
|
if (path === "add") anticipatedPaths.push(workspaceConfigPath(workspaceRoot));
|
|
16557
16888
|
if (path === "remember" && options.runtime) {
|
|
16558
|
-
anticipatedPaths.push(
|
|
16889
|
+
anticipatedPaths.push(join46(workspaceRoot, ".agentwheel", "overlays", options.runtime, "instructions.local.md"));
|
|
16559
16890
|
}
|
|
16560
16891
|
if (path === "uninstall" && source) anticipatedPaths.push(workspaceConfigPath(workspaceRoot));
|
|
16561
16892
|
return {
|