agentwheel 0.21.0 → 0.21.2
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 +17 -5
- package/dist/index.js +905 -362
- 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
|
@@ -522,13 +522,13 @@ async function hashPath(path) {
|
|
|
522
522
|
if (!stats.isDirectory()) {
|
|
523
523
|
throw new Error(`Unsupported path kind: ${path}`);
|
|
524
524
|
}
|
|
525
|
-
const
|
|
525
|
+
const hash2 = createHash("sha256").update("dir\0");
|
|
526
526
|
const files = await listFiles(path);
|
|
527
527
|
for (const file of files) {
|
|
528
|
-
|
|
529
|
-
|
|
528
|
+
hash2.update(relative(path, file).replaceAll("\\", "/")).update("\0");
|
|
529
|
+
hash2.update(await hashPath(file)).update("\0");
|
|
530
530
|
}
|
|
531
|
-
return
|
|
531
|
+
return hash2.digest("hex");
|
|
532
532
|
}
|
|
533
533
|
async function listFiles(root) {
|
|
534
534
|
const out = [];
|
|
@@ -583,7 +583,7 @@ async function withFilesystemLock(lockPath, timeoutMs, fn, description = "cache"
|
|
|
583
583
|
if (Date.now() - started > timeoutMs) {
|
|
584
584
|
throw new Error(`Timed out waiting for ${description} lock at ${lockPath}`);
|
|
585
585
|
}
|
|
586
|
-
await new Promise((
|
|
586
|
+
await new Promise((resolve45) => setTimeout(resolve45, 50));
|
|
587
587
|
}
|
|
588
588
|
}
|
|
589
589
|
try {
|
|
@@ -853,7 +853,7 @@ var init_protocol = __esm({
|
|
|
853
853
|
// src/mutation/process.ts
|
|
854
854
|
import { spawn } from "child_process";
|
|
855
855
|
async function runProcess(command, args, options = {}) {
|
|
856
|
-
return new Promise((
|
|
856
|
+
return new Promise((resolve45, reject) => {
|
|
857
857
|
const useProcessGroup = options.killProcessGroup === true && process.platform !== "win32";
|
|
858
858
|
const child = spawn(command, args, {
|
|
859
859
|
cwd: options.cwd,
|
|
@@ -913,7 +913,7 @@ async function runProcess(command, args, options = {}) {
|
|
|
913
913
|
reject(new Error(`${command} exited ${exitCode}${detail ? `: ${detail}` : ""}`));
|
|
914
914
|
return;
|
|
915
915
|
}
|
|
916
|
-
|
|
916
|
+
resolve45(result);
|
|
917
917
|
});
|
|
918
918
|
if (options.input !== void 0) child.stdin.end(options.input);
|
|
919
919
|
else child.stdin.end();
|
|
@@ -939,8 +939,8 @@ function agentMeshGraphPath() {
|
|
|
939
939
|
function agentwheelResourceRef(gitCommonDir, repositoryRelativePath) {
|
|
940
940
|
const commonDir = resolve5(gitCommonDir);
|
|
941
941
|
const normalizedPath = normalizeRepositoryRelativePath(repositoryRelativePath);
|
|
942
|
-
const
|
|
943
|
-
return `agentwheel-resource:${
|
|
942
|
+
const digest3 = createHash3("sha256").update(commonDir).update("\0").update(normalizedPath).digest("hex");
|
|
943
|
+
return `agentwheel-resource:${digest3}`;
|
|
944
944
|
}
|
|
945
945
|
async function resourceRefsForRepositoryPaths(repositoryRoot, paths) {
|
|
946
946
|
const result = await runProcess("git", [
|
|
@@ -1213,8 +1213,8 @@ async function listMutationReceipts() {
|
|
|
1213
1213
|
}
|
|
1214
1214
|
async function acquireMutationLock(repositoryRoot, operationId) {
|
|
1215
1215
|
const root = mutationStateRoot();
|
|
1216
|
-
const
|
|
1217
|
-
const lockPath = join4(root, "locks", `${
|
|
1216
|
+
const digest3 = createHash4("sha256").update(resolve6(repositoryRoot)).digest("hex");
|
|
1217
|
+
const lockPath = join4(root, "locks", `${digest3}.lock`);
|
|
1218
1218
|
await mkdir2(join4(root, "locks"), { recursive: true, mode: 448 });
|
|
1219
1219
|
await chmod(join4(root, "locks"), 448);
|
|
1220
1220
|
try {
|
|
@@ -1961,13 +1961,13 @@ async function spawnWithInput(command, args, input) {
|
|
|
1961
1961
|
if (result.stderr) throw new Error(result.stderr);
|
|
1962
1962
|
}
|
|
1963
1963
|
function waitForProcess(child, label) {
|
|
1964
|
-
return new Promise((
|
|
1964
|
+
return new Promise((resolve45, reject) => {
|
|
1965
1965
|
const stderr = [];
|
|
1966
1966
|
child.stderr?.on("data", (chunk) => stderr.push(chunk));
|
|
1967
1967
|
child.on("error", reject);
|
|
1968
1968
|
child.on("close", (code) => {
|
|
1969
1969
|
const message = Buffer.concat(stderr).toString("utf8");
|
|
1970
|
-
if (code === 0)
|
|
1970
|
+
if (code === 0) resolve45({ stderr: "" });
|
|
1971
1971
|
else reject(new Error(`${label} exited ${code}${message ? `: ${message}` : ""}`));
|
|
1972
1972
|
});
|
|
1973
1973
|
});
|
|
@@ -2582,7 +2582,7 @@ async function listInstallManifests(targetRoot, adapter, transport = localTransp
|
|
|
2582
2582
|
try {
|
|
2583
2583
|
const raw = JSON.parse(await transport.readFile(path));
|
|
2584
2584
|
const parsed = installManifestSchema.parse(raw);
|
|
2585
|
-
if (parsed.adapter !== adapter) continue;
|
|
2585
|
+
if (adapter !== void 0 && parsed.adapter !== adapter) continue;
|
|
2586
2586
|
found.push({ path, fileName, stateKey, manifest: { ...parsed, revision: computeManifestRevision(raw) } });
|
|
2587
2587
|
} catch (error) {
|
|
2588
2588
|
throw new Error(`Unreadable install manifest at ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -3236,15 +3236,15 @@ async function snapshotRepository(repositoryRoot) {
|
|
|
3236
3236
|
async function collectIntroducedPaths(repositoryRoot, expectedHead, before, declaredPaths) {
|
|
3237
3237
|
const after = await snapshotRepository(repositoryRoot);
|
|
3238
3238
|
const touchedPreexisting = /* @__PURE__ */ new Set();
|
|
3239
|
-
for (const [path,
|
|
3239
|
+
for (const [path, hash2] of before.changed) {
|
|
3240
3240
|
if (declaredPaths.includes(path)) touchedPreexisting.add(path);
|
|
3241
3241
|
else if (!after.changed.has(path)) {
|
|
3242
3242
|
const head = await currentHead(repositoryRoot);
|
|
3243
3243
|
const ancestor = await runProcess("git", ["-C", repositoryRoot, "merge-base", "--is-ancestor", expectedHead, head], {
|
|
3244
3244
|
allowExitCodes: [0, 1]
|
|
3245
3245
|
});
|
|
3246
|
-
if (head === expectedHead || ancestor.exitCode !== 0 || await hashWorkingPath(repositoryRoot, path) !==
|
|
3247
|
-
} else if (after.changed.get(path) !==
|
|
3246
|
+
if (head === expectedHead || ancestor.exitCode !== 0 || await hashWorkingPath(repositoryRoot, path) !== hash2 || await hashHeadPath(repositoryRoot, head, path) !== hash2) touchedPreexisting.add(path);
|
|
3247
|
+
} else if (after.changed.get(path) !== hash2) touchedPreexisting.add(path);
|
|
3248
3248
|
}
|
|
3249
3249
|
if (touchedPreexisting.size > 0) {
|
|
3250
3250
|
throw new Error(`Mutation touched pre-existing dirty paths: ${[...touchedPreexisting].sort().join(", ")}.`);
|
|
@@ -3310,19 +3310,19 @@ async function hashWorkingPath(repositoryRoot, path) {
|
|
|
3310
3310
|
throw new Error(`Unsupported repository path kind: ${path}`);
|
|
3311
3311
|
}
|
|
3312
3312
|
async function hashDirectory(root) {
|
|
3313
|
-
const
|
|
3313
|
+
const hash2 = createHash7("sha256");
|
|
3314
3314
|
async function walk2(dir) {
|
|
3315
3315
|
for (const entry of (await readdir4(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
3316
3316
|
if (entry.name === ".git" || entry.name === "node_modules") continue;
|
|
3317
3317
|
const absolute = resolve12(dir, entry.name);
|
|
3318
3318
|
const relativePath = normalizeRepoPath(relative5(root, absolute));
|
|
3319
3319
|
if (entry.isDirectory()) await walk2(absolute);
|
|
3320
|
-
else if (entry.isSymbolicLink())
|
|
3321
|
-
else if (entry.isFile())
|
|
3320
|
+
else if (entry.isSymbolicLink()) hash2.update(relativePath).update("\0").update(await readlink(absolute)).update("\0");
|
|
3321
|
+
else if (entry.isFile()) hash2.update(relativePath).update("\0").update(await readFile11(absolute)).update("\0");
|
|
3322
3322
|
}
|
|
3323
3323
|
}
|
|
3324
3324
|
await walk2(root);
|
|
3325
|
-
return
|
|
3325
|
+
return hash2.digest("hex");
|
|
3326
3326
|
}
|
|
3327
3327
|
function splitNull(value) {
|
|
3328
3328
|
return value.toString("utf8").split("\0").filter(Boolean);
|
|
@@ -3938,11 +3938,11 @@ function dirtyRepositoryPaths(repositoryRoot, baseline, paths) {
|
|
|
3938
3938
|
function activeMutationMetadata() {
|
|
3939
3939
|
return activeMutation?.metadata();
|
|
3940
3940
|
}
|
|
3941
|
-
async function reserveActiveRuntimeJournal(
|
|
3942
|
-
await activeMutation?.transitionRuntimeJournal(
|
|
3941
|
+
async function reserveActiveRuntimeJournal(link3) {
|
|
3942
|
+
await activeMutation?.transitionRuntimeJournal(link3, "reserved");
|
|
3943
3943
|
}
|
|
3944
|
-
async function activateActiveRuntimeJournal(
|
|
3945
|
-
await activeMutation?.transitionRuntimeJournal(
|
|
3944
|
+
async function activateActiveRuntimeJournal(link3) {
|
|
3945
|
+
await activeMutation?.transitionRuntimeJournal(link3, "pending");
|
|
3946
3946
|
}
|
|
3947
3947
|
async function beginResolveMutationRuntimeJournal(operationId, path) {
|
|
3948
3948
|
await transitionMutationRuntimeJournal(operationId, path, "resolving");
|
|
@@ -4208,14 +4208,14 @@ function receiptStatusForProvider(status) {
|
|
|
4208
4208
|
if (status === "no-repository-delta") return "no-repository-delta";
|
|
4209
4209
|
return "succeeded";
|
|
4210
4210
|
}
|
|
4211
|
-
function upsertRuntimeJournal(entries,
|
|
4212
|
-
const existing = entries.find((entry) => entry.path ===
|
|
4213
|
-
if (existing && (existing.transport !==
|
|
4214
|
-
throw new Error(`Runtime journal link collision for ${
|
|
4211
|
+
function upsertRuntimeJournal(entries, link3, status) {
|
|
4212
|
+
const existing = entries.find((entry) => entry.path === link3.path);
|
|
4213
|
+
if (existing && (existing.transport !== link3.transport || existing.transportDescription !== link3.transportDescription || existing.journalDigest !== link3.journalDigest)) {
|
|
4214
|
+
throw new Error(`Runtime journal link collision for ${link3.path}.`);
|
|
4215
4215
|
}
|
|
4216
4216
|
return [
|
|
4217
|
-
...entries.filter((entry) => entry.path !==
|
|
4218
|
-
{ ...
|
|
4217
|
+
...entries.filter((entry) => entry.path !== link3.path),
|
|
4218
|
+
{ ...link3, status }
|
|
4219
4219
|
].sort((a, b) => a.path.localeCompare(b.path));
|
|
4220
4220
|
}
|
|
4221
4221
|
var GovernedMutation, activeMutation;
|
|
@@ -4416,11 +4416,12 @@ var init_coordinator = __esm({
|
|
|
4416
4416
|
operationId: this.receipt.operationId,
|
|
4417
4417
|
reason: this.receipt.reason,
|
|
4418
4418
|
noCommit: this.receipt.noCommit,
|
|
4419
|
-
revisionMode: this.receipt.revisionMode
|
|
4419
|
+
revisionMode: this.receipt.revisionMode,
|
|
4420
|
+
workspaceRoot: this.receipt.workspaceRoot
|
|
4420
4421
|
};
|
|
4421
4422
|
}
|
|
4422
|
-
async transitionRuntimeJournal(
|
|
4423
|
-
const runtimeJournals = upsertRuntimeJournal(this.receipt.runtimeJournals,
|
|
4423
|
+
async transitionRuntimeJournal(link3, status) {
|
|
4424
|
+
const runtimeJournals = upsertRuntimeJournal(this.receipt.runtimeJournals, link3, status);
|
|
4424
4425
|
this.receipt = await updateMutationReceipt(this.receipt, { runtimeJournals });
|
|
4425
4426
|
}
|
|
4426
4427
|
async transitionExistingRuntimeJournal(path, status) {
|
|
@@ -4488,20 +4489,88 @@ __export(transaction_exports, {
|
|
|
4488
4489
|
applyLockPath: () => applyLockPath,
|
|
4489
4490
|
assertApplyJournalRecoveryAllowed: () => assertApplyJournalRecoveryAllowed,
|
|
4490
4491
|
assertGovernedRuntimeTransportSupported: () => assertGovernedRuntimeTransportSupported,
|
|
4492
|
+
assertNoSymlinkPath: () => assertNoSymlinkPath,
|
|
4491
4493
|
listAllApplyJournals: () => listAllApplyJournals,
|
|
4492
4494
|
listApplyJournals: () => listApplyJournals,
|
|
4493
4495
|
localPathExists: () => localPathExists,
|
|
4496
|
+
metadataAuditPath: () => metadataAuditPath,
|
|
4497
|
+
metadataRuntimePath: () => metadataRuntimePath,
|
|
4494
4498
|
mutationMetadataForApplyJournal: () => mutationMetadataForApplyJournal,
|
|
4499
|
+
ownershipRuntimeSnapshot: () => ownershipRuntimeSnapshot,
|
|
4495
4500
|
readApplyJournal: () => readApplyJournal,
|
|
4496
4501
|
readLinkedLocalApplyJournal: () => readLinkedLocalApplyJournal,
|
|
4497
4502
|
recordBackup: () => recordBackup,
|
|
4498
4503
|
removeApplyJournal: () => removeApplyJournal,
|
|
4499
4504
|
rollbackCompletedOperations: () => rollbackCompletedOperations,
|
|
4505
|
+
syncMetadataPath: () => syncMetadataPath,
|
|
4500
4506
|
writeApplyJournal: () => writeApplyJournal
|
|
4501
4507
|
});
|
|
4502
4508
|
import { createHash as createHash9 } from "crypto";
|
|
4503
|
-
import { cp as cp2, mkdir as mkdir7, readFile as readFile13, rm as rm5, stat as stat4 } from "fs/promises";
|
|
4504
|
-
import { dirname as dirname9, join as join11, resolve as resolve15 } from "path";
|
|
4509
|
+
import { cp as cp2, lstat as lstat2, mkdir as mkdir7, open as open3, readFile as readFile13, readdir as readdir5, rm as rm5, stat as stat4 } from "fs/promises";
|
|
4510
|
+
import { dirname as dirname9, join as join11, relative as relative7, resolve as resolve15, sep as sep5 } from "path";
|
|
4511
|
+
async function assertNoSymlinkPath(path) {
|
|
4512
|
+
const absolute = resolve15(path);
|
|
4513
|
+
let current = absolute;
|
|
4514
|
+
while (true) {
|
|
4515
|
+
try {
|
|
4516
|
+
if ((await lstat2(current)).isSymbolicLink()) throw new Error(`Symlink path is unsupported: ${current}`);
|
|
4517
|
+
} catch (error) {
|
|
4518
|
+
if (error.code !== "ENOENT") throw error;
|
|
4519
|
+
}
|
|
4520
|
+
const parent = dirname9(current);
|
|
4521
|
+
if (parent === current) break;
|
|
4522
|
+
current = parent;
|
|
4523
|
+
}
|
|
4524
|
+
}
|
|
4525
|
+
async function ownershipRuntimeSnapshot(path) {
|
|
4526
|
+
await assertNoSymlinkPath(path);
|
|
4527
|
+
const hash2 = createHash9("sha256");
|
|
4528
|
+
async function visit(candidate) {
|
|
4529
|
+
const info = await lstat2(candidate);
|
|
4530
|
+
if (info.isSymbolicLink() || !info.isDirectory() && !info.isFile()) {
|
|
4531
|
+
throw new Error(`Unsupported ownership runtime path: ${candidate}`);
|
|
4532
|
+
}
|
|
4533
|
+
hash2.update(JSON.stringify([relative7(path, candidate), info.isDirectory() ? "dir" : "file", info.mode & 4095]));
|
|
4534
|
+
if (info.isDirectory()) {
|
|
4535
|
+
for (const name of (await readdir5(candidate)).sort()) await visit(join11(candidate, name));
|
|
4536
|
+
} else {
|
|
4537
|
+
const content = await readFile13(candidate);
|
|
4538
|
+
hash2.update(String(content.length)).update("\0").update(content);
|
|
4539
|
+
}
|
|
4540
|
+
}
|
|
4541
|
+
await visit(path);
|
|
4542
|
+
return hash2.digest("hex");
|
|
4543
|
+
}
|
|
4544
|
+
function metadataAuditPath(audit, path) {
|
|
4545
|
+
mutationOperationIdSchema.parse(audit.operationId);
|
|
4546
|
+
if (resolve15(audit.workspaceRoot) !== audit.workspaceRoot || path !== `.agentwheel/ownership-releases/${audit.operationId}.json`) {
|
|
4547
|
+
throw new Error("Invalid ownership release audit path.");
|
|
4548
|
+
}
|
|
4549
|
+
return join11(audit.workspaceRoot, ...path.split("/"));
|
|
4550
|
+
}
|
|
4551
|
+
function metadataRuntimePath(root, path) {
|
|
4552
|
+
const result = resolve15(root, path);
|
|
4553
|
+
if (!path || relative7(resolve15(root), result).split(sep5).join("/") !== path || path === ".agentwheel" || path.startsWith(".agentwheel/") || result === resolve15(root) || !result.startsWith(`${resolve15(root)}${sep5}`)) {
|
|
4554
|
+
throw new Error(`Unsafe ownership release path: ${path}`);
|
|
4555
|
+
}
|
|
4556
|
+
return result;
|
|
4557
|
+
}
|
|
4558
|
+
async function syncMetadataPath(path, directoryOnly = false) {
|
|
4559
|
+
if (!directoryOnly) {
|
|
4560
|
+
const file = await open3(path, "r");
|
|
4561
|
+
try {
|
|
4562
|
+
await file.sync();
|
|
4563
|
+
} finally {
|
|
4564
|
+
await file.close();
|
|
4565
|
+
}
|
|
4566
|
+
}
|
|
4567
|
+
const directory = await open3(directoryOnly ? path : dirname9(path), "r");
|
|
4568
|
+
try {
|
|
4569
|
+
await directory.sync();
|
|
4570
|
+
} finally {
|
|
4571
|
+
await directory.close();
|
|
4572
|
+
}
|
|
4573
|
+
}
|
|
4505
4574
|
function applyLockPath(targetRoot, adapter, scope = {}) {
|
|
4506
4575
|
const installationType = scope.installationType ?? "local";
|
|
4507
4576
|
return join11(metadataDir(targetRoot), `${stateKeyFor(adapter, { installationType })}.runtime-apply-lock`);
|
|
@@ -4543,30 +4612,31 @@ async function writeApplyJournal(journal, transport = localTransport) {
|
|
|
4543
4612
|
installationType: journal.installationType,
|
|
4544
4613
|
stateKey: journal.stateKey
|
|
4545
4614
|
});
|
|
4546
|
-
let
|
|
4615
|
+
let link3;
|
|
4547
4616
|
if (journal.mutation) {
|
|
4548
|
-
const
|
|
4549
|
-
if (journal.mutation.journalDigest && journal.mutation.journalDigest !==
|
|
4617
|
+
const digest3 = applyJournalLinkDigest(journal, transport);
|
|
4618
|
+
if (journal.mutation.journalDigest && journal.mutation.journalDigest !== digest3) {
|
|
4550
4619
|
throw new Error(`Runtime apply journal identity changed for ${path}.`);
|
|
4551
4620
|
}
|
|
4552
4621
|
if (journal.mutation.transport && (journal.mutation.transport.kind !== transport.kind || journal.mutation.transport.description !== transport.description)) {
|
|
4553
4622
|
throw new Error(`Runtime apply journal transport changed for ${path}.`);
|
|
4554
4623
|
}
|
|
4555
|
-
journal.mutation.journalDigest =
|
|
4624
|
+
journal.mutation.journalDigest = digest3;
|
|
4556
4625
|
journal.mutation.transport = { kind: transport.kind, description: transport.description };
|
|
4557
|
-
|
|
4626
|
+
link3 = {
|
|
4558
4627
|
path,
|
|
4559
4628
|
transport: transport.kind,
|
|
4560
4629
|
transportDescription: transport.description,
|
|
4561
|
-
journalDigest:
|
|
4630
|
+
journalDigest: digest3
|
|
4562
4631
|
};
|
|
4563
|
-
await reserveActiveRuntimeJournal(
|
|
4632
|
+
await reserveActiveRuntimeJournal(link3);
|
|
4564
4633
|
}
|
|
4565
4634
|
await transport.writeJsonAtomic(path, {
|
|
4566
4635
|
...journal,
|
|
4567
4636
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4568
4637
|
});
|
|
4569
|
-
if (
|
|
4638
|
+
if ((journal.metadataAudit || journal.metadataGuards) && transport.kind === "local") await syncMetadataPath(path);
|
|
4639
|
+
if (link3) await activateActiveRuntimeJournal(link3);
|
|
4570
4640
|
}
|
|
4571
4641
|
async function readApplyJournal(targetRoot, adapter, transport = localTransport, scope = {}) {
|
|
4572
4642
|
const path = applyJournalPath(targetRoot, adapter, scope);
|
|
@@ -4593,7 +4663,7 @@ async function listAllApplyJournals(targetRoot, adapter, transport = localTransp
|
|
|
4593
4663
|
if (!fileName.endsWith(suffix)) continue;
|
|
4594
4664
|
const path = join11(metadataDir(targetRoot), fileName);
|
|
4595
4665
|
const journal = parseApplyJournal(JSON.parse(await transport.readFile(path)));
|
|
4596
|
-
if (journal.adapter === adapter) found.push({ path, journal });
|
|
4666
|
+
if (adapter === void 0 || journal.adapter === adapter) found.push({ path, journal });
|
|
4597
4667
|
}
|
|
4598
4668
|
return found.sort((a, b) => a.path.localeCompare(b.path));
|
|
4599
4669
|
}
|
|
@@ -4632,6 +4702,7 @@ async function removeApplyJournal(targetRoot, adapter, transport = localTranspor
|
|
|
4632
4702
|
if (existing?.mutation) await beginResolveMutationRuntimeJournal(existing.mutation.operationId, path);
|
|
4633
4703
|
await transport.rm(applyJournalPath(targetRoot, adapter, scope));
|
|
4634
4704
|
await transport.rm(applyBackupDir(targetRoot, adapter, scope));
|
|
4705
|
+
if ((existing?.metadataAudit || existing?.metadataGuards) && transport.kind === "local") await syncMetadataPath(dirname9(path), true);
|
|
4635
4706
|
if (existing?.mutation) await resolveMutationRuntimeJournal(existing.mutation.operationId, path);
|
|
4636
4707
|
}
|
|
4637
4708
|
async function abortApplyJournal(targetRoot, adapter, transport = localTransport, scope = {}) {
|
|
@@ -4644,6 +4715,9 @@ async function abortApplyJournal(targetRoot, adapter, transport = localTransport
|
|
|
4644
4715
|
const archivePath = join11(metadataDir(targetRoot), "archive", `${stateKey}.apply-journal.failed-${journalTimestamp(/* @__PURE__ */ new Date())}.json`);
|
|
4645
4716
|
const content = await transport.readFile(journalPath);
|
|
4646
4717
|
const journal = parseApplyJournal(JSON.parse(content));
|
|
4718
|
+
if (journal.metadataAudit || journal.metadataGuards) {
|
|
4719
|
+
throw new Error("A guarded metadata transaction must be recovered and verified; abort cannot discard its partial state.");
|
|
4720
|
+
}
|
|
4647
4721
|
await transport.writeFileAtomic(archivePath, content.endsWith("\n") ? content : `${content}
|
|
4648
4722
|
`);
|
|
4649
4723
|
if (journal.mutation) await beginResolveMutationRuntimeJournal(journal.mutation.operationId, journalPath);
|
|
@@ -4695,7 +4769,9 @@ function applyJournalLinkDigest(journal, transport) {
|
|
|
4695
4769
|
graphLockRemovePath: journal.graphLockRemovePath ?? null,
|
|
4696
4770
|
stateRemoveKey: journal.stateRemoveKey ?? null,
|
|
4697
4771
|
workspaceConfigPath: journal.workspaceConfigPath ?? null,
|
|
4698
|
-
operations: journal.operations
|
|
4772
|
+
operations: journal.operations,
|
|
4773
|
+
...journal.metadataAudit ? { metadataAudit: journal.metadataAudit, manifest: journal.manifest } : {},
|
|
4774
|
+
...journal.metadataGuards ? { metadataGuards: journal.metadataGuards, manifest: journal.manifest } : {}
|
|
4699
4775
|
})).digest("hex");
|
|
4700
4776
|
}
|
|
4701
4777
|
function assertApplyJournalRecoveryAllowed(journal) {
|
|
@@ -5363,7 +5439,56 @@ var init_desired = __esm({
|
|
|
5363
5439
|
import { createHash as createHash10 } from "crypto";
|
|
5364
5440
|
import { mkdtemp as mkdtemp2, readFile as readFile17, realpath, rm as rm6, writeFile as writeFile11 } from "fs/promises";
|
|
5365
5441
|
import { tmpdir as tmpdir3 } from "os";
|
|
5366
|
-
import { basename as basename2, dirname as dirname13, join as join12, relative as
|
|
5442
|
+
import { basename as basename2, dirname as dirname13, join as join12, relative as relative8 } from "path";
|
|
5443
|
+
function exactManagedInstructionBlockSnapshot(content, selector) {
|
|
5444
|
+
const beginCount = [...content.matchAll(new RegExp(`<!-- BEGIN openpack:include ${escapeRegex(selector)}(?=\\s|-->)`, "gu"))].length;
|
|
5445
|
+
const endCount = [...content.matchAll(new RegExp(`<!-- END openpack:include ${escapeRegex(selector)}(?=\\s|-->)`, "gu"))].length;
|
|
5446
|
+
if (beginCount !== 1 || endCount !== 1) throw new Error("Duplicate or missing managed instruction markers; adoption is refused.");
|
|
5447
|
+
const stack = [];
|
|
5448
|
+
let selectedBegin;
|
|
5449
|
+
let selectedEnd;
|
|
5450
|
+
let begins = 0;
|
|
5451
|
+
let ends = 0;
|
|
5452
|
+
for (const token of content.matchAll(/<!-- (BEGIN|END) openpack:include ([^\r\n]*?) -->/gu)) {
|
|
5453
|
+
const at = token.index;
|
|
5454
|
+
if (token[1] === "BEGIN") {
|
|
5455
|
+
const begin = /^(.*?) sha256:([a-f0-9]{64})$/u.exec(token[2]);
|
|
5456
|
+
if (!begin) throw new Error("Malformed managed instruction begin marker; adoption is refused.");
|
|
5457
|
+
const name = begin[1];
|
|
5458
|
+
if (name === selector) {
|
|
5459
|
+
if (++begins !== 1 || stack.length !== 0) throw new Error("Duplicate or overlapping managed instruction block; adoption is refused.");
|
|
5460
|
+
let bodyStart = at + token[0].length;
|
|
5461
|
+
if (content.slice(bodyStart, bodyStart + 2) === "\r\n") bodyStart += 2;
|
|
5462
|
+
else if (content[bodyStart] === "\n") bodyStart++;
|
|
5463
|
+
selectedBegin = { start: at, bodyStart, hash: begin[2] };
|
|
5464
|
+
}
|
|
5465
|
+
stack.push(name);
|
|
5466
|
+
} else {
|
|
5467
|
+
const name = token[2];
|
|
5468
|
+
if (stack.pop() !== name) throw new Error("Overlapping or unmatched managed instruction markers; adoption is refused.");
|
|
5469
|
+
if (name === selector) {
|
|
5470
|
+
if (++ends !== 1 || !selectedBegin) throw new Error("Duplicate or missing managed instruction marker; adoption is refused.");
|
|
5471
|
+
let end = at + token[0].length;
|
|
5472
|
+
if (content.slice(end, end + 2) === "\r\n") end += 2;
|
|
5473
|
+
else if (content[end] === "\n") end++;
|
|
5474
|
+
selectedEnd = { bodyEnd: at, end };
|
|
5475
|
+
}
|
|
5476
|
+
}
|
|
5477
|
+
}
|
|
5478
|
+
if (stack.length || begins !== 1 || ends !== 1 || !selectedBegin || !selectedEnd) {
|
|
5479
|
+
throw new Error(`Missing or incomplete managed instruction block for ${selector}; adoption is refused.`);
|
|
5480
|
+
}
|
|
5481
|
+
const hash2 = hashText(content.slice(selectedBegin.bodyStart, selectedEnd.bodyEnd));
|
|
5482
|
+
if (hash2 !== selectedBegin.hash) throw new Error(`Managed instruction block drift detected for ${selector}; adoption is refused.`);
|
|
5483
|
+
return {
|
|
5484
|
+
selector,
|
|
5485
|
+
hash: hash2,
|
|
5486
|
+
markerHash: selectedBegin.hash,
|
|
5487
|
+
prefixHash: hashText(content.slice(0, selectedBegin.start)),
|
|
5488
|
+
suffixHash: hashText(content.slice(selectedEnd.end)),
|
|
5489
|
+
blockBytesHash: hashText(content.slice(selectedBegin.start, selectedEnd.end))
|
|
5490
|
+
};
|
|
5491
|
+
}
|
|
5367
5492
|
async function desiredManagedInstructionBlockHash(sourcePath) {
|
|
5368
5493
|
const source = await readFile17(sourcePath, "utf8");
|
|
5369
5494
|
return hashText(managedBlockBody(source));
|
|
@@ -5375,13 +5500,13 @@ async function readManagedInstructionBlockState(destPath, selector, transport) {
|
|
|
5375
5500
|
const content = await transport.readFile(destPath);
|
|
5376
5501
|
const block = findManagedInstructionBlock(content, selector);
|
|
5377
5502
|
if (!block) return { exists: true, hasBlock: false, drifted: false };
|
|
5378
|
-
const
|
|
5503
|
+
const hash2 = hashText(block.body);
|
|
5379
5504
|
return {
|
|
5380
5505
|
exists: true,
|
|
5381
5506
|
hasBlock: true,
|
|
5382
|
-
hash,
|
|
5507
|
+
hash: hash2,
|
|
5383
5508
|
markerHash: block.markerHash,
|
|
5384
|
-
drifted:
|
|
5509
|
+
drifted: hash2 !== block.markerHash
|
|
5385
5510
|
};
|
|
5386
5511
|
}
|
|
5387
5512
|
async function writeManagedInstructionBlock(sourcePath, destPath, selector, transport, options = {}) {
|
|
@@ -5420,12 +5545,12 @@ function managedInstructionSelector(selector, artifactType, artifactName) {
|
|
|
5420
5545
|
}
|
|
5421
5546
|
function renderManagedInstructionBlock(selector, source) {
|
|
5422
5547
|
const body = managedBlockBody(source);
|
|
5423
|
-
const
|
|
5548
|
+
const hash2 = hashText(body);
|
|
5424
5549
|
return {
|
|
5425
|
-
block: `<!-- BEGIN openpack:include ${selector} sha256:${
|
|
5550
|
+
block: `<!-- BEGIN openpack:include ${selector} sha256:${hash2} -->
|
|
5426
5551
|
${body}<!-- END openpack:include ${selector} -->
|
|
5427
5552
|
`,
|
|
5428
|
-
hash
|
|
5553
|
+
hash: hash2
|
|
5429
5554
|
};
|
|
5430
5555
|
}
|
|
5431
5556
|
function upsertManagedInstructionBlock(content, selector, block, options) {
|
|
@@ -5507,7 +5632,7 @@ function referencesAgentsMd(content, claudePath, agentsPath) {
|
|
|
5507
5632
|
const cleaned = referenced.trim().replace(/^["']|["']$/g, "");
|
|
5508
5633
|
if (!/AGENTS\.md$/i.test(cleaned)) continue;
|
|
5509
5634
|
const candidate = cleaned.startsWith("/") ? cleaned : join12(claudeDir, cleaned);
|
|
5510
|
-
if (
|
|
5635
|
+
if (relative8(dirname13(agentsPath), candidate).replaceAll("\\", "/") === "AGENTS.md") return true;
|
|
5511
5636
|
if (candidate === agentsPath) return true;
|
|
5512
5637
|
}
|
|
5513
5638
|
return false;
|
|
@@ -5564,8 +5689,8 @@ __export(apply_exports, {
|
|
|
5564
5689
|
uninstall: () => uninstall
|
|
5565
5690
|
});
|
|
5566
5691
|
import { execFile as execFile4 } from "child_process";
|
|
5567
|
-
import { createHash as createHash11 } from "crypto";
|
|
5568
|
-
import { mkdtemp as mkdtemp3, readFile as readFile18, rm as rm7, writeFile as writeFile12 } from "fs/promises";
|
|
5692
|
+
import { createHash as createHash11, randomUUID as randomUUID3 } from "crypto";
|
|
5693
|
+
import { link as link2, mkdir as mkdir11, mkdtemp as mkdtemp3, open as open4, readFile as readFile18, rm as rm7, writeFile as writeFile12 } from "fs/promises";
|
|
5569
5694
|
import { tmpdir as tmpdir4 } from "os";
|
|
5570
5695
|
import { basename as basename3, dirname as dirname14, join as join13, resolve as resolve16 } from "path";
|
|
5571
5696
|
import { promisify as promisify4 } from "util";
|
|
@@ -5838,20 +5963,35 @@ async function commitManifestMetadataJournal(journal, transport = localTransport
|
|
|
5838
5963
|
async function commitJournalState(journal, transport, entries, now) {
|
|
5839
5964
|
const manifest = withManifestRevision({
|
|
5840
5965
|
...journal.manifest,
|
|
5841
|
-
generatedAt: now,
|
|
5966
|
+
generatedAt: journal.metadataAudit || journal.metadataGuards ? journal.createdAt : now,
|
|
5842
5967
|
entries: entries ? entries.sort((a, b) => a.path.localeCompare(b.path)) : journal.manifest.entries
|
|
5843
5968
|
});
|
|
5844
|
-
if (journal.
|
|
5969
|
+
if (journal.metadataGuards) await assertMetadataStateGuards(journal, manifest, journal.metadataGuards, transport);
|
|
5970
|
+
if (journal.metadataAudit) {
|
|
5971
|
+
await assertMetadataAuditGuards(journal, manifest, transport);
|
|
5972
|
+
for (const file of journal.metadataAudit.auditFiles) {
|
|
5973
|
+
const path = metadataAuditPath(journal.metadataAudit, file.path);
|
|
5974
|
+
declareMutationPath(path);
|
|
5975
|
+
await writeAuditFileExclusive(path, file.content);
|
|
5976
|
+
}
|
|
5977
|
+
await assertMetadataAuditGuards(journal, manifest, transport);
|
|
5978
|
+
}
|
|
5979
|
+
if (journal.mode === "uninstall" && manifest.entries.length === 0 && !journal.metadataAudit) {
|
|
5845
5980
|
await removeStateFiles(journal.targetRoot, journal.adapter, transport, {
|
|
5846
5981
|
installationType: journal.installationType,
|
|
5847
5982
|
stateKey: journal.stateKey
|
|
5848
5983
|
});
|
|
5984
|
+
if (journal.metadataGuards) await syncMetadataPath(metadataDir(journal.targetRoot), true);
|
|
5849
5985
|
} else {
|
|
5850
5986
|
if (journal.sourceLock) await writeSourceLock(journal.targetRoot, journal.adapter, journal.sourceLock, transport, {
|
|
5851
5987
|
installationType: journal.installationType,
|
|
5852
5988
|
stateKey: journal.stateKey
|
|
5853
5989
|
});
|
|
5854
5990
|
await writeInstallManifest(manifest, transport);
|
|
5991
|
+
if (journal.metadataAudit || journal.metadataGuards) await syncMetadataPath(installManifestPath(journal.targetRoot, journal.adapter, {
|
|
5992
|
+
installationType: journal.installationType,
|
|
5993
|
+
stateKey: journal.stateKey
|
|
5994
|
+
}));
|
|
5855
5995
|
}
|
|
5856
5996
|
if (journal.graphLockPath && journal.graphLock) await writeGraphLock(journal.graphLockPath, journal.graphLock);
|
|
5857
5997
|
if (journal.stateRemoveKey && journal.stateRemoveKey !== journal.stateKey) {
|
|
@@ -5869,6 +6009,8 @@ async function commitJournalState(journal, transport, entries, now) {
|
|
|
5869
6009
|
await writeJsonAtomic(journal.workspaceConfigPath, journal.workspaceConfig);
|
|
5870
6010
|
}
|
|
5871
6011
|
const verifiedManifest = await assertCommittedJournalState(journal, manifest, transport);
|
|
6012
|
+
if (journal.metadataAudit) await assertMetadataAuditGuards(journal, manifest, transport, true);
|
|
6013
|
+
if (journal.metadataGuards) await assertMetadataStateGuards(journal, manifest, journal.metadataGuards, transport, true);
|
|
5872
6014
|
await removeApplyJournal(journal.targetRoot, journal.adapter, transport, {
|
|
5873
6015
|
installationType: journal.installationType,
|
|
5874
6016
|
stateKey: journal.stateKey
|
|
@@ -5884,7 +6026,7 @@ async function commitJournalState(journal, transport, entries, now) {
|
|
|
5884
6026
|
async function assertCommittedJournalState(journal, expectedManifest, transport) {
|
|
5885
6027
|
const scope = { installationType: journal.installationType, stateKey: journal.stateKey };
|
|
5886
6028
|
const actualManifest = await readInstallManifest(journal.targetRoot, journal.adapter, transport, scope);
|
|
5887
|
-
if (journal.mode === "uninstall" && expectedManifest.entries.length === 0) {
|
|
6029
|
+
if (journal.mode === "uninstall" && expectedManifest.entries.length === 0 && !journal.metadataAudit) {
|
|
5888
6030
|
if (actualManifest) throw new Error("Uninstall postcheck found an install manifest that should have been removed.");
|
|
5889
6031
|
} else if (!actualManifest) {
|
|
5890
6032
|
throw new Error(
|
|
@@ -5924,6 +6066,88 @@ async function assertCommittedJournalState(journal, expectedManifest, transport)
|
|
|
5924
6066
|
}
|
|
5925
6067
|
return actualManifest;
|
|
5926
6068
|
}
|
|
6069
|
+
async function assertMetadataAuditGuards(journal, after, transport, requireAfter = false) {
|
|
6070
|
+
const audit = journal.metadataAudit;
|
|
6071
|
+
const sha = /^[a-f0-9]{64}$/;
|
|
6072
|
+
if (transport.kind !== "local" || audit.kind !== "ownership-release" || after.adapter !== journal.adapter || after.targetRoot !== journal.targetRoot || after.installationType !== journal.installationType || after.stateKey !== journal.stateKey || journal.mode !== "uninstall" || journal.operations.length || journal.completed.length || journal.sourceLock || journal.graphLock || journal.graphLockPath || journal.graphLockRemovePath || journal.stateRemoveKey || journal.workspaceConfigPath || journal.workspaceConfig || !sha.test(audit.planDigest) || !sha.test(journal.baseRevision ?? "") || audit.auditFiles.length !== 1 || audit.runtime.length !== 1 || !audit.inventory.length) {
|
|
6073
|
+
throw new Error("Invalid metadata-audit transaction; no state was committed.");
|
|
6074
|
+
}
|
|
6075
|
+
await assertMetadataStateGuards(journal, after, audit, transport, requireAfter);
|
|
6076
|
+
for (const file of audit.auditFiles) {
|
|
6077
|
+
const path = metadataAuditPath(audit, file.path);
|
|
6078
|
+
await assertNoSymlinkPath(path);
|
|
6079
|
+
if (!sha.test(file.sha256) || createHash11("sha256").update(file.content).digest("hex") !== file.sha256) {
|
|
6080
|
+
throw new Error("Metadata-audit content digest is invalid.");
|
|
6081
|
+
}
|
|
6082
|
+
try {
|
|
6083
|
+
if (await readFile18(path, "utf8") !== file.content) throw new Error(`Metadata-audit file changed: ${path}`);
|
|
6084
|
+
} catch (error) {
|
|
6085
|
+
if (error.code !== "ENOENT" || requireAfter) throw error;
|
|
6086
|
+
}
|
|
6087
|
+
}
|
|
6088
|
+
}
|
|
6089
|
+
async function assertMetadataStateGuards(journal, after, guards, transport, requireAfter = false) {
|
|
6090
|
+
const sha = /^[a-f0-9]{64}$/;
|
|
6091
|
+
if (transport.kind !== "local" || journal.metadataAudit && journal.metadataGuards || after.adapter !== journal.adapter || after.targetRoot !== journal.targetRoot || after.installationType !== journal.installationType || after.stateKey !== journal.stateKey || journal.mode !== "uninstall" || journal.operations.length || journal.completed.length || journal.sourceLock || journal.graphLock || journal.graphLockPath || journal.graphLockRemovePath || journal.stateRemoveKey || journal.workspaceConfigPath || journal.workspaceConfig || journal.baseRevision !== null && !sha.test(journal.baseRevision) || !guards.inventory.length || !guards.runtime.length) {
|
|
6092
|
+
throw new Error("Invalid guarded metadata transaction; no state was committed.");
|
|
6093
|
+
}
|
|
6094
|
+
await assertNoSymlinkPath(journal.targetRoot);
|
|
6095
|
+
await assertNoSymlinkPath(metadataDir(journal.targetRoot));
|
|
6096
|
+
const inventory = await listInstallManifests(journal.targetRoot, void 0, transport);
|
|
6097
|
+
for (const item of inventory) await assertNoSymlinkPath(item.path);
|
|
6098
|
+
const before = new Map(guards.inventory.map((item) => [item.stateKey, item.revision]));
|
|
6099
|
+
if (before.size !== guards.inventory.length || [...before.values()].some((revision) => !sha.test(revision)) || (before.get(journal.stateKey) ?? null) !== journal.baseRevision) {
|
|
6100
|
+
throw new Error("Invalid guarded metadata inventory baseline.");
|
|
6101
|
+
}
|
|
6102
|
+
const next = new Map(before);
|
|
6103
|
+
if (after.entries.length === 0 && !journal.metadataAudit) next.delete(journal.stateKey);
|
|
6104
|
+
else next.set(journal.stateKey, after.revision);
|
|
6105
|
+
const actual = new Map(inventory.map((item) => [item.stateKey, item.manifest.revision]));
|
|
6106
|
+
const equals = (expected) => actual.size === expected.size && [...expected].every(([key, revision]) => actual.get(key) === revision);
|
|
6107
|
+
if (!equals(next) && (requireAfter || !equals(before))) {
|
|
6108
|
+
throw new Error("Metadata inventory/manifest CAS failed; concurrent state was preserved.");
|
|
6109
|
+
}
|
|
6110
|
+
for (const guard of guards.runtime) {
|
|
6111
|
+
const path = metadataRuntimePath(journal.targetRoot, guard.path);
|
|
6112
|
+
if (!sha.test(guard.hash) || !sha.test(guard.snapshotDigest) || await ownershipRuntimeSnapshot(path) !== guard.snapshotDigest || await transport.hashPath(path) !== guard.hash) {
|
|
6113
|
+
throw new Error(`Metadata runtime CAS failed at ${guard.path}; runtime was preserved.`);
|
|
6114
|
+
}
|
|
6115
|
+
if (guard.managedBlock) {
|
|
6116
|
+
const current = exactManagedInstructionBlockSnapshot(await transport.readFile(path), guard.managedBlock.selector);
|
|
6117
|
+
if (canonicalJson5(current) !== canonicalJson5(guard.managedBlock)) {
|
|
6118
|
+
throw new Error(`Managed instruction snapshot CAS failed at ${guard.path}.`);
|
|
6119
|
+
}
|
|
6120
|
+
}
|
|
6121
|
+
}
|
|
6122
|
+
}
|
|
6123
|
+
async function writeAuditFileExclusive(path, content) {
|
|
6124
|
+
await assertNoSymlinkPath(path);
|
|
6125
|
+
await mkdir11(dirname14(path), { recursive: true });
|
|
6126
|
+
await syncMetadataPath(dirname14(dirname14(path)), true);
|
|
6127
|
+
const temporary = `${path}.pending-${randomUUID3()}`;
|
|
6128
|
+
try {
|
|
6129
|
+
const handle = await open4(temporary, "wx", 384);
|
|
6130
|
+
try {
|
|
6131
|
+
await handle.writeFile(content, "utf8");
|
|
6132
|
+
await handle.sync();
|
|
6133
|
+
} finally {
|
|
6134
|
+
await handle.close();
|
|
6135
|
+
}
|
|
6136
|
+
try {
|
|
6137
|
+
await link2(temporary, path);
|
|
6138
|
+
} catch (error) {
|
|
6139
|
+
if (error.code !== "EEXIST" || await readFile18(path, "utf8") !== content) throw error;
|
|
6140
|
+
}
|
|
6141
|
+
const directory = await open4(dirname14(path), "r");
|
|
6142
|
+
try {
|
|
6143
|
+
await directory.sync();
|
|
6144
|
+
} finally {
|
|
6145
|
+
await directory.close();
|
|
6146
|
+
}
|
|
6147
|
+
} finally {
|
|
6148
|
+
await rm7(temporary, { force: true });
|
|
6149
|
+
}
|
|
6150
|
+
}
|
|
5927
6151
|
function canonicalJson5(value) {
|
|
5928
6152
|
if (Array.isArray(value)) return `[${value.map((item) => canonicalJson5(item === void 0 ? null : item)).join(",")}]`;
|
|
5929
6153
|
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
@@ -5973,13 +6197,13 @@ async function applyOperation(operation, context) {
|
|
|
5973
6197
|
}
|
|
5974
6198
|
if (operation.mode === managedInstructionBlockMode) {
|
|
5975
6199
|
const selector = managedInstructionSelector(operation.logicalSelector, operation.artifactType, operation.artifactName);
|
|
5976
|
-
const
|
|
5977
|
-
if (
|
|
5978
|
-
throw new Error(`Managed block hash verification failed for ${operation.relativeDestPath}: expected ${operation.desiredHash}, got ${
|
|
6200
|
+
const hash3 = await writeManagedInstructionBlock(operation.sourcePath, operation.destPath, selector, transport, managedBlockMutationOptions(operation));
|
|
6201
|
+
if (hash3 !== operation.desiredHash) {
|
|
6202
|
+
throw new Error(`Managed block hash verification failed for ${operation.relativeDestPath}: expected ${operation.desiredHash}, got ${hash3}`);
|
|
5979
6203
|
}
|
|
5980
6204
|
return manifestEntryForOperation(operation, {
|
|
5981
6205
|
now,
|
|
5982
|
-
hash:
|
|
6206
|
+
hash: hash3,
|
|
5983
6207
|
sourceHash: operation.desiredHash,
|
|
5984
6208
|
graphLockDigest: context.graphLockDigest
|
|
5985
6209
|
});
|
|
@@ -5997,13 +6221,13 @@ async function applyOperation(operation, context) {
|
|
|
5997
6221
|
} else {
|
|
5998
6222
|
await transport.atomicCopy(operation.sourcePath, operation.destPath, operation.kind);
|
|
5999
6223
|
}
|
|
6000
|
-
const
|
|
6001
|
-
if (!operation.mergeStrategy &&
|
|
6002
|
-
throw new Error(`Hash verification failed for ${operation.relativeDestPath}: expected ${operation.desiredHash}, got ${
|
|
6224
|
+
const hash2 = await transport.hashPath(operation.destPath);
|
|
6225
|
+
if (!operation.mergeStrategy && hash2 !== operation.desiredHash) {
|
|
6226
|
+
throw new Error(`Hash verification failed for ${operation.relativeDestPath}: expected ${operation.desiredHash}, got ${hash2}`);
|
|
6003
6227
|
}
|
|
6004
6228
|
return manifestEntryForOperation(operation, {
|
|
6005
6229
|
now,
|
|
6006
|
-
hash,
|
|
6230
|
+
hash: hash2,
|
|
6007
6231
|
sourceHash: operation.desiredHash,
|
|
6008
6232
|
graphLockDigest: context.graphLockDigest
|
|
6009
6233
|
});
|
|
@@ -6599,6 +6823,7 @@ var init_apply = __esm({
|
|
|
6599
6823
|
init_openclaw_json_merge();
|
|
6600
6824
|
init_manifest2();
|
|
6601
6825
|
init_path_safety();
|
|
6826
|
+
init_paths();
|
|
6602
6827
|
init_transaction();
|
|
6603
6828
|
init_toml_merge();
|
|
6604
6829
|
init_yaml_merge();
|
|
@@ -6647,11 +6872,11 @@ var init_identify = __esm({
|
|
|
6647
6872
|
});
|
|
6648
6873
|
|
|
6649
6874
|
// src/cli/index.ts
|
|
6650
|
-
import { createHash as
|
|
6875
|
+
import { createHash as createHash28 } from "crypto";
|
|
6651
6876
|
import { existsSync } from "fs";
|
|
6652
|
-
import { mkdir as
|
|
6877
|
+
import { mkdir as mkdir27, mkdtemp as mkdtemp6, realpath as realpath4, rm as rm17, writeFile as writeFile24 } from "fs/promises";
|
|
6653
6878
|
import { homedir as homedir18, tmpdir as tmpdir7 } from "os";
|
|
6654
|
-
import { basename as basename24, dirname as dirname37, isAbsolute as isAbsolute14, join as
|
|
6879
|
+
import { basename as basename24, dirname as dirname37, isAbsolute as isAbsolute14, join as join61, relative as relative19, resolve as resolve44 } from "path";
|
|
6655
6880
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
6656
6881
|
import { Command } from "commander";
|
|
6657
6882
|
|
|
@@ -6680,8 +6905,8 @@ async function loadProgrammaticAdapter(modulePath, options) {
|
|
|
6680
6905
|
if (!stats.isFile()) {
|
|
6681
6906
|
throw new Error(`Programmatic adapter module is not a file: ${resolvedPath}`);
|
|
6682
6907
|
}
|
|
6683
|
-
const
|
|
6684
|
-
const importPath = extname(resolvedPath) === ".ts" ? await transpileTypeScriptAdapter(resolvedPath,
|
|
6908
|
+
const hash2 = await hashPath(resolvedPath);
|
|
6909
|
+
const importPath = extname(resolvedPath) === ".ts" ? await transpileTypeScriptAdapter(resolvedPath, hash2) : resolvedPath;
|
|
6685
6910
|
const imported = await import(`${pathToFileURL(importPath).href}?agentwheel=${Date.now()}`);
|
|
6686
6911
|
const candidate = imported.adapter;
|
|
6687
6912
|
if (!candidate || typeof candidate !== "object") {
|
|
@@ -6690,7 +6915,7 @@ async function loadProgrammaticAdapter(modulePath, options) {
|
|
|
6690
6915
|
const parsed = adapterSchema.parse(candidate);
|
|
6691
6916
|
const runtime = {
|
|
6692
6917
|
modulePath: resolvedPath,
|
|
6693
|
-
hash,
|
|
6918
|
+
hash: hash2,
|
|
6694
6919
|
capabilities: Array.isArray(candidate.capabilities) ? candidate.capabilities.filter((item) => typeof item === "string") : [],
|
|
6695
6920
|
plan: typeof candidate.plan === "function" ? candidate.plan.bind(candidate) : void 0,
|
|
6696
6921
|
apply: typeof candidate.apply === "function" ? candidate.apply.bind(candidate) : void 0,
|
|
@@ -6698,7 +6923,7 @@ async function loadProgrammaticAdapter(modulePath, options) {
|
|
|
6698
6923
|
};
|
|
6699
6924
|
return { ...parsed, programmatic: runtime };
|
|
6700
6925
|
}
|
|
6701
|
-
async function transpileTypeScriptAdapter(modulePath,
|
|
6926
|
+
async function transpileTypeScriptAdapter(modulePath, hash2) {
|
|
6702
6927
|
const source = await readFile3(modulePath, "utf8");
|
|
6703
6928
|
const output = ts.transpileModule(source, {
|
|
6704
6929
|
compilerOptions: {
|
|
@@ -6710,7 +6935,7 @@ async function transpileTypeScriptAdapter(modulePath, hash) {
|
|
|
6710
6935
|
},
|
|
6711
6936
|
fileName: modulePath
|
|
6712
6937
|
});
|
|
6713
|
-
const outPath = join2(tmpdir(), `agentwheel-adapter-${
|
|
6938
|
+
const outPath = join2(tmpdir(), `agentwheel-adapter-${hash2}.mjs`);
|
|
6714
6939
|
await writeFile2(outPath, output.outputText, "utf8");
|
|
6715
6940
|
return outPath;
|
|
6716
6941
|
}
|
|
@@ -6739,11 +6964,11 @@ init_manifest2();
|
|
|
6739
6964
|
// src/install/plan.ts
|
|
6740
6965
|
init_adapter();
|
|
6741
6966
|
init_manifest();
|
|
6742
|
-
import { basename as basename8, join as join24, relative as
|
|
6967
|
+
import { basename as basename8, join as join24, relative as relative9 } from "path";
|
|
6743
6968
|
|
|
6744
6969
|
// src/staging/codex-subagents.ts
|
|
6745
6970
|
init_fs();
|
|
6746
|
-
import { mkdir as
|
|
6971
|
+
import { mkdir as mkdir12, readFile as readFile19, writeFile as writeFile13 } from "fs/promises";
|
|
6747
6972
|
import { basename as basename4, dirname as dirname15, join as join14 } from "path";
|
|
6748
6973
|
import { parse as parseYaml } from "yaml";
|
|
6749
6974
|
var requiredCodexAgentFields = ["name", "description", "developer_instructions"];
|
|
@@ -6790,7 +7015,7 @@ async function renderCodexSubagent(artifact, stageRoot) {
|
|
|
6790
7015
|
}
|
|
6791
7016
|
const markdown = await readFile19(markdownPath, "utf8");
|
|
6792
7017
|
const toml = markdownToCodexAgentToml(agentName, markdown);
|
|
6793
|
-
await
|
|
7018
|
+
await mkdir12(dirname15(renderedPath), { recursive: true });
|
|
6794
7019
|
await writeFile13(renderedPath, toml, "utf8");
|
|
6795
7020
|
return {
|
|
6796
7021
|
...artifact,
|
|
@@ -6872,7 +7097,7 @@ function escapeRegExp2(value) {
|
|
|
6872
7097
|
|
|
6873
7098
|
// src/staging/copilot-artifacts.ts
|
|
6874
7099
|
init_fs();
|
|
6875
|
-
import { mkdir as
|
|
7100
|
+
import { mkdir as mkdir13, readFile as readFile20, writeFile as writeFile14 } from "fs/promises";
|
|
6876
7101
|
import { basename as basename5, dirname as dirname16, join as join15 } from "path";
|
|
6877
7102
|
async function renderCopilotArtifacts(artifacts, stageRoot, adapter) {
|
|
6878
7103
|
if (adapter?.name !== "copilot") return artifacts;
|
|
@@ -6904,7 +7129,7 @@ async function renderCopilotSubagent(artifact, stageRoot) {
|
|
|
6904
7129
|
throw new Error(`Copilot subagent ${artifact.relativePath} must be a .md file or directory containing AGENTS.md.`);
|
|
6905
7130
|
}
|
|
6906
7131
|
const markdown = await readFile20(markdownPath, "utf8");
|
|
6907
|
-
await
|
|
7132
|
+
await mkdir13(dirname16(renderedPath), { recursive: true });
|
|
6908
7133
|
await writeFile14(renderedPath, ensureCopilotAgentDescription(agentName, markdown), "utf8");
|
|
6909
7134
|
return {
|
|
6910
7135
|
...artifact,
|
|
@@ -6959,7 +7184,7 @@ function yamlString(value) {
|
|
|
6959
7184
|
|
|
6960
7185
|
// src/staging/openclaw-subagents.ts
|
|
6961
7186
|
init_fs();
|
|
6962
|
-
import { mkdir as
|
|
7187
|
+
import { mkdir as mkdir14, readFile as readFile21, writeFile as writeFile15 } from "fs/promises";
|
|
6963
7188
|
import { basename as basename6, dirname as dirname17, join as join16 } from "path";
|
|
6964
7189
|
async function renderOpenClawSubagents(artifacts, stageRoot, adapter) {
|
|
6965
7190
|
if (adapter?.name !== "openclaw") return artifacts;
|
|
@@ -6994,7 +7219,7 @@ async function renderOpenClawSubagent(artifact, stageRoot) {
|
|
|
6994
7219
|
|
|
6995
7220
|
${parsed.description ?? `OpenClaw subagent ${agentId}.`}`;
|
|
6996
7221
|
const renderedPath = join16(stageRoot, ".agentwheel-rendered", "openclaw-subagents", agentId, "AGENTS.md");
|
|
6997
|
-
await
|
|
7222
|
+
await mkdir14(dirname17(renderedPath), { recursive: true });
|
|
6998
7223
|
await writeFile15(renderedPath, `${body}
|
|
6999
7224
|
`, "utf8");
|
|
7000
7225
|
const renderedDir = dirname17(renderedPath);
|
|
@@ -8497,7 +8722,7 @@ async function operationForArtifact(artifact, adapter, targetRoot, installationT
|
|
|
8497
8722
|
kind: "file",
|
|
8498
8723
|
sourcePath: artifact.stagedPath ?? artifact.sourcePath,
|
|
8499
8724
|
destPath: destPath2,
|
|
8500
|
-
relativeDestPath:
|
|
8725
|
+
relativeDestPath: relative9(targetRoot, destPath2).replaceAll("\\", "/"),
|
|
8501
8726
|
desiredHash: artifact.hash,
|
|
8502
8727
|
reason: "destination missing",
|
|
8503
8728
|
channel: artifact.channel ?? "managed",
|
|
@@ -8515,7 +8740,7 @@ async function operationForArtifact(artifact, adapter, targetRoot, installationT
|
|
|
8515
8740
|
kind: artifact.kind,
|
|
8516
8741
|
sourcePath: artifact.stagedPath ?? artifact.sourcePath,
|
|
8517
8742
|
destPath,
|
|
8518
|
-
relativeDestPath:
|
|
8743
|
+
relativeDestPath: relative9(targetRoot, destPath).replaceAll("\\", "/"),
|
|
8519
8744
|
desiredHash: artifact.hash,
|
|
8520
8745
|
reason: "destination missing",
|
|
8521
8746
|
channel: artifact.channel ?? "managed",
|
|
@@ -9512,14 +9737,14 @@ async function writeMermaidAsset(response) {
|
|
|
9512
9737
|
"content-type": "application/javascript; charset=utf-8",
|
|
9513
9738
|
"cache-control": "public, max-age=3600"
|
|
9514
9739
|
});
|
|
9515
|
-
await new Promise((
|
|
9740
|
+
await new Promise((resolve45) => {
|
|
9516
9741
|
const stream = createReadStream(assetPath);
|
|
9517
9742
|
stream.on("error", () => {
|
|
9518
9743
|
if (!response.headersSent) writeText(response, 404, "Mermaid asset unavailable.\n");
|
|
9519
9744
|
else response.destroy();
|
|
9520
|
-
|
|
9745
|
+
resolve45();
|
|
9521
9746
|
});
|
|
9522
|
-
stream.on("end",
|
|
9747
|
+
stream.on("end", resolve45);
|
|
9523
9748
|
stream.pipe(response);
|
|
9524
9749
|
});
|
|
9525
9750
|
}
|
|
@@ -9546,14 +9771,14 @@ function escapeHtml2(value) {
|
|
|
9546
9771
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
9547
9772
|
}
|
|
9548
9773
|
function listen(server, port, bind) {
|
|
9549
|
-
return new Promise((
|
|
9774
|
+
return new Promise((resolve45, reject) => {
|
|
9550
9775
|
const onError = (error) => {
|
|
9551
9776
|
server.off("listening", onListening);
|
|
9552
9777
|
reject(error);
|
|
9553
9778
|
};
|
|
9554
9779
|
const onListening = () => {
|
|
9555
9780
|
server.off("error", onError);
|
|
9556
|
-
|
|
9781
|
+
resolve45();
|
|
9557
9782
|
};
|
|
9558
9783
|
server.once("error", onError);
|
|
9559
9784
|
server.once("listening", onListening);
|
|
@@ -9561,7 +9786,7 @@ function listen(server, port, bind) {
|
|
|
9561
9786
|
});
|
|
9562
9787
|
}
|
|
9563
9788
|
function waitForShutdown(server, beforeClose) {
|
|
9564
|
-
return new Promise((
|
|
9789
|
+
return new Promise((resolve45) => {
|
|
9565
9790
|
let closing = false;
|
|
9566
9791
|
const shutdown = () => {
|
|
9567
9792
|
if (closing) return;
|
|
@@ -9569,7 +9794,7 @@ function waitForShutdown(server, beforeClose) {
|
|
|
9569
9794
|
process.off("SIGINT", shutdown);
|
|
9570
9795
|
process.off("SIGTERM", shutdown);
|
|
9571
9796
|
beforeClose();
|
|
9572
|
-
server.close(() =>
|
|
9797
|
+
server.close(() => resolve45());
|
|
9573
9798
|
};
|
|
9574
9799
|
process.once("SIGINT", shutdown);
|
|
9575
9800
|
process.once("SIGTERM", shutdown);
|
|
@@ -9579,7 +9804,7 @@ function waitForShutdown(server, beforeClose) {
|
|
|
9579
9804
|
process.off("SIGINT", shutdown);
|
|
9580
9805
|
process.off("SIGTERM", shutdown);
|
|
9581
9806
|
beforeClose();
|
|
9582
|
-
|
|
9807
|
+
resolve45();
|
|
9583
9808
|
});
|
|
9584
9809
|
});
|
|
9585
9810
|
}
|
|
@@ -9597,7 +9822,7 @@ function errorMessage2(error) {
|
|
|
9597
9822
|
}
|
|
9598
9823
|
|
|
9599
9824
|
// src/source/clawhub.ts
|
|
9600
|
-
import { mkdir as
|
|
9825
|
+
import { mkdir as mkdir15, rm as rm8, writeFile as writeFile16 } from "fs/promises";
|
|
9601
9826
|
import { basename as basename10, dirname as dirname18, join as join28, resolve as resolve18 } from "path";
|
|
9602
9827
|
|
|
9603
9828
|
// src/model/package.ts
|
|
@@ -9791,8 +10016,8 @@ init_fs();
|
|
|
9791
10016
|
|
|
9792
10017
|
// src/source/local.ts
|
|
9793
10018
|
import { createHash as createHash13 } from "crypto";
|
|
9794
|
-
import { readdir as
|
|
9795
|
-
import { basename as basename9, join as join27, relative as
|
|
10019
|
+
import { readdir as readdir6, stat as stat5 } from "fs/promises";
|
|
10020
|
+
import { basename as basename9, join as join27, relative as relative10, resolve as resolve17 } from "path";
|
|
9796
10021
|
init_fs();
|
|
9797
10022
|
var LocalSourceDriver = class {
|
|
9798
10023
|
name = "local";
|
|
@@ -9933,23 +10158,23 @@ var LocalSourceDriver = class {
|
|
|
9933
10158
|
};
|
|
9934
10159
|
async function hashLocalSource(root, manifest) {
|
|
9935
10160
|
if (!manifest) return hashPath(root);
|
|
9936
|
-
const
|
|
10161
|
+
const hash2 = createHash13("sha256").update("local-openpack\0");
|
|
9937
10162
|
const manifestPath = await findPackageManifestPath(root, { warnLegacy: false });
|
|
9938
10163
|
if (manifestPath) {
|
|
9939
|
-
|
|
9940
|
-
|
|
9941
|
-
|
|
10164
|
+
hash2.update("manifest\0");
|
|
10165
|
+
hash2.update(relative10(root, manifestPath).replaceAll("\\", "/")).update("\0");
|
|
10166
|
+
hash2.update(await hashPath(manifestPath)).update("\0");
|
|
9942
10167
|
}
|
|
9943
10168
|
const provides = [...manifest.provides].sort((a, b) => `${a.type}\0${a.path}`.localeCompare(`${b.type}\0${b.path}`));
|
|
9944
10169
|
for (const provide of provides) {
|
|
9945
10170
|
const full = join27(root, provide.path);
|
|
9946
10171
|
if (!await pathExists(full)) continue;
|
|
9947
|
-
|
|
9948
|
-
|
|
9949
|
-
|
|
9950
|
-
|
|
10172
|
+
hash2.update("provide\0");
|
|
10173
|
+
hash2.update(provide.type).update("\0");
|
|
10174
|
+
hash2.update(provide.path.replaceAll("\\", "/")).update("\0");
|
|
10175
|
+
hash2.update(await hashPath(full)).update("\0");
|
|
9951
10176
|
}
|
|
9952
|
-
return
|
|
10177
|
+
return hash2.digest("hex");
|
|
9953
10178
|
}
|
|
9954
10179
|
async function firstExisting(paths) {
|
|
9955
10180
|
for (const path of paths) {
|
|
@@ -9958,7 +10183,7 @@ async function firstExisting(paths) {
|
|
|
9958
10183
|
return void 0;
|
|
9959
10184
|
}
|
|
9960
10185
|
async function sortedDirEntries(path) {
|
|
9961
|
-
return (await
|
|
10186
|
+
return (await readdir6(path, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name));
|
|
9962
10187
|
}
|
|
9963
10188
|
async function listFromManifest(root, packageName) {
|
|
9964
10189
|
const manifest = await readPackageManifest(root);
|
|
@@ -10139,7 +10364,7 @@ async function fetchClawHubPackage(fetchImpl, packageName) {
|
|
|
10139
10364
|
throw new Error(`ClawHub lookup failed for ${packageName}`);
|
|
10140
10365
|
}
|
|
10141
10366
|
async function delay(ms) {
|
|
10142
|
-
await new Promise((
|
|
10367
|
+
await new Promise((resolve45) => setTimeout(resolve45, ms));
|
|
10143
10368
|
}
|
|
10144
10369
|
function parseClawHubSource(source) {
|
|
10145
10370
|
if (!source.startsWith(sourcePrefix)) {
|
|
@@ -10171,7 +10396,7 @@ async function writeGeneratedPackage(root, packageInfo) {
|
|
|
10171
10396
|
};
|
|
10172
10397
|
const pluginPath = join28(root, "plugins", pluginId, "clawhub.json");
|
|
10173
10398
|
await rm8(root, { recursive: true, force: true });
|
|
10174
|
-
await
|
|
10399
|
+
await mkdir15(dirname18(pluginPath), { recursive: true });
|
|
10175
10400
|
await writeFile16(join28(root, "openpack.json"), `${JSON.stringify({
|
|
10176
10401
|
schemaVersion: CURRENT_OPENPACK_SCHEMA_VERSION,
|
|
10177
10402
|
name: `clawhub/${name}`,
|
|
@@ -10202,9 +10427,9 @@ function cachePathFor(packageName, cacheRoot) {
|
|
|
10202
10427
|
|
|
10203
10428
|
// src/source/git.ts
|
|
10204
10429
|
import { execFile as execFile5 } from "child_process";
|
|
10205
|
-
import { mkdir as
|
|
10430
|
+
import { mkdir as mkdir17, rename as rename6, rm as rm10, stat as stat7 } from "fs/promises";
|
|
10206
10431
|
import { homedir as homedir7 } from "os";
|
|
10207
|
-
import { basename as basename11, dirname as dirname20, join as join31, resolve as resolve19, sep as
|
|
10432
|
+
import { basename as basename11, dirname as dirname20, join as join31, resolve as resolve19, sep as sep6 } from "path";
|
|
10208
10433
|
import { promisify as promisify5 } from "util";
|
|
10209
10434
|
init_fs();
|
|
10210
10435
|
|
|
@@ -10285,8 +10510,8 @@ function isMissingFile(error) {
|
|
|
10285
10510
|
|
|
10286
10511
|
// src/source/cache.ts
|
|
10287
10512
|
init_fs();
|
|
10288
|
-
import { randomUUID as
|
|
10289
|
-
import { mkdir as
|
|
10513
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
10514
|
+
import { mkdir as mkdir16, readdir as readdir7, readFile as readFile27, rm as rm9, stat as stat6, writeFile as writeFile17 } from "fs/promises";
|
|
10290
10515
|
import { dirname as dirname19, join as join30 } from "path";
|
|
10291
10516
|
var snapshotNamePattern = /^(.*)-([0-9a-f]{12})$/i;
|
|
10292
10517
|
var leaseMarker = ".agentwheel-lease-";
|
|
@@ -10301,11 +10526,11 @@ async function pruneGitCache(cacheRoot, options = {}) {
|
|
|
10301
10526
|
}
|
|
10302
10527
|
async function withGitCacheMaintenanceLock(cacheRoot, timeoutMs, fn) {
|
|
10303
10528
|
const lockPath = join30(cacheRoot, ".maintenance.lock");
|
|
10304
|
-
await
|
|
10529
|
+
await mkdir16(cacheRoot, { recursive: true });
|
|
10305
10530
|
const started = Date.now();
|
|
10306
10531
|
while (true) {
|
|
10307
10532
|
try {
|
|
10308
|
-
await
|
|
10533
|
+
await mkdir16(lockPath);
|
|
10309
10534
|
await writeFile17(
|
|
10310
10535
|
join30(lockPath, "owner.json"),
|
|
10311
10536
|
JSON.stringify({ pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString() }),
|
|
@@ -10318,7 +10543,7 @@ async function withGitCacheMaintenanceLock(cacheRoot, timeoutMs, fn) {
|
|
|
10318
10543
|
if (Date.now() - started > timeoutMs) {
|
|
10319
10544
|
throw new Error(`Timed out waiting for git cache maintenance lock at ${lockPath}`);
|
|
10320
10545
|
}
|
|
10321
|
-
await new Promise((
|
|
10546
|
+
await new Promise((resolve45) => setTimeout(resolve45, 50));
|
|
10322
10547
|
}
|
|
10323
10548
|
}
|
|
10324
10549
|
try {
|
|
@@ -10328,7 +10553,7 @@ async function withGitCacheMaintenanceLock(cacheRoot, timeoutMs, fn) {
|
|
|
10328
10553
|
}
|
|
10329
10554
|
}
|
|
10330
10555
|
async function createGitSnapshotLease(snapshotPath) {
|
|
10331
|
-
const leasePath = `${snapshotPath}${leaseMarker}${process.pid}-${
|
|
10556
|
+
const leasePath = `${snapshotPath}${leaseMarker}${process.pid}-${randomUUID4()}`;
|
|
10332
10557
|
await writeFile17(leasePath, JSON.stringify({ pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString() }), {
|
|
10333
10558
|
encoding: "utf8",
|
|
10334
10559
|
flag: "wx"
|
|
@@ -10341,7 +10566,7 @@ async function releaseGitSnapshotLease(leasePath) {
|
|
|
10341
10566
|
async function removeGeneratedEntries(root, preserveGit) {
|
|
10342
10567
|
let entries;
|
|
10343
10568
|
try {
|
|
10344
|
-
entries = await
|
|
10569
|
+
entries = await readdir7(root, { withFileTypes: true });
|
|
10345
10570
|
} catch {
|
|
10346
10571
|
return;
|
|
10347
10572
|
}
|
|
@@ -10356,7 +10581,7 @@ async function removeGeneratedEntries(root, preserveGit) {
|
|
|
10356
10581
|
}
|
|
10357
10582
|
async function pruneGitCacheUnlocked(cacheRoot, options) {
|
|
10358
10583
|
const referencedCommits = await referencedGraphLockCommits(join30(dirname19(cacheRoot), "locks"));
|
|
10359
|
-
const entries = await
|
|
10584
|
+
const entries = await readdir7(cacheRoot, { withFileTypes: true });
|
|
10360
10585
|
const groups = /* @__PURE__ */ new Map();
|
|
10361
10586
|
for (const entry of entries) {
|
|
10362
10587
|
if (!entry.isDirectory()) continue;
|
|
@@ -10403,7 +10628,7 @@ async function referencedGraphLockCommits(lockRoot) {
|
|
|
10403
10628
|
async function walkGraphLocks(root, visit) {
|
|
10404
10629
|
let entries;
|
|
10405
10630
|
try {
|
|
10406
|
-
entries = await
|
|
10631
|
+
entries = await readdir7(root, { withFileTypes: true });
|
|
10407
10632
|
} catch (error) {
|
|
10408
10633
|
if (isNotFound(error)) return;
|
|
10409
10634
|
throw new Error(`Cannot inspect graph-lock directory ${root}; cache prune aborted: ${errorMessage3(error)}`);
|
|
@@ -10424,7 +10649,7 @@ async function walkGraphLocks(root, visit) {
|
|
|
10424
10649
|
async function hasLiveSnapshotLease(snapshotPath, dryRun) {
|
|
10425
10650
|
const directory = dirname19(snapshotPath);
|
|
10426
10651
|
const prefix = `${snapshotPath.slice(directory.length + 1)}${leaseMarker}`;
|
|
10427
|
-
const entries = await
|
|
10652
|
+
const entries = await readdir7(directory, { withFileTypes: true });
|
|
10428
10653
|
let live = false;
|
|
10429
10654
|
for (const entry of entries) {
|
|
10430
10655
|
if (!entry.isFile() || !entry.name.startsWith(prefix)) continue;
|
|
@@ -10523,10 +10748,10 @@ function normalizeGitSubpath(subpath, source) {
|
|
|
10523
10748
|
}
|
|
10524
10749
|
return normalized;
|
|
10525
10750
|
}
|
|
10526
|
-
function resolveGitRelativeSubpath(declaringSubpath,
|
|
10527
|
-
const joined = posix4.normalize(posix4.join(declaringSubpath ?? ".",
|
|
10751
|
+
function resolveGitRelativeSubpath(declaringSubpath, relative20, source) {
|
|
10752
|
+
const joined = posix4.normalize(posix4.join(declaringSubpath ?? ".", relative20));
|
|
10528
10753
|
if (joined === ".." || joined.startsWith("../")) {
|
|
10529
|
-
throw new Error(`Relative dependency '${
|
|
10754
|
+
throw new Error(`Relative dependency '${relative20}' escapes the git repository it is declared in: ${source}`);
|
|
10530
10755
|
}
|
|
10531
10756
|
return normalizeGitSubpath(joined, source);
|
|
10532
10757
|
}
|
|
@@ -10562,7 +10787,7 @@ var GitSourceDriver = class {
|
|
|
10562
10787
|
return withFilesystemLock(`${resolved.resolvedPath}.lock`, resolved.cacheLockTimeoutMs ?? 3e4, async () => {
|
|
10563
10788
|
const parsed = parseGitSource(resolved.source);
|
|
10564
10789
|
const cacheRoot = dirname20(resolved.resolvedPath);
|
|
10565
|
-
await
|
|
10790
|
+
await mkdir17(cacheRoot, { recursive: true });
|
|
10566
10791
|
await assertOwnedByCurrentUser(cacheRoot);
|
|
10567
10792
|
if (await pathExists(resolved.resolvedPath)) await assertOwnedByCurrentUser(resolved.resolvedPath);
|
|
10568
10793
|
if (!await pathExists(join31(resolved.resolvedPath, ".git"))) {
|
|
@@ -10662,11 +10887,11 @@ async function snapshotCommit(checkoutPath, commit) {
|
|
|
10662
10887
|
const tempIndexPath = `${tempPath}.index`;
|
|
10663
10888
|
await rm10(tempPath, { recursive: true, force: true });
|
|
10664
10889
|
await rm10(tempIndexPath, { force: true });
|
|
10665
|
-
await
|
|
10890
|
+
await mkdir17(tempPath, { recursive: true });
|
|
10666
10891
|
const env2 = { ...process.env, GIT_INDEX_FILE: tempIndexPath };
|
|
10667
10892
|
try {
|
|
10668
10893
|
await git(["-C", checkoutPath, "read-tree", commit], env2);
|
|
10669
|
-
await git(["-C", checkoutPath, "checkout-index", "--all", `--prefix=${resolve19(tempPath)}${
|
|
10894
|
+
await git(["-C", checkoutPath, "checkout-index", "--all", `--prefix=${resolve19(tempPath)}${sep6}`], env2);
|
|
10670
10895
|
await removeGeneratedEntries(tempPath, false);
|
|
10671
10896
|
} catch (error) {
|
|
10672
10897
|
await rm10(tempPath, { recursive: true, force: true });
|
|
@@ -10695,7 +10920,7 @@ async function assertOwnedByCurrentUser(path) {
|
|
|
10695
10920
|
}
|
|
10696
10921
|
|
|
10697
10922
|
// src/source/mcp-registry.ts
|
|
10698
|
-
import { mkdir as
|
|
10923
|
+
import { mkdir as mkdir18, writeFile as writeFile18 } from "fs/promises";
|
|
10699
10924
|
import { basename as basename12, dirname as dirname21, join as join32, resolve as resolve20 } from "path";
|
|
10700
10925
|
init_fs();
|
|
10701
10926
|
var registryBaseUrl = "https://registry.modelcontextprotocol.io/v0.1";
|
|
@@ -10804,7 +11029,7 @@ function isSafeHttpUrl(value) {
|
|
|
10804
11029
|
async function writeGeneratedPackage2(root, server) {
|
|
10805
11030
|
const serverId = installNameFor3(server.serverName);
|
|
10806
11031
|
const mcpPath = join32(root, "mcp", `${serverId}.json`);
|
|
10807
|
-
await
|
|
11032
|
+
await mkdir18(dirname21(mcpPath), { recursive: true });
|
|
10808
11033
|
await writeFile18(join32(root, "openpack.json"), `${JSON.stringify({
|
|
10809
11034
|
schemaVersion: CURRENT_OPENPACK_SCHEMA_VERSION,
|
|
10810
11035
|
name: `mcp-registry/${server.serverName}`,
|
|
@@ -10834,9 +11059,9 @@ function cachePathFor3(serverName, cacheRoot) {
|
|
|
10834
11059
|
// src/source/skillkit.ts
|
|
10835
11060
|
init_cache_identity();
|
|
10836
11061
|
init_fs();
|
|
10837
|
-
import { createHash as createHash14, randomUUID as
|
|
11062
|
+
import { createHash as createHash14, randomUUID as randomUUID5 } from "crypto";
|
|
10838
11063
|
import { execFile as execFile6 } from "child_process";
|
|
10839
|
-
import { cp as cp3, mkdir as
|
|
11064
|
+
import { cp as cp3, mkdir as mkdir19, readFile as readFile28, rename as rename7, rm as rm11 } from "fs/promises";
|
|
10840
11065
|
import { homedir as homedir8 } from "os";
|
|
10841
11066
|
import { basename as basename14, dirname as dirname23, join as join34, resolve as resolve21 } from "path";
|
|
10842
11067
|
import { promisify as promisify6 } from "util";
|
|
@@ -10844,7 +11069,7 @@ import * as defaultSkillKit from "@skillkit/core";
|
|
|
10844
11069
|
|
|
10845
11070
|
// src/source/skill-artifacts.ts
|
|
10846
11071
|
init_fs();
|
|
10847
|
-
import { readdir as
|
|
11072
|
+
import { readdir as readdir8, stat as stat8 } from "fs/promises";
|
|
10848
11073
|
import { basename as basename13, dirname as dirname22, extname as extname2, join as join33 } from "path";
|
|
10849
11074
|
async function artifactsFromSkillPaths(paths, packageName) {
|
|
10850
11075
|
const artifacts = [];
|
|
@@ -10912,7 +11137,7 @@ async function artifactFromSkillPath(item, packageName) {
|
|
|
10912
11137
|
}
|
|
10913
11138
|
async function walk(dir, paths) {
|
|
10914
11139
|
if (!await pathExists(dir)) return;
|
|
10915
|
-
const entries = await
|
|
11140
|
+
const entries = await readdir8(dir, { withFileTypes: true });
|
|
10916
11141
|
if (entries.some((entry) => entry.isFile() && entry.name === "SKILL.md")) {
|
|
10917
11142
|
paths.push({ path: dir });
|
|
10918
11143
|
return;
|
|
@@ -11015,8 +11240,8 @@ var SkillKitSourceDriver = class {
|
|
|
11015
11240
|
if (immutablePath && await pathExists(immutablePath)) {
|
|
11016
11241
|
return { path: immutablePath, commit: requestedCommit, cacheIdentity: requestedCommit };
|
|
11017
11242
|
}
|
|
11018
|
-
await
|
|
11019
|
-
const candidatePath = `${specCachePath}.agentwheel-tmp-${process.pid}-${
|
|
11243
|
+
await mkdir19(dirname23(specCachePath), { recursive: true });
|
|
11244
|
+
const candidatePath = `${specCachePath}.agentwheel-tmp-${process.pid}-${randomUUID5()}`;
|
|
11020
11245
|
const publishPath = `${candidatePath}.publish`;
|
|
11021
11246
|
let result;
|
|
11022
11247
|
try {
|
|
@@ -11171,7 +11396,7 @@ async function checkoutCommit(root, commit) {
|
|
|
11171
11396
|
// src/source/vercel-skills.ts
|
|
11172
11397
|
init_fs();
|
|
11173
11398
|
import { stat as stat9 } from "fs/promises";
|
|
11174
|
-
import { basename as basename15, join as join35, relative as
|
|
11399
|
+
import { basename as basename15, join as join35, relative as relative11, resolve as resolve22 } from "path";
|
|
11175
11400
|
var VercelSkillsSourceDriver = class {
|
|
11176
11401
|
name = "vercel-skills";
|
|
11177
11402
|
git = new GitSourceDriver();
|
|
@@ -11238,7 +11463,7 @@ async function resolveVercelSkillSubpath(root, subpath) {
|
|
|
11238
11463
|
for (const candidate of candidates) {
|
|
11239
11464
|
if (await pathExists(candidate)) return candidate;
|
|
11240
11465
|
}
|
|
11241
|
-
const tried = candidates.map((candidate) =>
|
|
11466
|
+
const tried = candidates.map((candidate) => relative11(root, candidate)).join(", ");
|
|
11242
11467
|
throw new Error(`Vercel skills subpath not found: ${subpath} (tried ${tried})`);
|
|
11243
11468
|
}
|
|
11244
11469
|
function parseVercelSource(source) {
|
|
@@ -11307,8 +11532,8 @@ function getSourceDriver(name = "local") {
|
|
|
11307
11532
|
init_identify();
|
|
11308
11533
|
|
|
11309
11534
|
// src/staging/staging.ts
|
|
11310
|
-
import { chmod as chmod3, cp as cp5, mkdir as
|
|
11311
|
-
import { basename as basename19, dirname as dirname27, join as join39, relative as
|
|
11535
|
+
import { chmod as chmod3, cp as cp5, mkdir as mkdir22, mkdtemp as mkdtemp4, readdir as readdir11, stat as stat12 } from "fs/promises";
|
|
11536
|
+
import { basename as basename19, dirname as dirname27, join as join39, relative as relative13, resolve as resolve25, sep as sep8 } from "path";
|
|
11312
11537
|
import { tmpdir as tmpdir5 } from "os";
|
|
11313
11538
|
init_fs();
|
|
11314
11539
|
|
|
@@ -11316,8 +11541,8 @@ init_fs();
|
|
|
11316
11541
|
init_artifact();
|
|
11317
11542
|
init_fs();
|
|
11318
11543
|
import { createHash as createHash15 } from "crypto";
|
|
11319
|
-
import { readdir as
|
|
11320
|
-
import { basename as basename16, dirname as dirname24, extname as extname3, join as join36, relative as
|
|
11544
|
+
import { readdir as readdir9, readFile as readFile29, stat as stat10, writeFile as writeFile19 } from "fs/promises";
|
|
11545
|
+
import { basename as basename16, dirname as dirname24, extname as extname3, join as join36, relative as relative12, resolve as resolve24, sep as sep7 } from "path";
|
|
11321
11546
|
var includePattern = /<!--\s*openpack:include(\?)?\s+([^>]+?)\s*-->/g;
|
|
11322
11547
|
var escapedIncludePattern = /<!--\s*openpack\\:include(\?)?\s+([^>]+?)\s*-->/g;
|
|
11323
11548
|
var generatedPattern = /<!--\s*(?:BEGIN|END)\s+openpack:include\b/;
|
|
@@ -11527,7 +11752,7 @@ function extractOpenPackIncludeSelectors(content) {
|
|
|
11527
11752
|
function resolvePackageSelector(packageRoot, selector) {
|
|
11528
11753
|
const root = resolve24(packageRoot);
|
|
11529
11754
|
const resolved = resolve24(root, selector);
|
|
11530
|
-
if (resolved !== root && !resolved.startsWith(`${root}${
|
|
11755
|
+
if (resolved !== root && !resolved.startsWith(`${root}${sep7}`)) {
|
|
11531
11756
|
throw new Error(`OpenPack include escapes package root: ${selector}`);
|
|
11532
11757
|
}
|
|
11533
11758
|
return resolved;
|
|
@@ -11542,7 +11767,7 @@ async function markdownFilesForArtifact(artifact) {
|
|
|
11542
11767
|
async function listMarkdownFiles(root) {
|
|
11543
11768
|
const out = [];
|
|
11544
11769
|
async function walk2(dir) {
|
|
11545
|
-
for (const entry of (await
|
|
11770
|
+
for (const entry of (await readdir9(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
11546
11771
|
const full = join36(dir, entry.name);
|
|
11547
11772
|
if (entry.isDirectory()) {
|
|
11548
11773
|
await walk2(full);
|
|
@@ -11578,7 +11803,7 @@ function applyReplacements(content, replacements) {
|
|
|
11578
11803
|
return out + content.slice(cursor);
|
|
11579
11804
|
}
|
|
11580
11805
|
function relativeSelector(root, file) {
|
|
11581
|
-
return
|
|
11806
|
+
return relative12(root, file).replaceAll("\\", "/");
|
|
11582
11807
|
}
|
|
11583
11808
|
function ownerSelector(root, file, nodeId) {
|
|
11584
11809
|
const selector = relativeSelector(root, file);
|
|
@@ -11608,7 +11833,7 @@ function artifactPathMap(artifacts) {
|
|
|
11608
11833
|
|
|
11609
11834
|
// src/staging/customize.ts
|
|
11610
11835
|
init_fs();
|
|
11611
|
-
import { cp as cp4, mkdir as
|
|
11836
|
+
import { cp as cp4, mkdir as mkdir20, readdir as readdir10, readFile as readFile30, writeFile as writeFile20 } from "fs/promises";
|
|
11612
11837
|
import { dirname as dirname25, join as join37 } from "path";
|
|
11613
11838
|
async function applyCustomizations(artifacts, options) {
|
|
11614
11839
|
let next = [...artifacts];
|
|
@@ -11633,7 +11858,7 @@ async function applyInstructionOverlay(artifacts, options) {
|
|
|
11633
11858
|
const managed = await readFile30(artifact.stagedPath ?? artifact.sourcePath, "utf8");
|
|
11634
11859
|
const local = await readFile30(overlayPath, "utf8");
|
|
11635
11860
|
const composedPath = join37(options.stageRoot, ".agentwheel-composed", "instructions", "AGENTS.md");
|
|
11636
|
-
await
|
|
11861
|
+
await mkdir20(dirname25(composedPath), { recursive: true });
|
|
11637
11862
|
await writeFile20(
|
|
11638
11863
|
composedPath,
|
|
11639
11864
|
[
|
|
@@ -11706,7 +11931,7 @@ async function applyReplacements2(artifacts, options, channel, artifactTypes) {
|
|
|
11706
11931
|
const artifactKind = entry.isDirectory() ? "dir" : "file";
|
|
11707
11932
|
const existing = byKey.get(artifactMapKey);
|
|
11708
11933
|
const stagedPath = join37(options.stageRoot, ".agentwheel-composed", channel, type, entry.name);
|
|
11709
|
-
await
|
|
11934
|
+
await mkdir20(dirname25(stagedPath), { recursive: true });
|
|
11710
11935
|
await cp4(full, stagedPath, { recursive: artifactKind === "dir", dereference: true });
|
|
11711
11936
|
byKey.set(artifactMapKey, {
|
|
11712
11937
|
...existing,
|
|
@@ -11746,12 +11971,12 @@ function installableArtifactTypes() {
|
|
|
11746
11971
|
return ["instructions", "rules", "skills", "commands", "subagents", "mcp", "hooks", "settings", "plugins"];
|
|
11747
11972
|
}
|
|
11748
11973
|
async function sortedDirEntries2(path) {
|
|
11749
|
-
return (await
|
|
11974
|
+
return (await readdir10(path, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name));
|
|
11750
11975
|
}
|
|
11751
11976
|
|
|
11752
11977
|
// src/staging/claude-subagents.ts
|
|
11753
11978
|
init_fs();
|
|
11754
|
-
import { mkdir as
|
|
11979
|
+
import { mkdir as mkdir21, readFile as readFile31, writeFile as writeFile21 } from "fs/promises";
|
|
11755
11980
|
import { basename as basename18, dirname as dirname26, join as join38 } from "path";
|
|
11756
11981
|
async function renderClaudeSubagents(artifacts, stageRoot, adapter) {
|
|
11757
11982
|
if (adapter?.name !== "claude") return artifacts;
|
|
@@ -11783,7 +12008,7 @@ async function renderClaudeSubagent(artifact, stageRoot) {
|
|
|
11783
12008
|
}
|
|
11784
12009
|
const content = await readFile31(markdownPath, "utf8");
|
|
11785
12010
|
const renderedPath = join38(stageRoot, ".agentwheel-rendered", "claude-subagents", `${agentName}.md`);
|
|
11786
|
-
await
|
|
12011
|
+
await mkdir21(dirname26(renderedPath), { recursive: true });
|
|
11787
12012
|
await writeFile21(renderedPath, content.endsWith("\n") ? content : `${content}
|
|
11788
12013
|
`, "utf8");
|
|
11789
12014
|
return {
|
|
@@ -11825,7 +12050,7 @@ async function stageResolvedArtifactsRaw(resolved, artifacts, parentDir = tmpdir
|
|
|
11825
12050
|
await removeOnFailure(root, async () => {
|
|
11826
12051
|
for (const artifact of artifacts) {
|
|
11827
12052
|
const stagedPath = join39(root, artifact.relativePath);
|
|
11828
|
-
await
|
|
12053
|
+
await mkdir22(dirname27(stagedPath), { recursive: true });
|
|
11829
12054
|
await cp5(artifact.sourcePath, stagedPath, {
|
|
11830
12055
|
recursive: artifact.kind === "dir",
|
|
11831
12056
|
dereference: true,
|
|
@@ -11922,7 +12147,7 @@ async function copyAsset(asset, source, dest) {
|
|
|
11922
12147
|
const sourceStats = await stat12(source);
|
|
11923
12148
|
if (sourceStats.isFile()) {
|
|
11924
12149
|
if (matchesAny(basename19(source), asset.include)) {
|
|
11925
|
-
await
|
|
12150
|
+
await mkdir22(dest, { recursive: true });
|
|
11926
12151
|
await copyAssetFile(source, join39(dest, basename19(source)), asset);
|
|
11927
12152
|
}
|
|
11928
12153
|
return;
|
|
@@ -11931,26 +12156,26 @@ async function copyAsset(asset, source, dest) {
|
|
|
11931
12156
|
throw new Error(`Asset include source is not a file or directory: ${source}`);
|
|
11932
12157
|
}
|
|
11933
12158
|
if (!asset.include?.length) {
|
|
11934
|
-
await
|
|
12159
|
+
await mkdir22(dirname27(dest), { recursive: true });
|
|
11935
12160
|
await cp5(source, dest, { recursive: true, dereference: true });
|
|
11936
12161
|
if (asset.mode === "copy") await normalizeCopiedModes(dest);
|
|
11937
12162
|
return;
|
|
11938
12163
|
}
|
|
11939
12164
|
for (const file of await listFiles2(source)) {
|
|
11940
|
-
const rel =
|
|
12165
|
+
const rel = relative13(source, file).replaceAll("\\", "/");
|
|
11941
12166
|
if (!matchesAny(rel, asset.include) && !matchesAny(basename19(file), asset.include)) continue;
|
|
11942
12167
|
await copyAssetFile(file, join39(dest, rel), asset);
|
|
11943
12168
|
}
|
|
11944
12169
|
}
|
|
11945
12170
|
async function copyAssetFile(source, dest, asset) {
|
|
11946
|
-
await
|
|
12171
|
+
await mkdir22(dirname27(dest), { recursive: true });
|
|
11947
12172
|
await cp5(source, dest, { dereference: true });
|
|
11948
12173
|
if (asset.mode === "copy") await chmod3(dest, 420);
|
|
11949
12174
|
}
|
|
11950
12175
|
function resolvePackagePath(packageRoot, path) {
|
|
11951
12176
|
const resolved = resolve25(packageRoot, path);
|
|
11952
12177
|
const root = resolve25(packageRoot);
|
|
11953
|
-
if (resolved !== root && !resolved.startsWith(`${root}${
|
|
12178
|
+
if (resolved !== root && !resolved.startsWith(`${root}${sep8}`)) {
|
|
11954
12179
|
throw new Error(`Asset include escapes package root: ${path}`);
|
|
11955
12180
|
}
|
|
11956
12181
|
return resolved;
|
|
@@ -11958,7 +12183,7 @@ function resolvePackagePath(packageRoot, path) {
|
|
|
11958
12183
|
async function listFiles2(root) {
|
|
11959
12184
|
const out = [];
|
|
11960
12185
|
async function walk2(dir) {
|
|
11961
|
-
for (const entry of (await
|
|
12186
|
+
for (const entry of (await readdir11(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
11962
12187
|
const full = join39(dir, entry.name);
|
|
11963
12188
|
if (entry.isDirectory()) {
|
|
11964
12189
|
await walk2(full);
|
|
@@ -11977,7 +12202,7 @@ async function normalizeCopiedModes(path) {
|
|
|
11977
12202
|
return;
|
|
11978
12203
|
}
|
|
11979
12204
|
if (!stats.isDirectory()) return;
|
|
11980
|
-
for (const entry of await
|
|
12205
|
+
for (const entry of await readdir11(path, { withFileTypes: true })) {
|
|
11981
12206
|
await normalizeCopiedModes(join39(path, entry.name));
|
|
11982
12207
|
}
|
|
11983
12208
|
}
|
|
@@ -11998,12 +12223,12 @@ init_adapters();
|
|
|
11998
12223
|
init_adapter();
|
|
11999
12224
|
init_artifact();
|
|
12000
12225
|
init_workspace();
|
|
12001
|
-
import { appendFile, cp as cp6, lstat as
|
|
12002
|
-
import { dirname as dirname29, join as join42, relative as
|
|
12226
|
+
import { appendFile, cp as cp6, lstat as lstat3, mkdir as mkdir23, readdir as readdir13, rm as rm13 } from "fs/promises";
|
|
12227
|
+
import { dirname as dirname29, join as join42, relative as relative14 } from "path";
|
|
12003
12228
|
|
|
12004
12229
|
// src/resolve/graph.ts
|
|
12005
12230
|
import { createHash as createHash17 } from "crypto";
|
|
12006
|
-
import { readdir as
|
|
12231
|
+
import { readdir as readdir12, readFile as readFile34, stat as stat14 } from "fs/promises";
|
|
12007
12232
|
import { basename as basename20, extname as extname4, join as join41 } from "path";
|
|
12008
12233
|
init_artifact();
|
|
12009
12234
|
init_fs();
|
|
@@ -13075,7 +13300,7 @@ async function markdownFilesForArtifact2(artifact) {
|
|
|
13075
13300
|
async function listMarkdownFiles2(root) {
|
|
13076
13301
|
const out = [];
|
|
13077
13302
|
async function walk2(dir) {
|
|
13078
|
-
for (const entry of (await
|
|
13303
|
+
for (const entry of (await readdir12(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
13079
13304
|
const full = join41(dir, entry.name);
|
|
13080
13305
|
if (entry.isDirectory()) {
|
|
13081
13306
|
await walk2(full);
|
|
@@ -13257,8 +13482,8 @@ function verifyIntegrity(integrity, sourceHash, label) {
|
|
|
13257
13482
|
async function withCachePathLock(path, fn) {
|
|
13258
13483
|
const previous = cacheLocks.get(path) ?? Promise.resolve();
|
|
13259
13484
|
let release = () => void 0;
|
|
13260
|
-
const current = previous.then(() => new Promise((
|
|
13261
|
-
release =
|
|
13485
|
+
const current = previous.then(() => new Promise((resolve45) => {
|
|
13486
|
+
release = resolve45;
|
|
13262
13487
|
}));
|
|
13263
13488
|
cacheLocks.set(path, current);
|
|
13264
13489
|
await previous;
|
|
@@ -13339,8 +13564,8 @@ function detectDirectCollisions(nodes) {
|
|
|
13339
13564
|
}
|
|
13340
13565
|
}
|
|
13341
13566
|
function graphNodeId(name, version, normalizedSource, resolvedCommit, sourceHash) {
|
|
13342
|
-
const
|
|
13343
|
-
return `${name}@${version}+${
|
|
13567
|
+
const digest3 = createHash17("sha256").update(normalizedSource).update("\0").update(resolvedCommit ?? sourceHash).digest("hex").slice(0, 12);
|
|
13568
|
+
return `${name}@${version}+${digest3}`;
|
|
13344
13569
|
}
|
|
13345
13570
|
function sortedUnique4(values) {
|
|
13346
13571
|
return [...new Set(values)].sort((a, b) => a.localeCompare(b));
|
|
@@ -13382,7 +13607,7 @@ init_declarations();
|
|
|
13382
13607
|
async function remember(workspaceRoot, runtime, text) {
|
|
13383
13608
|
const overlayPath = join42(workspaceRoot, ".agentwheel", "overlays", runtime, "instructions.local.md");
|
|
13384
13609
|
declareMutationPath(overlayPath);
|
|
13385
|
-
await
|
|
13610
|
+
await mkdir23(dirname29(overlayPath), { recursive: true });
|
|
13386
13611
|
await appendFile(overlayPath, `${text.trim()}
|
|
13387
13612
|
`, "utf8");
|
|
13388
13613
|
return { overlayPath };
|
|
@@ -13410,12 +13635,12 @@ async function ejectArtifact(workspaceRoot, item) {
|
|
|
13410
13635
|
for (const path of await listFilesIfPresent(ejectedPath)) declareMutationPath(path);
|
|
13411
13636
|
if (artifact.kind === "dir") {
|
|
13412
13637
|
for (const sourceFile of await listFiles3(sourcePath)) {
|
|
13413
|
-
declareMutationPath(join42(ejectedPath,
|
|
13638
|
+
declareMutationPath(join42(ejectedPath, relative14(sourcePath, sourceFile)));
|
|
13414
13639
|
}
|
|
13415
13640
|
} else {
|
|
13416
13641
|
declareMutationPath(ejectedPath);
|
|
13417
13642
|
}
|
|
13418
|
-
await
|
|
13643
|
+
await mkdir23(dirname29(ejectedPath), { recursive: true });
|
|
13419
13644
|
await rm13(ejectedPath, { recursive: true, force: true });
|
|
13420
13645
|
await cp6(sourcePath, ejectedPath, { recursive: artifact.kind === "dir", dereference: true });
|
|
13421
13646
|
return {
|
|
@@ -13431,11 +13656,11 @@ async function ejectArtifact(workspaceRoot, item) {
|
|
|
13431
13656
|
}
|
|
13432
13657
|
}
|
|
13433
13658
|
async function listFiles3(root) {
|
|
13434
|
-
const stats = await
|
|
13659
|
+
const stats = await lstat3(root);
|
|
13435
13660
|
if (stats.isFile()) return [root];
|
|
13436
13661
|
const files = [];
|
|
13437
13662
|
async function walk2(dir) {
|
|
13438
|
-
for (const entry of await
|
|
13663
|
+
for (const entry of await readdir13(dir, { withFileTypes: true })) {
|
|
13439
13664
|
const path = join42(dir, entry.name);
|
|
13440
13665
|
if (entry.isDirectory()) await walk2(path);
|
|
13441
13666
|
else files.push(path);
|
|
@@ -13533,7 +13758,7 @@ init_adapter();
|
|
|
13533
13758
|
init_adapter();
|
|
13534
13759
|
init_graph_lock();
|
|
13535
13760
|
import { createHash as createHash19 } from "crypto";
|
|
13536
|
-
import { mkdir as
|
|
13761
|
+
import { mkdir as mkdir25, readdir as readdir14 } from "fs/promises";
|
|
13537
13762
|
import { basename as basename21, dirname as dirname31, join as join45, posix as posix6, resolve as resolve31 } from "path";
|
|
13538
13763
|
init_paths();
|
|
13539
13764
|
init_merge_removal();
|
|
@@ -13674,8 +13899,8 @@ function formatNamespace(decision) {
|
|
|
13674
13899
|
function formatOverride(decision) {
|
|
13675
13900
|
return `${decision.graphNodeId}:${decision.type}/${decision.name} replaces ${decision.overriddenGraphNodeId}:${decision.type}/${decision.name} via ${decision.rootId} (${decision.selector})`;
|
|
13676
13901
|
}
|
|
13677
|
-
function short(
|
|
13678
|
-
return
|
|
13902
|
+
function short(hash2) {
|
|
13903
|
+
return hash2.slice(0, 12);
|
|
13679
13904
|
}
|
|
13680
13905
|
function selectionImportKey(root) {
|
|
13681
13906
|
const selection = root.selectionImport;
|
|
@@ -14215,7 +14440,7 @@ init_fs();
|
|
|
14215
14440
|
|
|
14216
14441
|
// src/lifecycle/trust.ts
|
|
14217
14442
|
init_fs();
|
|
14218
|
-
import { mkdir as
|
|
14443
|
+
import { mkdir as mkdir24, readFile as readFile36 } from "fs/promises";
|
|
14219
14444
|
import { homedir as homedir12 } from "os";
|
|
14220
14445
|
import { dirname as dirname30, join as join44 } from "path";
|
|
14221
14446
|
import { z as z14 } from "zod";
|
|
@@ -14294,7 +14519,7 @@ async function readTrustStore(path) {
|
|
|
14294
14519
|
return trustStoreSchema.parse(JSON.parse(await readFile36(path, "utf8")));
|
|
14295
14520
|
}
|
|
14296
14521
|
async function writeTrustStore(path, store) {
|
|
14297
|
-
await
|
|
14522
|
+
await mkdir24(dirname30(path), { recursive: true });
|
|
14298
14523
|
await writeJsonAtomic(path, trustStoreSchema.parse(store));
|
|
14299
14524
|
}
|
|
14300
14525
|
function defaultTrustStorePath() {
|
|
@@ -14422,9 +14647,9 @@ async function createExactMcpRetirementPlan(desiredArtifacts, adapter, targetRoo
|
|
|
14422
14647
|
init_fs();
|
|
14423
14648
|
init_declarations();
|
|
14424
14649
|
init_workspace();
|
|
14425
|
-
import { lstat as
|
|
14650
|
+
import { lstat as lstat4, readFile as readFile37, realpath as realpath2 } from "fs/promises";
|
|
14426
14651
|
import { homedir as homedir13 } from "os";
|
|
14427
|
-
import { isAbsolute as isAbsolute10, relative as
|
|
14652
|
+
import { isAbsolute as isAbsolute10, relative as relative15, resolve as resolve29 } from "path";
|
|
14428
14653
|
async function resolveWorkspaceScope(request = {}) {
|
|
14429
14654
|
const selected = [request.user === true, request.local === true, request.fleet !== void 0].filter(Boolean).length;
|
|
14430
14655
|
if (selected > 1) {
|
|
@@ -14562,7 +14787,7 @@ function assertFleetContract(id, registration, config) {
|
|
|
14562
14787
|
async function assertCanonicalDirectory(input, label) {
|
|
14563
14788
|
const canonical = await canonicalizeDirectory(input, label);
|
|
14564
14789
|
const normalized = resolve29(input);
|
|
14565
|
-
const stats = await
|
|
14790
|
+
const stats = await lstat4(input);
|
|
14566
14791
|
if (stats.isSymbolicLink() || canonical !== normalized || input !== normalized) {
|
|
14567
14792
|
throw new Error(`${label} must be canonical and symlink-unambiguous: ${input} resolves to ${canonical}.`);
|
|
14568
14793
|
}
|
|
@@ -14576,7 +14801,7 @@ async function canonicalizeDirectory(input, label) {
|
|
|
14576
14801
|
} catch {
|
|
14577
14802
|
throw new Error(`${label} does not exist: ${input}`);
|
|
14578
14803
|
}
|
|
14579
|
-
const stats = await
|
|
14804
|
+
const stats = await lstat4(canonical);
|
|
14580
14805
|
if (!stats.isDirectory()) throw new Error(`${label} is not a directory: ${input}`);
|
|
14581
14806
|
return canonical;
|
|
14582
14807
|
}
|
|
@@ -14596,7 +14821,7 @@ function sortedUnique6(values) {
|
|
|
14596
14821
|
return [...new Set(values.map((value) => value.trim()).filter(Boolean))].sort((a, b) => a.localeCompare(b));
|
|
14597
14822
|
}
|
|
14598
14823
|
function containsPath(parent, candidate) {
|
|
14599
|
-
const path =
|
|
14824
|
+
const path = relative15(parent, candidate);
|
|
14600
14825
|
return path === "" || !path.startsWith("..") && !isAbsolute10(path);
|
|
14601
14826
|
}
|
|
14602
14827
|
|
|
@@ -15117,7 +15342,7 @@ async function discoverLegacyStateCandidates(options) {
|
|
|
15117
15342
|
const directory = dirname31(options.graphLockPath);
|
|
15118
15343
|
let names;
|
|
15119
15344
|
try {
|
|
15120
|
-
names = await
|
|
15345
|
+
names = await readdir14(directory);
|
|
15121
15346
|
} catch (error) {
|
|
15122
15347
|
if (error.code === "ENOENT") return [];
|
|
15123
15348
|
throw error;
|
|
@@ -15553,7 +15778,7 @@ import { basename as basename22, dirname as dirname32, join as join47, resolve a
|
|
|
15553
15778
|
// src/transport/run-as-identity.ts
|
|
15554
15779
|
import { execFileSync } from "child_process";
|
|
15555
15780
|
import { lstatSync, realpathSync as realpathSync2 } from "fs";
|
|
15556
|
-
import { isAbsolute as isAbsolute11, join as join46, parse as parse5, resolve as resolve32, sep as
|
|
15781
|
+
import { isAbsolute as isAbsolute11, join as join46, parse as parse5, resolve as resolve32, sep as sep9 } from "path";
|
|
15557
15782
|
function resolveRunAsIdentity(user, root, getent = (name) => execFileSync("/usr/bin/getent", ["passwd", name], { encoding: "utf8" })) {
|
|
15558
15783
|
if (!/^[a-z_][a-z0-9_-]*[$]?$/i.test(user) || user === "root") {
|
|
15559
15784
|
throw new Error(`Invalid local run-as account '${user}'.`);
|
|
@@ -15575,19 +15800,19 @@ function resolveRunAsIdentity(user, root, getent = (name) => execFileSync("/usr/
|
|
|
15575
15800
|
if (!Number.isSafeInteger(uid) || uid <= 0 || !Number.isSafeInteger(gid) || gid < 0 || !isAbsolute11(home) || resolve32(home) !== home) {
|
|
15576
15801
|
throw new Error(`Invalid UID, GID or home for local run-as account '${user}'.`);
|
|
15577
15802
|
}
|
|
15578
|
-
|
|
15803
|
+
assertNoSymlinkPath2(home);
|
|
15579
15804
|
if (realpathSync2(home) !== home || !lstatSync(home).isDirectory()) {
|
|
15580
15805
|
throw new Error(`Local run-as home ${home} must be a real directory.`);
|
|
15581
15806
|
}
|
|
15582
|
-
|
|
15807
|
+
assertNoSymlinkPath2(root);
|
|
15583
15808
|
if (realpathSync2(root) !== root || !lstatSync(root).isDirectory() || lstatSync(root).uid !== uid) {
|
|
15584
15809
|
throw new Error(`Local run-as root ${root} must be a real directory owned by UID ${uid}.`);
|
|
15585
15810
|
}
|
|
15586
15811
|
return { user, uid, gid, home, root };
|
|
15587
15812
|
}
|
|
15588
|
-
function
|
|
15813
|
+
function assertNoSymlinkPath2(path) {
|
|
15589
15814
|
let current = parse5(path).root;
|
|
15590
|
-
for (const part of path.slice(current.length).split(
|
|
15815
|
+
for (const part of path.slice(current.length).split(sep9).filter(Boolean)) {
|
|
15591
15816
|
current = join46(current, part);
|
|
15592
15817
|
if (lstatSync(current).isSymbolicLink()) throw new Error(`Local run-as root contains a symlink: ${current}`);
|
|
15593
15818
|
}
|
|
@@ -16094,7 +16319,7 @@ function shellQuoteArg(value) {
|
|
|
16094
16319
|
}
|
|
16095
16320
|
|
|
16096
16321
|
// src/cli/update-check.ts
|
|
16097
|
-
import { mkdir as
|
|
16322
|
+
import { mkdir as mkdir26, readFile as readFile38, writeFile as writeFile22 } from "fs/promises";
|
|
16098
16323
|
import { homedir as homedir14 } from "os";
|
|
16099
16324
|
import { dirname as dirname33, join as join48 } from "path";
|
|
16100
16325
|
var DEFAULT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -16149,7 +16374,7 @@ async function readCache(path) {
|
|
|
16149
16374
|
}
|
|
16150
16375
|
}
|
|
16151
16376
|
async function writeCache(path, cache) {
|
|
16152
|
-
await
|
|
16377
|
+
await mkdir26(dirname33(path), { recursive: true });
|
|
16153
16378
|
await writeFile22(path, `${JSON.stringify(cache, null, 2)}
|
|
16154
16379
|
`, "utf8");
|
|
16155
16380
|
}
|
|
@@ -16872,14 +17097,222 @@ function canonicalJson6(value) {
|
|
|
16872
17097
|
return `{${Object.keys(record).filter((key) => record[key] !== void 0).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson6(record[key])}`).join(",")}}`;
|
|
16873
17098
|
}
|
|
16874
17099
|
|
|
17100
|
+
// src/lifecycle/ownership-release.ts
|
|
17101
|
+
init_apply();
|
|
17102
|
+
init_manifest2();
|
|
17103
|
+
init_paths();
|
|
17104
|
+
init_transaction();
|
|
17105
|
+
import { createHash as createHash21, randomUUID as randomUUID6 } from "crypto";
|
|
17106
|
+
import { lstat as lstat5 } from "fs/promises";
|
|
17107
|
+
import { join as join52, resolve as resolve38 } from "path";
|
|
17108
|
+
init_coordinator();
|
|
17109
|
+
init_declarations();
|
|
17110
|
+
init_protocol();
|
|
17111
|
+
init_repository();
|
|
17112
|
+
init_process();
|
|
17113
|
+
init_transport();
|
|
17114
|
+
async function planReleaseOwnership(request) {
|
|
17115
|
+
return observe2(request);
|
|
17116
|
+
}
|
|
17117
|
+
async function applyReleaseOwnership(request) {
|
|
17118
|
+
for (const [name, value] of Object.entries({
|
|
17119
|
+
planDigest: request.planDigest,
|
|
17120
|
+
expectedSourceRevision: request.expectedSourceRevision,
|
|
17121
|
+
expectedInventoryRevision: request.expectedInventoryRevision
|
|
17122
|
+
})) {
|
|
17123
|
+
if (!value || !/^[a-f0-9]{64}$/.test(value)) throw new Error(`Reviewed SHA-256 ${name} is required to release a claim.`);
|
|
17124
|
+
}
|
|
17125
|
+
const workspaceRoot = resolve38(request.workspaceRoot);
|
|
17126
|
+
const existing = activeMutationMetadata();
|
|
17127
|
+
if (existing && (resolve38(existing.workspaceRoot) !== workspaceRoot || existing.reason !== request.reason.replace(/\r\n?/gu, "\n").trim() || request.operationId && request.operationId !== existing.operationId)) {
|
|
17128
|
+
throw new Error("Ownership release does not match the active governed workspace, reason or operation.");
|
|
17129
|
+
}
|
|
17130
|
+
const operationId = mutationOperationIdSchema.parse(existing?.operationId ?? request.operationId ?? randomUUID6());
|
|
17131
|
+
const auditPath = join52(workspaceRoot, ".agentwheel", "ownership-releases", `${operationId}.json`);
|
|
17132
|
+
const mutation = existing ? void 0 : await GovernedMutation.begin({
|
|
17133
|
+
workspaceRoot,
|
|
17134
|
+
commandName: "agentwheel ownership release-claim",
|
|
17135
|
+
reason: request.reason,
|
|
17136
|
+
operationId,
|
|
17137
|
+
noCommit: request.noCommit,
|
|
17138
|
+
globalRoot: request.globalRoot,
|
|
17139
|
+
anticipatedPaths: [auditPath]
|
|
17140
|
+
});
|
|
17141
|
+
try {
|
|
17142
|
+
if ((existing ?? mutation?.metadata())?.revisionMode === "commit-after-verify") {
|
|
17143
|
+
const repository = await discoverGitRepository(workspaceRoot);
|
|
17144
|
+
if (!repository) throw new Error("Ownership release requires a declarative Git audit path.");
|
|
17145
|
+
const ignored = await runProcess("git", ["-C", repository.root, "check-ignore", "--no-index", "--", auditPath], { allowExitCodes: [0, 1] });
|
|
17146
|
+
if (ignored.exitCode === 0) {
|
|
17147
|
+
throw new Error("Ownership release audit path is Git-ignored; explicitly declare it in repository policy before releasing a claim.");
|
|
17148
|
+
}
|
|
17149
|
+
}
|
|
17150
|
+
const result = await applyLocked(request, operationId, auditPath);
|
|
17151
|
+
await mutation?.complete();
|
|
17152
|
+
return result;
|
|
17153
|
+
} catch (error) {
|
|
17154
|
+
await mutation?.fail(error);
|
|
17155
|
+
throw error;
|
|
17156
|
+
}
|
|
17157
|
+
}
|
|
17158
|
+
async function applyLocked(request, operationId, auditPath) {
|
|
17159
|
+
const transport = request.transport ?? localTransport;
|
|
17160
|
+
if (transport.kind !== "local") throw new Error("Ownership release supports local targets only.");
|
|
17161
|
+
await assertNoSymlinkPath(join52(resolve38(request.targetRoot), ".agentwheel"));
|
|
17162
|
+
await assertNoSymlinkPath(auditPath);
|
|
17163
|
+
const lock = await acquireApplyLock(resolve38(request.targetRoot), request.adapter, transport, {}, {
|
|
17164
|
+
installationType: request.installationType,
|
|
17165
|
+
stateKey: request.stateKey
|
|
17166
|
+
});
|
|
17167
|
+
try {
|
|
17168
|
+
if ((await listAllApplyJournals(request.targetRoot, void 0, transport)).length) {
|
|
17169
|
+
throw new Error("Ownership release refuses pending runtime journals; recover them first.");
|
|
17170
|
+
}
|
|
17171
|
+
const plan = await observe2(request);
|
|
17172
|
+
if (plan.planDigest !== request.planDigest || plan.source.revision !== request.expectedSourceRevision || plan.manifestInventoryRevision !== request.expectedInventoryRevision) {
|
|
17173
|
+
throw new Error("Stale ownership release plan, manifest or inventory; replan required.");
|
|
17174
|
+
}
|
|
17175
|
+
if (await transport.pathExists(auditPath)) throw new Error(`Ownership release audit already exists: ${auditPath}`);
|
|
17176
|
+
declareMutationPath(auditPath);
|
|
17177
|
+
declareMutationPath(plan.source.manifestPath);
|
|
17178
|
+
const manifest = await readInstallManifest(plan.targetRoot, plan.adapter, transport, {
|
|
17179
|
+
installationType: plan.installationType,
|
|
17180
|
+
stateKey: plan.source.stateKey
|
|
17181
|
+
});
|
|
17182
|
+
if (!manifest || manifest.version !== 2 || manifest.revision !== plan.source.revision) {
|
|
17183
|
+
throw new Error("Source manifest changed after selection; replan required.");
|
|
17184
|
+
}
|
|
17185
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
17186
|
+
const content = `${JSON.stringify({
|
|
17187
|
+
schemaVersion: 1,
|
|
17188
|
+
event: "ownership-release-intent",
|
|
17189
|
+
operationId,
|
|
17190
|
+
reason: plan.reason,
|
|
17191
|
+
reviewedPlanDigest: plan.planDigest,
|
|
17192
|
+
plan,
|
|
17193
|
+
// This immutable dossier records intent. The linked journal verifies completion separately.
|
|
17194
|
+
completion: "Verify install state and the governed mutation receipt; intent alone does not prove completion."
|
|
17195
|
+
}, null, 2)}
|
|
17196
|
+
`;
|
|
17197
|
+
const metadata = mutationMetadataForApplyJournal();
|
|
17198
|
+
const journal = {
|
|
17199
|
+
version: metadata ? 2 : 1,
|
|
17200
|
+
...metadata ? { mutation: metadata } : {},
|
|
17201
|
+
mode: "uninstall",
|
|
17202
|
+
adapter: plan.adapter,
|
|
17203
|
+
installationType: plan.installationType,
|
|
17204
|
+
stateKey: plan.source.stateKey,
|
|
17205
|
+
targetRoot: plan.targetRoot,
|
|
17206
|
+
baseRevision: manifest.revision,
|
|
17207
|
+
createdAt: now,
|
|
17208
|
+
updatedAt: now,
|
|
17209
|
+
operations: [],
|
|
17210
|
+
completed: [],
|
|
17211
|
+
manifest: { ...manifest, entries: manifest.entries.filter((entry) => digest2(entry) !== plan.source.entryDigest) },
|
|
17212
|
+
metadataAudit: {
|
|
17213
|
+
kind: "ownership-release",
|
|
17214
|
+
workspaceRoot: plan.workspaceRoot,
|
|
17215
|
+
operationId,
|
|
17216
|
+
planDigest: plan.planDigest,
|
|
17217
|
+
inventory: plan.inventory,
|
|
17218
|
+
runtime: [{ path: plan.path, hash: plan.runtimeHash, snapshotDigest: plan.runtimeSnapshotDigest }],
|
|
17219
|
+
auditFiles: [{ path: `.agentwheel/ownership-releases/${operationId}.json`, content, sha256: hash(content) }]
|
|
17220
|
+
}
|
|
17221
|
+
};
|
|
17222
|
+
await writeApplyJournal(journal, transport);
|
|
17223
|
+
await commitManifestMetadataJournal(journal, transport);
|
|
17224
|
+
return { ...plan, applied: true, operationId, auditPath, retainedEntries: journal.manifest.entries.length };
|
|
17225
|
+
} finally {
|
|
17226
|
+
await lock.release();
|
|
17227
|
+
}
|
|
17228
|
+
}
|
|
17229
|
+
async function observe2(request) {
|
|
17230
|
+
const transport = request.transport ?? localTransport;
|
|
17231
|
+
if (transport.kind !== "local") throw new Error("Ownership release supports local targets only.");
|
|
17232
|
+
const targetRoot = resolve38(request.targetRoot);
|
|
17233
|
+
const workspaceRoot = resolve38(request.workspaceRoot);
|
|
17234
|
+
const reason = mutationReasonSchema.parse(request.reason.trim());
|
|
17235
|
+
if (!request.stateKey || stateKeyFor(request.adapter, { stateKey: request.stateKey }) !== request.stateKey || !request.installationType.trim() || !request.selector.includes("/")) {
|
|
17236
|
+
throw new Error("Ownership release requires an exact state key, installation type and type/name selector.");
|
|
17237
|
+
}
|
|
17238
|
+
const runtimePath = metadataRuntimePath(targetRoot, request.path);
|
|
17239
|
+
await assertNoSymlinkPath(workspaceRoot);
|
|
17240
|
+
await assertNoSymlinkPath(join52(targetRoot, ".agentwheel"));
|
|
17241
|
+
const manifests = await listInstallManifests(targetRoot, void 0, transport);
|
|
17242
|
+
for (const item of manifests) await assertNoSymlinkPath(item.path);
|
|
17243
|
+
const source = manifests.find((item) => item.stateKey === request.stateKey);
|
|
17244
|
+
const manifest = source?.manifest;
|
|
17245
|
+
if (!manifest || manifest.version !== 2 || manifest.adapter !== request.adapter || manifest.targetRoot !== targetRoot || manifest.installationType !== request.installationType || stateKeyFor(manifest.adapter, { stateKey: manifest.stateKey, installationType: manifest.installationType }) !== request.stateKey) {
|
|
17246
|
+
throw new Error("Source manifest does not prove the requested target, adapter, installation type and state key.");
|
|
17247
|
+
}
|
|
17248
|
+
const matches = manifest.entries.filter((entry2) => entry2.path === request.path && `${entry2.artifactType}/${entry2.artifactName}` === request.selector);
|
|
17249
|
+
if (matches.length !== 1) throw new Error(`Expected exactly one ownership claim at ${request.path} for ${request.selector}.`);
|
|
17250
|
+
const entry = matches[0];
|
|
17251
|
+
const owner = workspaceOwnerForRoot(request.fromWorkspaceRoot, request.fromFleetId);
|
|
17252
|
+
if (entry.workspaceOwner !== owner) throw new Error(`Workspace owner does not match the selected claim at ${entry.path}.`);
|
|
17253
|
+
if (entry.mergeStrategy || entry.mode || entry.semanticPlugin || entry.semanticCommand || entry.executed || entry.channel === "ejected") {
|
|
17254
|
+
throw new Error("Ownership release only supports plain file/directory claims; shared or semantic contributions are protected.");
|
|
17255
|
+
}
|
|
17256
|
+
for (const item of manifests) {
|
|
17257
|
+
for (const other of item.manifest.entries) {
|
|
17258
|
+
if (item === source && other === entry) continue;
|
|
17259
|
+
const otherPath = metadataRuntimePath(targetRoot, other.path);
|
|
17260
|
+
if (otherPath === runtimePath || otherPath.startsWith(`${runtimePath}/`) || runtimePath.startsWith(`${otherPath}/`)) {
|
|
17261
|
+
throw new Error(`Ambiguous or overlapping ownership claim at ${entry.path} in ${item.stateKey}.`);
|
|
17262
|
+
}
|
|
17263
|
+
}
|
|
17264
|
+
}
|
|
17265
|
+
const snapshotDigest = await ownershipRuntimeSnapshot(runtimePath);
|
|
17266
|
+
const runtimeKind = (await lstat5(runtimePath)).isDirectory() ? "dir" : "file";
|
|
17267
|
+
if (runtimeKind !== entry.kind) throw new Error("Selected runtime kind does not match the manifest claim.");
|
|
17268
|
+
const runtimeHash = await transport.hashPath(runtimePath);
|
|
17269
|
+
const inventory = manifests.map((item) => ({ stateKey: item.stateKey, revision: item.manifest.revision }));
|
|
17270
|
+
const plan = {
|
|
17271
|
+
adapter: request.adapter,
|
|
17272
|
+
installationType: request.installationType,
|
|
17273
|
+
targetRoot,
|
|
17274
|
+
workspaceRoot,
|
|
17275
|
+
owner,
|
|
17276
|
+
selector: request.selector,
|
|
17277
|
+
path: entry.path,
|
|
17278
|
+
source: {
|
|
17279
|
+
stateKey: request.stateKey,
|
|
17280
|
+
manifestPath: installManifestPath(targetRoot, request.adapter, { installationType: request.installationType, stateKey: request.stateKey }),
|
|
17281
|
+
revision: manifest.revision,
|
|
17282
|
+
entryDigest: digest2(entry),
|
|
17283
|
+
entry
|
|
17284
|
+
},
|
|
17285
|
+
recordedHash: entry.hash,
|
|
17286
|
+
runtimeHash,
|
|
17287
|
+
runtimeSnapshotDigest: snapshotDigest,
|
|
17288
|
+
manifestInventoryRevision: await computeInstallManifestInventoryRevision(targetRoot, void 0, transport),
|
|
17289
|
+
inventory,
|
|
17290
|
+
reason
|
|
17291
|
+
};
|
|
17292
|
+
return { ...plan, planDigest: digest2(plan) };
|
|
17293
|
+
}
|
|
17294
|
+
function hash(content) {
|
|
17295
|
+
return createHash21("sha256").update(content).digest("hex");
|
|
17296
|
+
}
|
|
17297
|
+
function digest2(value) {
|
|
17298
|
+
return hash(canonicalJson7(value));
|
|
17299
|
+
}
|
|
17300
|
+
function canonicalJson7(value) {
|
|
17301
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson7).join(",")}]`;
|
|
17302
|
+
if (!value || typeof value !== "object") return JSON.stringify(value);
|
|
17303
|
+
const record = value;
|
|
17304
|
+
return `{${Object.keys(record).filter((key) => record[key] !== void 0).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson7(record[key])}`).join(",")}}`;
|
|
17305
|
+
}
|
|
17306
|
+
|
|
16875
17307
|
// src/lifecycle/ownership-adopt-legacy.ts
|
|
16876
17308
|
init_apply();
|
|
16877
17309
|
init_manifest2();
|
|
16878
17310
|
init_paths();
|
|
16879
17311
|
init_transaction();
|
|
16880
|
-
|
|
17312
|
+
init_instructions_block();
|
|
17313
|
+
import { createHash as createHash23 } from "crypto";
|
|
16881
17314
|
import { realpath as realpath3 } from "fs/promises";
|
|
16882
|
-
import { isAbsolute as isAbsolute12, relative as
|
|
17315
|
+
import { isAbsolute as isAbsolute12, join as join54, relative as relative17, resolve as resolve40 } from "path";
|
|
16883
17316
|
init_manifest();
|
|
16884
17317
|
init_declarations();
|
|
16885
17318
|
init_transport();
|
|
@@ -16892,18 +17325,18 @@ init_instructions_block();
|
|
|
16892
17325
|
init_merge_removal();
|
|
16893
17326
|
init_graph_lock();
|
|
16894
17327
|
init_manifest();
|
|
16895
|
-
import { createHash as
|
|
16896
|
-
import { lstat as
|
|
16897
|
-
import { basename as basename23, dirname as dirname35, join as
|
|
17328
|
+
import { createHash as createHash22 } from "crypto";
|
|
17329
|
+
import { lstat as lstat6, readdir as readdir15 } from "fs/promises";
|
|
17330
|
+
import { basename as basename23, dirname as dirname35, join as join53, posix as posix7, relative as relative16, resolve as resolve39 } from "path";
|
|
16898
17331
|
async function inventoryHistoricalLocks(fleetRoot) {
|
|
16899
|
-
const root =
|
|
17332
|
+
const root = join53(fleetRoot, ".agentwheel", "locks");
|
|
16900
17333
|
const found = [];
|
|
16901
17334
|
async function visit(dir) {
|
|
16902
|
-
for (const entry of await
|
|
17335
|
+
for (const entry of await readdir15(dir, { withFileTypes: true }).catch((error) => {
|
|
16903
17336
|
if (error.code === "ENOENT") return [];
|
|
16904
17337
|
throw error;
|
|
16905
17338
|
})) {
|
|
16906
|
-
const path =
|
|
17339
|
+
const path = join53(dir, entry.name);
|
|
16907
17340
|
if (entry.isDirectory()) await visit(path);
|
|
16908
17341
|
else if (entry.isFile() && entry.name.endsWith(".graph-lock.json")) found.push(await historicalLock(path));
|
|
16909
17342
|
}
|
|
@@ -16912,15 +17345,15 @@ async function inventoryHistoricalLocks(fleetRoot) {
|
|
|
16912
17345
|
return found.sort((a, b) => a.path.localeCompare(b.path));
|
|
16913
17346
|
}
|
|
16914
17347
|
async function findLegacyNamedLocks(workspaceRoot, adapter, fingerprint) {
|
|
16915
|
-
const root =
|
|
17348
|
+
const root = join53(workspaceRoot, ".agentwheel", "locks");
|
|
16916
17349
|
const found = [];
|
|
16917
|
-
for (const target of await
|
|
17350
|
+
for (const target of await readdir15(root, { withFileTypes: true }).catch((error) => {
|
|
16918
17351
|
if (error.code === "ENOENT") return [];
|
|
16919
17352
|
throw error;
|
|
16920
17353
|
})) {
|
|
16921
17354
|
if (!target.isDirectory()) continue;
|
|
16922
|
-
const path =
|
|
16923
|
-
const stats = await
|
|
17355
|
+
const path = join53(root, target.name, adapter, `${fingerprint}.graph-lock.json`);
|
|
17356
|
+
const stats = await lstat6(path).catch((error) => {
|
|
16924
17357
|
if (error.code === "ENOENT") return void 0;
|
|
16925
17358
|
throw error;
|
|
16926
17359
|
});
|
|
@@ -16959,7 +17392,7 @@ async function planLegacyRecovery(input) {
|
|
|
16959
17392
|
const nodeHashes = new Map(graphPlan.bundle.graphLock.canonical.nodes.map((node) => [node.id, node.sourceHash]));
|
|
16960
17393
|
for (const op of graphPlan.plan.operations) {
|
|
16961
17394
|
if (!op.desiredHash || op.action === "remove" || op.action === "keep") continue;
|
|
16962
|
-
const path =
|
|
17395
|
+
const path = relative16(installRoot, op.destPath);
|
|
16963
17396
|
if (path.startsWith("..") || path === "") throw new Error(`Planner destination escapes target: ${op.destPath}`);
|
|
16964
17397
|
paths.add(path);
|
|
16965
17398
|
const items = desired.get(path) ?? [];
|
|
@@ -16980,8 +17413,8 @@ async function planLegacyRecovery(input) {
|
|
|
16980
17413
|
const currentNodeIds = [...new Set(current.map((item) => item.graphNodeId).filter((value) => Boolean(value)))].sort();
|
|
16981
17414
|
const currentSelectors = [...new Set(current.map((item) => item.logicalSelector).filter((value) => Boolean(value)))].sort();
|
|
16982
17415
|
const currentOwners = [...new Set(current.flatMap((item) => item.owners ?? []))].sort();
|
|
16983
|
-
const livePath =
|
|
16984
|
-
if (
|
|
17416
|
+
const livePath = resolve39(installRoot, path);
|
|
17417
|
+
if (relative16(installRoot, livePath).startsWith("..")) throw new Error(`Manifest path escapes target: ${path}`);
|
|
16985
17418
|
const liveHash = await transport.pathExists(livePath) ? await transport.hashPath(livePath) : null;
|
|
16986
17419
|
const reasons = [];
|
|
16987
17420
|
const currentResults = await Promise.all(current.map((op) => verifyCurrentOperation(op, liveHash, transport)));
|
|
@@ -17046,14 +17479,14 @@ async function planLegacyRecovery(input) {
|
|
|
17046
17479
|
};
|
|
17047
17480
|
}
|
|
17048
17481
|
function claimFor(item, entry, locks, binding) {
|
|
17049
|
-
const
|
|
17482
|
+
const digest3 = "graphLockDigest" in entry ? entry.graphLockDigest : void 0;
|
|
17050
17483
|
const nodeId = "graphNodeId" in entry ? entry.graphNodeId : void 0;
|
|
17051
17484
|
const selector = "logicalSelector" in entry ? entry.logicalSelector : void 0;
|
|
17052
|
-
const matches = locks.filter((candidate) => candidate.digest ===
|
|
17485
|
+
const matches = locks.filter((candidate) => candidate.digest === digest3);
|
|
17053
17486
|
const evidence = [];
|
|
17054
17487
|
const owner = "workspaceOwner" in entry ? entry.workspaceOwner : legacyUnownedWorkspaceOwner;
|
|
17055
|
-
const sameRoot = binding.transportKind === "ssh" ? posix7.normalize(item.manifest.targetRoot) === posix7.normalize(binding.installRoot) :
|
|
17056
|
-
const inTargetDir = matches.filter((candidate) =>
|
|
17488
|
+
const sameRoot = binding.transportKind === "ssh" ? posix7.normalize(item.manifest.targetRoot) === posix7.normalize(binding.installRoot) : resolve39(item.manifest.targetRoot) === resolve39(binding.installRoot);
|
|
17489
|
+
const inTargetDir = matches.filter((candidate) => resolve39(dirname35(candidate.path)) === resolve39(binding.targetLockDir));
|
|
17057
17490
|
const boundLocks = inTargetDir.filter((candidate) => lockBindsState(candidate, item, binding));
|
|
17058
17491
|
if (owner !== binding.expectedOwner) evidence.push("workspace owner differs from selected Fleet target");
|
|
17059
17492
|
if (item.manifest.version !== 2 || item.manifest.installationType !== binding.installationType) evidence.push("installation type differs or manifest is legacy v1");
|
|
@@ -17061,7 +17494,7 @@ function claimFor(item, entry, locks, binding) {
|
|
|
17061
17494
|
if (item.manifest.version === 2 && (item.manifest.stateKey !== item.stateKey || item.manifest.adapter !== binding.adapter)) {
|
|
17062
17495
|
evidence.push("manifest state key or adapter differs from filename");
|
|
17063
17496
|
}
|
|
17064
|
-
if (!
|
|
17497
|
+
if (!digest3) evidence.push("historical graph-lock digest missing");
|
|
17065
17498
|
else if (matches.length === 0) evidence.push("historical graph-lock digest has no matching lock");
|
|
17066
17499
|
if (matches.length && !inTargetDir.length) evidence.push("claim belongs to another Fleet target graph");
|
|
17067
17500
|
if (inTargetDir.length && !boundLocks.length) evidence.push("graph-lock fingerprint, filename, or state key does not bind claim");
|
|
@@ -17082,7 +17515,7 @@ function claimFor(item, entry, locks, binding) {
|
|
|
17082
17515
|
owner,
|
|
17083
17516
|
hash: entry.hash,
|
|
17084
17517
|
sourceHash: entry.sourceHash,
|
|
17085
|
-
graphLockDigest:
|
|
17518
|
+
graphLockDigest: digest3,
|
|
17086
17519
|
graphNodeId: nodeId,
|
|
17087
17520
|
logicalSelector: selector,
|
|
17088
17521
|
owners: "owners" in entry ? entry.owners : [entry.packageName ?? "legacy"],
|
|
@@ -17095,7 +17528,7 @@ function lockBindsState(candidate, manifest, binding) {
|
|
|
17095
17528
|
if (!name.endsWith(".graph-lock.json")) return false;
|
|
17096
17529
|
const fingerprint = name.slice(0, -".graph-lock.json".length);
|
|
17097
17530
|
if (!/^[a-f0-9]{64}$/u.test(fingerprint)) return false;
|
|
17098
|
-
const stable =
|
|
17531
|
+
const stable = resolve39(candidate.path) === resolve39(binding.stableLockPath);
|
|
17099
17532
|
if (stable) {
|
|
17100
17533
|
return manifest.stateKey === binding.stableStateKey && candidate.lock.canonical.targetFingerprint === binding.currentFingerprint;
|
|
17101
17534
|
}
|
|
@@ -17163,7 +17596,7 @@ function assertCompleteGraphPlan(plan) {
|
|
|
17163
17596
|
}
|
|
17164
17597
|
}
|
|
17165
17598
|
function sha2566(value) {
|
|
17166
|
-
return
|
|
17599
|
+
return createHash22("sha256").update(value).digest("hex");
|
|
17167
17600
|
}
|
|
17168
17601
|
|
|
17169
17602
|
// src/lifecycle/ownership-adopt-legacy.ts
|
|
@@ -17174,24 +17607,31 @@ function desiredCoverageFromPlan(plan) {
|
|
|
17174
17607
|
artifactType: operation.artifactType,
|
|
17175
17608
|
artifactName: operation.artifactName,
|
|
17176
17609
|
kind: operation.kind,
|
|
17610
|
+
...operation.mode ? { mode: operation.mode } : {},
|
|
17611
|
+
...operation.mode && operation.logicalSelector ? { logicalSelector: operation.logicalSelector } : {},
|
|
17177
17612
|
...operation.packageName ? { packageName: operation.packageName } : {}
|
|
17178
17613
|
})).sort((left, right) => left.path.localeCompare(right.path));
|
|
17179
17614
|
}
|
|
17180
17615
|
async function planAdoptLegacyOwnership(request) {
|
|
17181
|
-
return
|
|
17616
|
+
return observe3(request, request.transport ?? localTransport);
|
|
17182
17617
|
}
|
|
17183
17618
|
async function applyAdoptLegacyOwnership(request) {
|
|
17184
17619
|
assertApplyPreconditions2(request);
|
|
17185
17620
|
const transport = request.transport ?? localTransport;
|
|
17186
|
-
const targetRoot =
|
|
17621
|
+
const targetRoot = resolve40(request.targetRoot);
|
|
17187
17622
|
const installationType = request.installationType.trim();
|
|
17623
|
+
if (transport.kind !== "local") throw new Error("Legacy ownership adoption requires a local filesystem transport.");
|
|
17624
|
+
const beforeLock = await readInstallManifest(targetRoot, request.adapter, transport, { installationType, stateKey: request.sourceStateKey });
|
|
17625
|
+
if (beforeLock?.entries.some((entry) => entry.mode === "managed-block")) {
|
|
17626
|
+
await assertNoSymlinkPath(join54(targetRoot, ".agentwheel"));
|
|
17627
|
+
}
|
|
17188
17628
|
const lock = await acquireApplyLock(targetRoot, request.adapter, transport, {}, {
|
|
17189
17629
|
installationType,
|
|
17190
17630
|
stateKey: request.destinationStateKey
|
|
17191
17631
|
});
|
|
17192
17632
|
try {
|
|
17193
17633
|
await assertNoPendingJournals(targetRoot, request.adapter, transport);
|
|
17194
|
-
const current = await
|
|
17634
|
+
const current = await observe3(request, transport);
|
|
17195
17635
|
assertExpected(request.planDigest, current.planDigest, "plan digest");
|
|
17196
17636
|
assertExpected(request.expectedSourceRevision, current.source.revision, "source manifest revision");
|
|
17197
17637
|
assertExpected(request.expectedDestinationRevision, current.destination.revision ?? absentRevision, "destination manifest revision");
|
|
@@ -17211,6 +17651,7 @@ async function applyAdoptLegacyOwnership(request) {
|
|
|
17211
17651
|
throw new Error("Source manifest selection changed while locked; replan required.");
|
|
17212
17652
|
}
|
|
17213
17653
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
17654
|
+
let guards = current.metadataGuards;
|
|
17214
17655
|
if (adopted.length > 0) {
|
|
17215
17656
|
declareMutationPath(current.destination.manifestPath);
|
|
17216
17657
|
const base = destination ?? {
|
|
@@ -17224,13 +17665,22 @@ async function applyAdoptLegacyOwnership(request) {
|
|
|
17224
17665
|
legacy: false,
|
|
17225
17666
|
entries: []
|
|
17226
17667
|
};
|
|
17227
|
-
|
|
17668
|
+
const next = withManifestRevision({
|
|
17228
17669
|
...base,
|
|
17670
|
+
generatedAt: now,
|
|
17229
17671
|
entries: [...base.entries, ...adopted].sort((left, right) => left.path.localeCompare(right.path))
|
|
17230
|
-
}
|
|
17672
|
+
});
|
|
17673
|
+
await commitMetadata(next, destination?.revision ?? null, now, transport, guards);
|
|
17674
|
+
if (guards) guards = {
|
|
17675
|
+
...guards,
|
|
17676
|
+
inventory: [
|
|
17677
|
+
...guards.inventory.filter((item) => item.stateKey !== current.destination.stateKey),
|
|
17678
|
+
{ stateKey: current.destination.stateKey, revision: next.revision }
|
|
17679
|
+
].sort((left, right) => left.stateKey.localeCompare(right.stateKey))
|
|
17680
|
+
};
|
|
17231
17681
|
}
|
|
17232
17682
|
declareMutationPath(current.source.manifestPath);
|
|
17233
|
-
await commitMetadata({ ...source, entries: retained }, source.revision, now, transport);
|
|
17683
|
+
await commitMetadata({ ...source, entries: retained }, source.revision, now, transport, guards);
|
|
17234
17684
|
return {
|
|
17235
17685
|
...current,
|
|
17236
17686
|
applied: true,
|
|
@@ -17243,7 +17693,7 @@ async function applyAdoptLegacyOwnership(request) {
|
|
|
17243
17693
|
await lock.release();
|
|
17244
17694
|
}
|
|
17245
17695
|
}
|
|
17246
|
-
async function commitMetadata(manifest, baseRevision, now, transport) {
|
|
17696
|
+
async function commitMetadata(manifest, baseRevision, now, transport, metadataGuards) {
|
|
17247
17697
|
const mutation = mutationMetadataForApplyJournal();
|
|
17248
17698
|
const journal = {
|
|
17249
17699
|
version: mutation ? 2 : 1,
|
|
@@ -17258,12 +17708,13 @@ async function commitMetadata(manifest, baseRevision, now, transport) {
|
|
|
17258
17708
|
updatedAt: now,
|
|
17259
17709
|
operations: [],
|
|
17260
17710
|
completed: [],
|
|
17261
|
-
manifest
|
|
17711
|
+
manifest,
|
|
17712
|
+
...metadataGuards ? { metadataGuards } : {}
|
|
17262
17713
|
};
|
|
17263
17714
|
await writeApplyJournal(journal, transport);
|
|
17264
17715
|
await commitManifestMetadataJournal(journal, transport);
|
|
17265
17716
|
}
|
|
17266
|
-
async function
|
|
17717
|
+
async function observe3(request, transport) {
|
|
17267
17718
|
if (transport.kind === "ssh") {
|
|
17268
17719
|
throw new Error("Legacy ownership adoption does not support SSH targets; legacy SSH state does not prove its endpoint.");
|
|
17269
17720
|
}
|
|
@@ -17294,11 +17745,21 @@ async function observe2(request, transport) {
|
|
|
17294
17745
|
desiredByPath
|
|
17295
17746
|
);
|
|
17296
17747
|
const selectedPaths = new Set(selectedEntries.map((entry) => entry.path));
|
|
17748
|
+
const hasManagedBlock = selectedEntries.some((entry) => entry.mode === "managed-block");
|
|
17749
|
+
if (hasManagedBlock && request.carryDrift) throw new Error("--carry-drift is forbidden for managed instruction block adoption.");
|
|
17297
17750
|
const selected = [];
|
|
17298
17751
|
for (const entry of selectedEntries) {
|
|
17299
17752
|
const runtimePath = containedArtifactPath(targetRoot, entry.path);
|
|
17753
|
+
const managedBlock = entry.mode === "managed-block" ? exactManagedInstructionBlockSnapshot(
|
|
17754
|
+
await transport.readFile(runtimePath),
|
|
17755
|
+
managedInstructionSelector(entry.logicalSelector, entry.artifactType, entry.artifactName)
|
|
17756
|
+
) : void 0;
|
|
17757
|
+
if (managedBlock && managedBlock.hash !== entry.hash) {
|
|
17758
|
+
throw new Error(`Managed instruction block hash differs from its recorded manifest hash at ${entry.path}; adoption is refused.`);
|
|
17759
|
+
}
|
|
17760
|
+
const runtimeSnapshotDigest = hasManagedBlock ? await ownershipRuntimeSnapshot(runtimePath) : void 0;
|
|
17300
17761
|
const runtimeHash = await transport.pathExists(runtimePath) ? await transport.hashPath(runtimePath) : null;
|
|
17301
|
-
const drift = runtimeHash !== entry.hash;
|
|
17762
|
+
const drift = managedBlock ? false : runtimeHash !== entry.hash;
|
|
17302
17763
|
if (drift && request.carryDrift !== true) {
|
|
17303
17764
|
throw new Error(
|
|
17304
17765
|
runtimeHash === null ? `Managed artifact is missing at ${entry.path}; pass --carry-drift to keep the recorded hash.` : `Managed artifact is drifted at ${entry.path}: manifest ${entry.hash}, current ${runtimeHash}; pass --carry-drift to keep the recorded hash.`
|
|
@@ -17321,9 +17782,28 @@ async function observe2(request, transport) {
|
|
|
17321
17782
|
...destinationEntryDigest ? { destinationEntryDigest } : {},
|
|
17322
17783
|
recordedHash: entry.hash,
|
|
17323
17784
|
runtimeHash,
|
|
17324
|
-
drift
|
|
17785
|
+
drift,
|
|
17786
|
+
...runtimeSnapshotDigest ? { runtimeSnapshotDigest } : {},
|
|
17787
|
+
...managedBlock ? { managedBlock } : {}
|
|
17325
17788
|
});
|
|
17326
17789
|
}
|
|
17790
|
+
const guardedInventory = hasManagedBlock ? await listInstallManifests(targetRoot, void 0, transport) : void 0;
|
|
17791
|
+
if (guardedInventory) {
|
|
17792
|
+
for (const item of guardedInventory) {
|
|
17793
|
+
await assertNoSymlinkPath(item.path);
|
|
17794
|
+
for (const other of item.manifest.entries) {
|
|
17795
|
+
const otherPath = containedArtifactPath(targetRoot, other.path);
|
|
17796
|
+
for (const entry of selectedEntries) {
|
|
17797
|
+
const path = containedArtifactPath(targetRoot, entry.path);
|
|
17798
|
+
const overlap = otherPath === path || otherPath.startsWith(`${path}/`) || path.startsWith(`${otherPath}/`);
|
|
17799
|
+
if (!overlap) continue;
|
|
17800
|
+
if (item.stateKey === input.sourceStateKey && otherPath === path && "workspaceOwner" in other && entryDigest(other) === entryDigest(entry)) continue;
|
|
17801
|
+
if (item.stateKey === input.destinationStateKey && otherPath === path && "workspaceOwner" in other && coversSourceEntry(other, entry, input.destinationOwner)) continue;
|
|
17802
|
+
throw new Error(`Overlapping or duplicate ownership claim at ${entry.path} in ${item.stateKey}; managed-block adoption is refused.`);
|
|
17803
|
+
}
|
|
17804
|
+
}
|
|
17805
|
+
}
|
|
17806
|
+
}
|
|
17327
17807
|
const remainingDuplicates = [];
|
|
17328
17808
|
const selectedByPath = new Map(selectedEntries.map((entry) => [entry.path, entry]));
|
|
17329
17809
|
for (const item of await listInstallManifests(targetRoot, adapter, transport)) {
|
|
@@ -17360,20 +17840,35 @@ async function observe2(request, transport) {
|
|
|
17360
17840
|
owner: input.destinationOwner,
|
|
17361
17841
|
workspaceRoot: input.workspaceRoot
|
|
17362
17842
|
},
|
|
17363
|
-
manifestInventoryRevision: await computeInstallManifestInventoryRevision(targetRoot, adapter, transport),
|
|
17843
|
+
manifestInventoryRevision: await computeInstallManifestInventoryRevision(targetRoot, hasManagedBlock ? void 0 : adapter, transport),
|
|
17364
17844
|
selected: selected.sort((left, right) => left.path.localeCompare(right.path)),
|
|
17365
17845
|
retained: retained.sort((left, right) => left.path.localeCompare(right.path) || left.owner.localeCompare(right.owner)),
|
|
17366
17846
|
remainingDuplicates: remainingDuplicates.sort((left, right) => left.stateKey.localeCompare(right.stateKey) || left.path.localeCompare(right.path)),
|
|
17367
17847
|
// identity only, so a newer package revision doesn't invalidate a reviewed plan
|
|
17368
|
-
desiredCoverage: request.desiredCoverage.filter((item) => selectedPaths.has(item.path)).map(({ path, artifactType, artifactName, kind
|
|
17848
|
+
desiredCoverage: request.desiredCoverage.filter((item) => selectedPaths.has(item.path)).map(({ path, artifactType, artifactName, kind, mode, logicalSelector }) => ({
|
|
17849
|
+
path,
|
|
17850
|
+
artifactType,
|
|
17851
|
+
artifactName,
|
|
17852
|
+
kind,
|
|
17853
|
+
...mode ? { mode, logicalSelector: managedInstructionSelector(logicalSelector, artifactType, artifactName) } : {}
|
|
17854
|
+
})).sort((left, right) => left.path.localeCompare(right.path) || left.artifactType.localeCompare(right.artifactType) || left.artifactName.localeCompare(right.artifactName)),
|
|
17855
|
+
...guardedInventory ? { metadataGuards: {
|
|
17856
|
+
inventory: guardedInventory.map((item) => ({ stateKey: item.stateKey, revision: item.manifest.revision })),
|
|
17857
|
+
runtime: selected.map((entry) => ({
|
|
17858
|
+
path: entry.path,
|
|
17859
|
+
hash: entry.runtimeHash,
|
|
17860
|
+
snapshotDigest: entry.runtimeSnapshotDigest,
|
|
17861
|
+
...entry.managedBlock ? { managedBlock: entry.managedBlock } : {}
|
|
17862
|
+
}))
|
|
17863
|
+
} } : {}
|
|
17369
17864
|
};
|
|
17370
|
-
return { ...withoutDigest, planDigest:
|
|
17865
|
+
return { ...withoutDigest, planDigest: createHash23("sha256").update(canonicalJson6(withoutDigest)).digest("hex") };
|
|
17371
17866
|
}
|
|
17372
17867
|
async function normalizeRequest2(request) {
|
|
17373
17868
|
if (request.destinationFleetId !== void 0) {
|
|
17374
17869
|
throw new Error("Legacy ownership adoption targets a nested workspace; a Fleet target uses fleet normalize or ownership retire-stale.");
|
|
17375
17870
|
}
|
|
17376
|
-
const targetRoot =
|
|
17871
|
+
const targetRoot = resolve40(request.targetRoot);
|
|
17377
17872
|
const installationType = request.installationType.trim();
|
|
17378
17873
|
const sourceStateKey = exactStateKey(request.adapter, request.sourceStateKey, installationType, "source");
|
|
17379
17874
|
const destinationStateKey = exactStateKey(request.adapter, request.destinationStateKey, installationType, "destination");
|
|
@@ -17385,22 +17880,22 @@ async function normalizeRequest2(request) {
|
|
|
17385
17880
|
const scope = await resolveWorkspaceOwnershipScope(request.workspaceRoot, { globalRoot: request.globalRoot });
|
|
17386
17881
|
const destinationOwner = workspaceOwnerForRoot(scope.root, scope.fleetId);
|
|
17387
17882
|
const workspaceRoot = await realpath3(request.workspaceRoot);
|
|
17388
|
-
const sourceRoot =
|
|
17883
|
+
const sourceRoot = resolve40(request.fromWorkspaceRoot);
|
|
17389
17884
|
const sourceOwner = workspaceOwnerForRoot(sourceRoot);
|
|
17390
17885
|
let sourceClass;
|
|
17391
|
-
if (sourceRoot === workspaceRoot || sourceRoot ===
|
|
17886
|
+
if (sourceRoot === workspaceRoot || sourceRoot === resolve40(request.workspaceRoot)) {
|
|
17392
17887
|
if (sourceOwner === destinationOwner) {
|
|
17393
17888
|
throw new Error(`${sourceOwner} is already the destination owner; ordinary install migration handles its legacy state.`);
|
|
17394
17889
|
}
|
|
17395
17890
|
sourceClass = "own-legacy-owner";
|
|
17396
17891
|
} else {
|
|
17397
17892
|
const sourceForms = [sourceRoot, ...await realpath3(sourceRoot).then((path) => [path], () => [])];
|
|
17398
|
-
const workspaceForms = [workspaceRoot,
|
|
17893
|
+
const workspaceForms = [workspaceRoot, resolve40(request.workspaceRoot), scope.root];
|
|
17399
17894
|
if (sourceForms.some((source) => workspaceForms.some((workspace) => containsPath2(source, workspace) || containsPath2(workspace, source)))) {
|
|
17400
17895
|
throw new Error(`Source root ${sourceRoot} contains or is contained by the destination workspace; it cannot be adopted as a foreign root.`);
|
|
17401
17896
|
}
|
|
17402
17897
|
for (const fleet of await listRegisteredFleets({ globalRoot: request.globalRoot })) {
|
|
17403
|
-
const fleetForms = [
|
|
17898
|
+
const fleetForms = [resolve40(fleet.root), ...await realpath3(fleet.root).then((path) => [path], () => [])];
|
|
17404
17899
|
if (sourceForms.some((source) => fleetForms.some((root) => containsPath2(root, source)))) {
|
|
17405
17900
|
throw new Error(`Source root ${sourceRoot} is inside registered Fleet '${fleet.id}'; use fleet normalize or ownership retire-stale.`);
|
|
17406
17901
|
}
|
|
@@ -17436,7 +17931,7 @@ async function selectLegacySource(input, stateKey, fingerprint, source, desiredB
|
|
|
17436
17931
|
);
|
|
17437
17932
|
}
|
|
17438
17933
|
for (const entry of source.entries) {
|
|
17439
|
-
if (entry.workspaceOwner === input.sourceOwner)
|
|
17934
|
+
if (entry.workspaceOwner === input.sourceOwner) assertSupportedLegacyEntry(entry, input.targetRoot);
|
|
17440
17935
|
}
|
|
17441
17936
|
const entries = [];
|
|
17442
17937
|
const retained = [];
|
|
@@ -17514,20 +18009,21 @@ async function readDestination(identity, stateKey, transport) {
|
|
|
17514
18009
|
}
|
|
17515
18010
|
return requireManifest(identity, stateKey, transport, "destination");
|
|
17516
18011
|
}
|
|
17517
|
-
function
|
|
17518
|
-
|
|
18012
|
+
function assertSupportedLegacyEntry(entry, targetRoot) {
|
|
18013
|
+
const supportedBlock = entry.mode === "managed-block" && entry.kind === "file" && entry.artifactType === "instructions";
|
|
18014
|
+
if (entry.mode && !supportedBlock || entry.mergeStrategy || entry.semanticPlugin || entry.semanticCommand || entry.executed) {
|
|
17519
18015
|
throw new Error(`Legacy ownership adoption supports only plain file or directory entries; ${entry.path} is not one.`);
|
|
17520
18016
|
}
|
|
17521
18017
|
containedArtifactPath(targetRoot, entry.path);
|
|
17522
18018
|
}
|
|
17523
18019
|
function desiredCovers(item, entry) {
|
|
17524
|
-
return item.artifactType === entry.artifactType && item.artifactName === entry.artifactName && item.kind === entry.kind && (!item.packageName || !entry.packageName || item.packageName === entry.packageName);
|
|
18020
|
+
return item.artifactType === entry.artifactType && item.artifactName === entry.artifactName && item.kind === entry.kind && (!item.packageName || !entry.packageName || item.packageName === entry.packageName) && (entry.mode !== "managed-block" || item.mode === "managed-block" && managedInstructionSelector(item.logicalSelector, item.artifactType, item.artifactName) === managedInstructionSelector(entry.logicalSelector, entry.artifactType, entry.artifactName));
|
|
17525
18021
|
}
|
|
17526
18022
|
function coversSourceEntry(destination, source, owner) {
|
|
17527
|
-
return destination.workspaceOwner === owner &&
|
|
18023
|
+
return destination.workspaceOwner === owner && destination.mode === source.mode && !destination.mergeStrategy && !destination.semanticPlugin && sameRecordedArtifact(destination, source) && (source.mode !== "managed-block" || entryDigest({ ...destination, workspaceOwner: source.workspaceOwner }) === entryDigest(source));
|
|
17528
18024
|
}
|
|
17529
18025
|
function sameRecordedArtifact(left, right) {
|
|
17530
|
-
return left.artifactType === right.artifactType && left.artifactName === right.artifactName && left.kind === right.kind && left.hash === right.hash && left.sourceHash === right.sourceHash;
|
|
18026
|
+
return left.artifactType === right.artifactType && left.artifactName === right.artifactName && left.kind === right.kind && left.hash === right.hash && left.sourceHash === right.sourceHash && left.mode === right.mode && (left.mode !== "managed-block" || managedInstructionSelector(left.logicalSelector, left.artifactType, left.artifactName) === managedInstructionSelector(right.logicalSelector, right.artifactType, right.artifactName));
|
|
17531
18027
|
}
|
|
17532
18028
|
async function assertNoPendingJournals(targetRoot, adapter, transport) {
|
|
17533
18029
|
const pending = await listAllApplyJournals(targetRoot, adapter, transport);
|
|
@@ -17549,14 +18045,14 @@ function assertApplyPreconditions2(request) {
|
|
|
17549
18045
|
}
|
|
17550
18046
|
}
|
|
17551
18047
|
function containsPath2(parent, candidate) {
|
|
17552
|
-
const path =
|
|
18048
|
+
const path = relative17(parent, candidate);
|
|
17553
18049
|
return path === "" || !path.startsWith("..") && !isAbsolute12(path);
|
|
17554
18050
|
}
|
|
17555
18051
|
|
|
17556
18052
|
// src/version/policy.ts
|
|
17557
18053
|
import { execFile as execFile7 } from "child_process";
|
|
17558
18054
|
import { readFile as readFile40 } from "fs/promises";
|
|
17559
|
-
import { join as
|
|
18055
|
+
import { join as join55, resolve as resolve41 } from "path";
|
|
17560
18056
|
import { promisify as promisify7 } from "util";
|
|
17561
18057
|
import { parse as parseJsonc } from "jsonc-parser";
|
|
17562
18058
|
import { z as z15 } from "zod";
|
|
@@ -17653,7 +18149,7 @@ async function discoverVersionsFromSource(pkg, workspaceRoot) {
|
|
|
17653
18149
|
if (tagged.length > 0) return tagged;
|
|
17654
18150
|
}
|
|
17655
18151
|
if (driverName === "local") {
|
|
17656
|
-
const root =
|
|
18152
|
+
const root = resolve41(workspaceRoot, pkg.source);
|
|
17657
18153
|
const manifest2 = await readPackageManifest(root);
|
|
17658
18154
|
const current = manifest2 ? [{ version: manifest2.version, ref: pkg.requestedRef ?? root }] : [];
|
|
17659
18155
|
try {
|
|
@@ -17668,7 +18164,7 @@ async function discoverVersionsFromSource(pkg, workspaceRoot) {
|
|
|
17668
18164
|
}
|
|
17669
18165
|
const driver = getSourceDriver(driverName);
|
|
17670
18166
|
const resolved = await driver.resolve(pkg.source, {
|
|
17671
|
-
cacheRoot:
|
|
18167
|
+
cacheRoot: join55(workspaceRoot, ".agentwheel", "cache"),
|
|
17672
18168
|
mode: "tracking",
|
|
17673
18169
|
ref: pkg.requestedRef
|
|
17674
18170
|
});
|
|
@@ -17762,7 +18258,7 @@ function gitUrlFromSource(source) {
|
|
|
17762
18258
|
throw new Error(`Version discovery does not support Git source: ${source}`);
|
|
17763
18259
|
}
|
|
17764
18260
|
function versionCachePath(workspaceRoot) {
|
|
17765
|
-
return
|
|
18261
|
+
return join55(workspaceRoot, ".agentwheel", "cache", "version-index.json");
|
|
17766
18262
|
}
|
|
17767
18263
|
async function readVersionCache(path) {
|
|
17768
18264
|
if (!await pathExists(path)) return { schemaVersion: 1, sources: {} };
|
|
@@ -17779,7 +18275,7 @@ init_semver();
|
|
|
17779
18275
|
// src/profile/members.ts
|
|
17780
18276
|
import { execFile as execFile8 } from "child_process";
|
|
17781
18277
|
import { readFile as readFile41 } from "fs/promises";
|
|
17782
|
-
import { join as
|
|
18278
|
+
import { join as join56, resolve as resolve42 } from "path";
|
|
17783
18279
|
import { promisify as promisify8 } from "util";
|
|
17784
18280
|
import { z as z17 } from "zod";
|
|
17785
18281
|
|
|
@@ -17955,7 +18451,7 @@ async function invokeMemberStatus(member, parentWorkspace, chain, options, cliEn
|
|
|
17955
18451
|
let stderr = "";
|
|
17956
18452
|
try {
|
|
17957
18453
|
if (member.transport === "local") {
|
|
17958
|
-
const workspace =
|
|
18454
|
+
const workspace = resolve42(parentWorkspace, member.workspace);
|
|
17959
18455
|
const result = await execFileAsync8(process.execPath, [cliEntry, ...args], {
|
|
17960
18456
|
cwd: workspace,
|
|
17961
18457
|
env: env2,
|
|
@@ -18001,7 +18497,7 @@ async function runMemberAgentwheel(member, parentWorkspace, args, chain) {
|
|
|
18001
18497
|
process.execPath,
|
|
18002
18498
|
[process.argv[1], ...memberCommandArgs(member, args)],
|
|
18003
18499
|
{
|
|
18004
|
-
cwd:
|
|
18500
|
+
cwd: resolve42(parentWorkspace, member.workspace),
|
|
18005
18501
|
env: env2,
|
|
18006
18502
|
maxBuffer: 20 * 1024 * 1024
|
|
18007
18503
|
}
|
|
@@ -18081,7 +18577,7 @@ function memberFailure(member, health, error) {
|
|
|
18081
18577
|
};
|
|
18082
18578
|
}
|
|
18083
18579
|
function memberCachePath(workspaceRoot, profileName, memberId) {
|
|
18084
|
-
return
|
|
18580
|
+
return join56(workspaceRoot, ".agentwheel", "cache", "member-status", profileName, `${memberId}.json`);
|
|
18085
18581
|
}
|
|
18086
18582
|
async function readMemberCache(path) {
|
|
18087
18583
|
if (!await pathExists(path)) return void 0;
|
|
@@ -18107,7 +18603,7 @@ function assertNoCompositeCycle(workspaceRoot, profileName, chain) {
|
|
|
18107
18603
|
}
|
|
18108
18604
|
}
|
|
18109
18605
|
function compositeKey(workspaceRoot, profileName) {
|
|
18110
|
-
return `${
|
|
18606
|
+
return `${resolve42(workspaceRoot)}#${profileName}`;
|
|
18111
18607
|
}
|
|
18112
18608
|
|
|
18113
18609
|
// src/status/repository.ts
|
|
@@ -18154,10 +18650,10 @@ function valueAfter(lines, prefix) {
|
|
|
18154
18650
|
}
|
|
18155
18651
|
|
|
18156
18652
|
// src/catalogue/client.ts
|
|
18157
|
-
import { createHash as
|
|
18653
|
+
import { createHash as createHash24 } from "crypto";
|
|
18158
18654
|
import { readFile as readFile42, rm as rm15 } from "fs/promises";
|
|
18159
18655
|
import { homedir as homedir15 } from "os";
|
|
18160
|
-
import { join as
|
|
18656
|
+
import { join as join57 } from "path";
|
|
18161
18657
|
|
|
18162
18658
|
// src/model/catalogue.ts
|
|
18163
18659
|
import { z as z18 } from "zod";
|
|
@@ -18430,18 +18926,18 @@ var CatalogueClient = class {
|
|
|
18430
18926
|
}
|
|
18431
18927
|
return {
|
|
18432
18928
|
value: schema.parse(value),
|
|
18433
|
-
digest:
|
|
18929
|
+
digest: createHash24("sha256").update(bytes).digest("hex")
|
|
18434
18930
|
};
|
|
18435
18931
|
}
|
|
18436
18932
|
};
|
|
18437
18933
|
function defaultCatalogueCachePath() {
|
|
18438
|
-
return
|
|
18934
|
+
return join57(homedir15(), ".agentwheel", "catalogue-cache.json");
|
|
18439
18935
|
}
|
|
18440
18936
|
function sameSources2(a, b) {
|
|
18441
18937
|
return a.length === b.length && a.every((source, index) => source === b[index]);
|
|
18442
18938
|
}
|
|
18443
18939
|
function catalogueContentHash(enriched, vercel) {
|
|
18444
|
-
return
|
|
18940
|
+
return createHash24("sha256").update(JSON.stringify({ enriched, vercel })).digest("hex");
|
|
18445
18941
|
}
|
|
18446
18942
|
|
|
18447
18943
|
// src/search/index.ts
|
|
@@ -18803,9 +19299,9 @@ function includesToken(normalizedText, token) {
|
|
|
18803
19299
|
}
|
|
18804
19300
|
|
|
18805
19301
|
// src/semantic/index.ts
|
|
18806
|
-
import { createHash as
|
|
19302
|
+
import { createHash as createHash25 } from "crypto";
|
|
18807
19303
|
import { homedir as homedir16 } from "os";
|
|
18808
|
-
import { join as
|
|
19304
|
+
import { join as join58 } from "path";
|
|
18809
19305
|
import { env, pipeline } from "@huggingface/transformers";
|
|
18810
19306
|
var DEFAULT_SEMANTIC_INDEX_URL = "https://raw.githubusercontent.com/NestDevLab/agentwheel-registry/main/catalogue-semantic-index/gte-v1/";
|
|
18811
19307
|
var CONTRACT = {
|
|
@@ -18893,13 +19389,13 @@ var SemanticSearchClient = class {
|
|
|
18893
19389
|
if (!response.ok) throw new Error(`Semantic index file failed (${response.status}): ${descriptor.path}`);
|
|
18894
19390
|
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
18895
19391
|
if (bytes.byteLength !== descriptor.bytes) throw new Error(`Semantic index file size does not match: ${descriptor.path}`);
|
|
18896
|
-
const
|
|
18897
|
-
if (
|
|
19392
|
+
const digest3 = createHash25("sha256").update(bytes).digest("hex");
|
|
19393
|
+
if (digest3 !== descriptor.sha256) throw new Error(`Semantic index checksum does not match: ${descriptor.path}`);
|
|
18898
19394
|
return bytes;
|
|
18899
19395
|
}
|
|
18900
19396
|
};
|
|
18901
19397
|
async function embedQuery(query) {
|
|
18902
|
-
env.cacheDir =
|
|
19398
|
+
env.cacheDir = join58(homedir16(), ".agentwheel", "semantic-models");
|
|
18903
19399
|
const extractor = await pipeline("feature-extraction", CONTRACT.model.id, {
|
|
18904
19400
|
revision: CONTRACT.model.revision,
|
|
18905
19401
|
dtype: CONTRACT.model.dtype,
|
|
@@ -18983,9 +19479,9 @@ function ensureTrailingSlash(value) {
|
|
|
18983
19479
|
}
|
|
18984
19480
|
|
|
18985
19481
|
// src/trial/skill.ts
|
|
18986
|
-
import { createHash as
|
|
19482
|
+
import { createHash as createHash26 } from "crypto";
|
|
18987
19483
|
import { readFile as readFile43, stat as stat16 } from "fs/promises";
|
|
18988
|
-
import { join as
|
|
19484
|
+
import { join as join59 } from "path";
|
|
18989
19485
|
import { parse as parseYaml2 } from "yaml";
|
|
18990
19486
|
var MAX_TRIAL_SKILL_BYTES = 512 * 1024;
|
|
18991
19487
|
async function createSkillTrial(driver, resolved, selectors) {
|
|
@@ -18997,7 +19493,7 @@ async function createSkillTrial(driver, resolved, selectors) {
|
|
|
18997
19493
|
throw new Error("Skill trial requires exactly one selected skill. Use --skill <name> or --select skills/<name>.");
|
|
18998
19494
|
}
|
|
18999
19495
|
const artifact = skills[0];
|
|
19000
|
-
const path = artifact.kind === "dir" ?
|
|
19496
|
+
const path = artifact.kind === "dir" ? join59(artifact.sourcePath, "SKILL.md") : artifact.sourcePath;
|
|
19001
19497
|
const info = await stat16(path);
|
|
19002
19498
|
if (info.size > MAX_TRIAL_SKILL_BYTES) {
|
|
19003
19499
|
throw new Error(`Skill trial exceeds the ${MAX_TRIAL_SKILL_BYTES / 1024} KiB content limit.`);
|
|
@@ -19013,7 +19509,7 @@ async function createSkillTrial(driver, resolved, selectors) {
|
|
|
19013
19509
|
skill: {
|
|
19014
19510
|
name: artifact.name,
|
|
19015
19511
|
relativePath: artifact.relativePath,
|
|
19016
|
-
sha256:
|
|
19512
|
+
sha256: createHash26("sha256").update(content).digest("hex"),
|
|
19017
19513
|
frontmatter,
|
|
19018
19514
|
content
|
|
19019
19515
|
}
|
|
@@ -19032,10 +19528,10 @@ function readSkillFrontmatter(content, artifact) {
|
|
|
19032
19528
|
}
|
|
19033
19529
|
|
|
19034
19530
|
// src/lifecycle/fleet-normalize.ts
|
|
19035
|
-
import { createHash as
|
|
19036
|
-
import { readFile as readFile44, readdir as
|
|
19531
|
+
import { createHash as createHash27 } from "crypto";
|
|
19532
|
+
import { readFile as readFile44, readdir as readdir16, rm as rm16 } from "fs/promises";
|
|
19037
19533
|
import { homedir as homedir17 } from "os";
|
|
19038
|
-
import { dirname as dirname36, isAbsolute as isAbsolute13, join as
|
|
19534
|
+
import { dirname as dirname36, isAbsolute as isAbsolute13, join as join60, relative as relative18, resolve as resolve43 } from "path";
|
|
19039
19535
|
init_manifest2();
|
|
19040
19536
|
init_paths();
|
|
19041
19537
|
init_merge_removal();
|
|
@@ -19071,14 +19567,14 @@ async function planFleetNormalization(request) {
|
|
|
19071
19567
|
if (legacySelfNormalization && normalizedRequest.orphanedOwnerRoots?.length && normalizedRequest.packages?.includes(name)) {
|
|
19072
19568
|
packages.push({
|
|
19073
19569
|
name,
|
|
19074
|
-
declarationDigest: sha2567(
|
|
19570
|
+
declarationDigest: sha2567(canonicalJson8({ name, evidence: "orphaned-installed-state" }))
|
|
19075
19571
|
});
|
|
19076
19572
|
continue;
|
|
19077
19573
|
}
|
|
19078
19574
|
throw new Error(`Package '${name}' must be an existing duplicate in both source and destination fleets.`);
|
|
19079
19575
|
}
|
|
19080
|
-
const sourceDeclaration =
|
|
19081
|
-
const destinationDeclaration =
|
|
19576
|
+
const sourceDeclaration = canonicalJson8(sourcePackage);
|
|
19577
|
+
const destinationDeclaration = canonicalJson8(destinationPackage);
|
|
19082
19578
|
if (sourceDeclaration !== destinationDeclaration) {
|
|
19083
19579
|
throw new Error(
|
|
19084
19580
|
`Package '${name}' has divergent declarations. Source, driver, adapter, adapter configuration, installation type, version policy, runtime restrictions, selections, aliases, overrides, and suggestions must match before normalization.`
|
|
@@ -19115,7 +19611,7 @@ async function planFleetNormalization(request) {
|
|
|
19115
19611
|
packages,
|
|
19116
19612
|
installedState
|
|
19117
19613
|
};
|
|
19118
|
-
return { ...planWithoutDigest, planDigest: sha2567(
|
|
19614
|
+
return { ...planWithoutDigest, planDigest: sha2567(canonicalJson8(planWithoutDigest)) };
|
|
19119
19615
|
}
|
|
19120
19616
|
async function applyFleetNormalization(request, hooks = {}) {
|
|
19121
19617
|
if (!request.apply) throw new Error("Fleet normalization is dry-run by default; pass --apply with --plan-digest <digest>.");
|
|
@@ -19253,8 +19749,8 @@ async function assertNormalizationPostconditions(journal) {
|
|
|
19253
19749
|
}
|
|
19254
19750
|
for (const graphLock of journal.graphLocks) {
|
|
19255
19751
|
const raw = await readOptionalRecord(graphLock.path);
|
|
19256
|
-
const
|
|
19257
|
-
if (
|
|
19752
|
+
const digest3 = raw ? sha2567(canonicalJson8(await readGraphLock(graphLock.path))) : null;
|
|
19753
|
+
if (digest3 !== graphLock.afterDigest) {
|
|
19258
19754
|
throw new Error(`Fleet normalization graph-lock postcheck failed: ${graphLock.path}`);
|
|
19259
19755
|
}
|
|
19260
19756
|
}
|
|
@@ -19325,17 +19821,17 @@ function normalizeRequest3(request) {
|
|
|
19325
19821
|
...profile ? { profile } : {},
|
|
19326
19822
|
...agent ? { agent } : {},
|
|
19327
19823
|
...orphanedOwnerRoots ? { orphanedOwnerRoots } : {},
|
|
19328
|
-
...request.globalRoot ? { globalRoot:
|
|
19824
|
+
...request.globalRoot ? { globalRoot: resolve43(request.globalRoot) } : {}
|
|
19329
19825
|
};
|
|
19330
19826
|
}
|
|
19331
19827
|
function normalizeOrphanedOwnerRoot(value) {
|
|
19332
19828
|
const root = value.trim();
|
|
19333
19829
|
if (!root || !isAbsolute13(root)) throw new Error(`Invalid orphaned owner root '${value}'; expected an absolute workspace path.`);
|
|
19334
|
-
return
|
|
19830
|
+
return resolve43(root);
|
|
19335
19831
|
}
|
|
19336
19832
|
async function normalizationRoots(request) {
|
|
19337
19833
|
const destination = await showRegisteredFleet(request.destinationFleet, { globalRoot: request.globalRoot });
|
|
19338
|
-
const source = request.from === "user" ?
|
|
19834
|
+
const source = request.from === "user" ? resolve43(request.globalRoot ?? homedir17()) : (await showRegisteredFleet(request.from.slice("fleet:".length), { globalRoot: request.globalRoot })).root;
|
|
19339
19835
|
return { source, destination: destination.root };
|
|
19340
19836
|
}
|
|
19341
19837
|
async function withOrderedLocks(roots, fn) {
|
|
@@ -19343,7 +19839,7 @@ async function withOrderedLocks(roots, fn) {
|
|
|
19343
19839
|
if (index === roots.length) return fn();
|
|
19344
19840
|
const root = roots[index];
|
|
19345
19841
|
return withFilesystemLock(
|
|
19346
|
-
|
|
19842
|
+
join60(root, ".agentwheel", "fleet-normalize.lock"),
|
|
19347
19843
|
5e3,
|
|
19348
19844
|
() => run(index + 1),
|
|
19349
19845
|
"fleet normalization"
|
|
@@ -19442,10 +19938,10 @@ async function inspectInstalledState(source, destination, packageNames) {
|
|
|
19442
19938
|
const destinationGraphAfter = destinationGraph?.lock ?? destinationGraphFromSource(sourceGraph.lock, destinationState.targetFingerprint);
|
|
19443
19939
|
graphTransfers.push({
|
|
19444
19940
|
sourceGraphLockPath: sourceGraph.path,
|
|
19445
|
-
sourceGraphLockDigest: sha2567(
|
|
19941
|
+
sourceGraphLockDigest: sha2567(canonicalJson8(sourceGraph.lock)),
|
|
19446
19942
|
destinationGraphLockPath: destinationState.graphLockPath,
|
|
19447
|
-
destinationGraphLockDigest: destinationGraph ? sha2567(
|
|
19448
|
-
destinationGraphLockAfterDigest: sha2567(
|
|
19943
|
+
destinationGraphLockDigest: destinationGraph ? sha2567(canonicalJson8(destinationGraph.lock)) : null,
|
|
19944
|
+
destinationGraphLockAfterDigest: sha2567(canonicalJson8(destinationGraphAfter)),
|
|
19449
19945
|
targetKey: sourceGraph.targetKey,
|
|
19450
19946
|
adapter: destinationState.adapter,
|
|
19451
19947
|
targetFingerprint: destinationState.targetFingerprint
|
|
@@ -19550,7 +20046,7 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
19550
20046
|
let normalizedGraphCount = 0;
|
|
19551
20047
|
for (const graph of graphCandidates) {
|
|
19552
20048
|
const destinationState = await targetStateForGraph(fleet, graph, fleet.fleetId, allowOrphanedRetiredPackages);
|
|
19553
|
-
if (
|
|
20049
|
+
if (resolve43(destinationState.graphLockPath) === resolve43(graph.path)) {
|
|
19554
20050
|
const destinationManifest = (await collectManifestPaths([destinationState.manifestPath]))[0];
|
|
19555
20051
|
if (destinationManifest?.manifest.entries.some((entry) => entry.workspaceOwner === fleetOwner && entryMatchesPackages(entry, selected))) {
|
|
19556
20052
|
assertCorrelatedGraphManifest(
|
|
@@ -19565,7 +20061,7 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
19565
20061
|
}
|
|
19566
20062
|
}
|
|
19567
20063
|
const legacyState = await legacyTargetStateForGraph(fleet, graph, allowOrphanedRetiredPackages);
|
|
19568
|
-
if (
|
|
20064
|
+
if (resolve43(legacyState.graphLockPath) !== resolve43(graph.path)) {
|
|
19569
20065
|
throw new Error(`Legacy graph lock target identity is stale or noncanonical: ${graph.path}`);
|
|
19570
20066
|
}
|
|
19571
20067
|
const legacyManifest = (await collectManifestPaths([legacyState.manifestPath]))[0];
|
|
@@ -19673,7 +20169,7 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
19673
20169
|
)
|
|
19674
20170
|
});
|
|
19675
20171
|
if (!selectedArtifacts) {
|
|
19676
|
-
const fullDigest = sha2567(
|
|
20172
|
+
const fullDigest = sha2567(canonicalJson8(graph.lock));
|
|
19677
20173
|
const existingDestinationGraph = graphCandidates.find((candidate) => candidate.path === destinationState.graphLockPath);
|
|
19678
20174
|
if (existingDestinationGraph) assertGraphIsSubset(graph, existingDestinationGraph);
|
|
19679
20175
|
const destinationGraph = destinationGraphFromSource(graph.lock, destinationState.targetFingerprint);
|
|
@@ -19681,8 +20177,8 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
19681
20177
|
sourceGraphLockPath: graph.path,
|
|
19682
20178
|
sourceGraphLockDigest: fullDigest,
|
|
19683
20179
|
destinationGraphLockPath: destinationState.graphLockPath,
|
|
19684
|
-
destinationGraphLockDigest: existingDestinationGraph ? sha2567(
|
|
19685
|
-
destinationGraphLockAfterDigest: sha2567(
|
|
20180
|
+
destinationGraphLockDigest: existingDestinationGraph ? sha2567(canonicalJson8(existingDestinationGraph.lock)) : null,
|
|
20181
|
+
destinationGraphLockAfterDigest: sha2567(canonicalJson8(destinationGraph)),
|
|
19686
20182
|
targetKey: graph.targetKey,
|
|
19687
20183
|
adapter: graph.adapter,
|
|
19688
20184
|
targetFingerprint: destinationState.targetFingerprint
|
|
@@ -19740,7 +20236,7 @@ function targetKeysForAgent(scope, name) {
|
|
|
19740
20236
|
async function runtimeRoots(scope) {
|
|
19741
20237
|
const roots = /* @__PURE__ */ new Set([scope.root]);
|
|
19742
20238
|
if (scope.config.packages.some((pkg) => pkg.installationType === "user") || Object.values(scope.config.agents).some((agent) => agent.installationType === "user")) {
|
|
19743
|
-
roots.add(
|
|
20239
|
+
roots.add(resolve43(process.env.AGENTWHEEL_TEST_HOME || process.env.HOME || homedir17()));
|
|
19744
20240
|
}
|
|
19745
20241
|
for (const [name, agent] of Object.entries(scope.config.agents)) {
|
|
19746
20242
|
if (agent.transport === "ssh" || agent.transport === "local-run-as") throw new Error(`Installed-state normalization cannot inspect ${agent.transport} agent '${name}' locally.`);
|
|
@@ -19758,17 +20254,17 @@ async function runtimeRoots(scope) {
|
|
|
19758
20254
|
async function collectManifests(roots) {
|
|
19759
20255
|
const found = /* @__PURE__ */ new Map();
|
|
19760
20256
|
for (const root of roots) {
|
|
19761
|
-
const dir =
|
|
20257
|
+
const dir = join60(root, ".agentwheel");
|
|
19762
20258
|
let names;
|
|
19763
20259
|
try {
|
|
19764
|
-
names = await
|
|
20260
|
+
names = await readdir16(dir);
|
|
19765
20261
|
} catch (error) {
|
|
19766
20262
|
if (isMissing5(error)) continue;
|
|
19767
20263
|
throw error;
|
|
19768
20264
|
}
|
|
19769
20265
|
for (const name of names.sort((a, b) => a.localeCompare(b))) {
|
|
19770
20266
|
if (!name.endsWith(".install-manifest.json")) continue;
|
|
19771
|
-
const path =
|
|
20267
|
+
const path = join60(dir, name);
|
|
19772
20268
|
const raw = JSON.parse(await readFile44(path, "utf8"));
|
|
19773
20269
|
const parsed = installManifestSchema.parse(raw);
|
|
19774
20270
|
if (parsed.version !== 2) {
|
|
@@ -19777,14 +20273,14 @@ async function collectManifests(roots) {
|
|
|
19777
20273
|
}
|
|
19778
20274
|
continue;
|
|
19779
20275
|
}
|
|
19780
|
-
if (
|
|
20276
|
+
if (resolve43(parsed.targetRoot) !== resolve43(root)) {
|
|
19781
20277
|
throw new Error(`Install manifest target root does not match its enumerated runtime root: ${path}`);
|
|
19782
20278
|
}
|
|
19783
20279
|
const expectedPath = installManifestPath(parsed.targetRoot, parsed.adapter, {
|
|
19784
20280
|
installationType: parsed.installationType,
|
|
19785
20281
|
stateKey: parsed.stateKey
|
|
19786
20282
|
});
|
|
19787
|
-
if (
|
|
20283
|
+
if (resolve43(expectedPath) !== resolve43(path)) {
|
|
19788
20284
|
throw new Error(`Install manifest state identity does not match its canonical path: ${path}`);
|
|
19789
20285
|
}
|
|
19790
20286
|
found.set(path, { path, raw, manifest: { ...parsed, revision: computeManifestRevision(raw), legacy: false } });
|
|
@@ -19805,7 +20301,7 @@ async function collectManifestPaths(paths) {
|
|
|
19805
20301
|
installationType: parsed.installationType,
|
|
19806
20302
|
stateKey: parsed.stateKey
|
|
19807
20303
|
});
|
|
19808
|
-
if (
|
|
20304
|
+
if (resolve43(expectedPath) !== resolve43(path)) {
|
|
19809
20305
|
throw new Error(`Install manifest state identity does not match its canonical path: ${path}`);
|
|
19810
20306
|
}
|
|
19811
20307
|
found.push({ path, raw, manifest: { ...parsed, revision: computeManifestRevision(raw), legacy: false } });
|
|
@@ -19824,14 +20320,14 @@ function assertNoForeignSamePathOwnership(manifests, renderedPaths, allowedOwner
|
|
|
19824
20320
|
}
|
|
19825
20321
|
}
|
|
19826
20322
|
async function relevantGraphLocks(workspaceRoot, selected, targetKeys, include) {
|
|
19827
|
-
const root =
|
|
20323
|
+
const root = join60(workspaceRoot, ".agentwheel", "locks");
|
|
19828
20324
|
if (!await pathExists(root)) return [];
|
|
19829
20325
|
const results = [];
|
|
19830
20326
|
for (const path of await listFiles(root)) {
|
|
19831
20327
|
if (!path.endsWith(".graph-lock.json")) continue;
|
|
19832
20328
|
const lock = await readGraphLock(path);
|
|
19833
20329
|
if (!lock.canonical.roots.some((candidate2) => selected.has(candidate2.rootId))) continue;
|
|
19834
|
-
const parts =
|
|
20330
|
+
const parts = relative18(root, path).split(/[\\/]/);
|
|
19835
20331
|
if (parts.length !== 3) throw new Error(`Graph lock path is not canonical: ${path}`);
|
|
19836
20332
|
const [targetKey2, adapter] = parts;
|
|
19837
20333
|
if (targetKeys && !targetKeys.has(targetKey2)) continue;
|
|
@@ -19848,7 +20344,7 @@ async function relevantGraphLocks(workspaceRoot, selected, targetKeys, include)
|
|
|
19848
20344
|
const projection = relevantGraphProjection(lock, selected);
|
|
19849
20345
|
results.push({
|
|
19850
20346
|
...candidate,
|
|
19851
|
-
digest: sha2567(
|
|
20347
|
+
digest: sha2567(canonicalJson8(projection)),
|
|
19852
20348
|
artifactIdentities: lock.canonical.artifacts.filter((artifact) => artifact.owners.some((owner) => ownerMatchesGraphPackage(lock, owner, selected))).map(graphArtifactIdentity).sort()
|
|
19853
20349
|
});
|
|
19854
20350
|
}
|
|
@@ -19873,7 +20369,7 @@ async function hasRelevantLegacyManifestCandidate(scope, graph, selected) {
|
|
|
19873
20369
|
const legacyManifest = (await collectManifestPaths([legacyState.manifestPath]))[0];
|
|
19874
20370
|
if (legacyManifest?.manifest.entries.some((entry) => entryMatchesGraphPackages(entry, graph.lock, selected))) return true;
|
|
19875
20371
|
const stableState = await targetStateForGraph(scope, graph, scope.fleetId ?? null);
|
|
19876
|
-
if (
|
|
20372
|
+
if (resolve43(stableState.graphLockPath) !== resolve43(graph.path)) return false;
|
|
19877
20373
|
const stableManifest = (await collectManifestPaths([stableState.manifestPath]))[0];
|
|
19878
20374
|
return stableManifest?.manifest.entries.some((entry) => entryMatchesGraphPackages(entry, graph.lock, selected)) ?? false;
|
|
19879
20375
|
}
|
|
@@ -19894,7 +20390,7 @@ async function legacyManifestPathForConfiguredTarget(scope, graph) {
|
|
|
19894
20390
|
baseDir: scope.root
|
|
19895
20391
|
});
|
|
19896
20392
|
const targetRoot = resolveConfigPath(agent.root, scope.root);
|
|
19897
|
-
const installRoot =
|
|
20393
|
+
const installRoot = resolve43(installRootForAdapterInstallationType(adapter, targetRoot, agent.installationType, { kind: "local" }));
|
|
19898
20394
|
const stateKey = stateKeyFor(adapter.name, {
|
|
19899
20395
|
installationType: agent.installationType,
|
|
19900
20396
|
stateKey: agent.stateKey,
|
|
@@ -19962,7 +20458,7 @@ async function targetStateForGraph(scope, graph, identityFleetId = scope.fleetId
|
|
|
19962
20458
|
ssh: void 0,
|
|
19963
20459
|
...agent?.stateKey ? { stateKey: agent.stateKey } : {}
|
|
19964
20460
|
};
|
|
19965
|
-
const installRoot =
|
|
20461
|
+
const installRoot = resolve43(installRootForAdapterInstallationType(adapter, targetRoot, installationType, { kind: "local" }));
|
|
19966
20462
|
const targetIdentity = resolveTargetStateIdentity({
|
|
19967
20463
|
targetFingerprintParts: fingerprintParts,
|
|
19968
20464
|
workspaceRoot: scope.root,
|
|
@@ -19994,16 +20490,16 @@ async function targetStateForGraph(scope, graph, identityFleetId = scope.fleetId
|
|
|
19994
20490
|
async function installedTargetStateForGraph(scope, graph, manifests, owners, selected, identityFleetId = scope.fleetId ?? null) {
|
|
19995
20491
|
const stable = await targetStateForGraph(scope, graph, identityFleetId);
|
|
19996
20492
|
let state;
|
|
19997
|
-
if (
|
|
20493
|
+
if (resolve43(graph.path) === resolve43(stable.graphLockPath)) {
|
|
19998
20494
|
state = stable;
|
|
19999
20495
|
} else {
|
|
20000
20496
|
const released = await releasedTargetStateForGraph(scope, graph, identityFleetId);
|
|
20001
|
-
if (
|
|
20497
|
+
if (resolve43(graph.path) !== resolve43(released.graphLockPath)) {
|
|
20002
20498
|
throw new Error(`Graph lock target identity is stale or noncanonical: ${graph.path}`);
|
|
20003
20499
|
}
|
|
20004
20500
|
state = released;
|
|
20005
20501
|
}
|
|
20006
|
-
const manifest = manifests.find((candidate) =>
|
|
20502
|
+
const manifest = manifests.find((candidate) => resolve43(candidate.path) === resolve43(state.manifestPath));
|
|
20007
20503
|
if (!manifest) {
|
|
20008
20504
|
throw new Error(`Graph lock is not covered by its canonical install manifest: ${state.manifestPath}`);
|
|
20009
20505
|
}
|
|
@@ -20027,7 +20523,7 @@ async function releasedTargetStateForGraph(scope, graph, identityFleetId = scope
|
|
|
20027
20523
|
...current,
|
|
20028
20524
|
stateKey,
|
|
20029
20525
|
targetFingerprint,
|
|
20030
|
-
graphLockPath:
|
|
20526
|
+
graphLockPath: join60(dirname36(graph.path), `${targetFingerprint}.graph-lock.json`),
|
|
20031
20527
|
manifestPath: installManifestPath(current.installRoot, current.adapter, {
|
|
20032
20528
|
installationType: current.installationType,
|
|
20033
20529
|
stateKey
|
|
@@ -20035,7 +20531,7 @@ async function releasedTargetStateForGraph(scope, graph, identityFleetId = scope
|
|
|
20035
20531
|
};
|
|
20036
20532
|
}
|
|
20037
20533
|
function assertCorrelatedGraphManifest(graph, state, manifest, owners, selected) {
|
|
20038
|
-
if (manifest.manifest.adapter !== state.adapter || manifest.manifest.installationType !== state.installationType || manifest.manifest.stateKey !== state.stateKey ||
|
|
20534
|
+
if (manifest.manifest.adapter !== state.adapter || manifest.manifest.installationType !== state.installationType || manifest.manifest.stateKey !== state.stateKey || resolve43(manifest.manifest.targetRoot) !== resolve43(state.installRoot)) {
|
|
20039
20535
|
throw new Error(`Install manifest identity does not match graph target state: ${manifest.path}`);
|
|
20040
20536
|
}
|
|
20041
20537
|
const entries = manifest.manifest.entries.filter((entry) => entryMatchesGraphPackages(entry, graph.lock, selected));
|
|
@@ -20043,7 +20539,7 @@ function assertCorrelatedGraphManifest(graph, state, manifest, owners, selected)
|
|
|
20043
20539
|
throw new Error(`Graph lock is not covered by selected install-manifest contributions: ${graph.path}`);
|
|
20044
20540
|
}
|
|
20045
20541
|
const graphArtifacts = new Set(graph.artifactIdentities);
|
|
20046
|
-
const graphLockDigest = sha2567(
|
|
20542
|
+
const graphLockDigest = sha2567(canonicalJson8(graph.lock));
|
|
20047
20543
|
for (const entry of entries) {
|
|
20048
20544
|
if (!owners.has(entry.workspaceOwner)) {
|
|
20049
20545
|
throw new Error(`Graph lock manifest owner mismatch at ${manifest.path}: ${entry.workspaceOwner}.`);
|
|
@@ -20060,9 +20556,9 @@ async function legacyTargetStateForGraph(scope, graph, allowMissingNamedAgentPac
|
|
|
20060
20556
|
return releasedTargetStateForGraph(scope, graph, null, allowMissingNamedAgentPackages);
|
|
20061
20557
|
}
|
|
20062
20558
|
function assertGraphIsSubset(source, destination) {
|
|
20063
|
-
const sourceRoots = new Map(source.lock.canonical.roots.map((root) => [root.rootId,
|
|
20559
|
+
const sourceRoots = new Map(source.lock.canonical.roots.map((root) => [root.rootId, canonicalJson8(root)]));
|
|
20064
20560
|
for (const root of destination.lock.canonical.roots) {
|
|
20065
|
-
if (sourceRoots.get(root.rootId) !==
|
|
20561
|
+
if (sourceRoots.get(root.rootId) !== canonicalJson8(root)) {
|
|
20066
20562
|
throw new Error(
|
|
20067
20563
|
`Partially normalized destination graph diverges at root '${root.rootId}': ${destination.path}`
|
|
20068
20564
|
);
|
|
@@ -20092,7 +20588,7 @@ function destinationGraphFromSource(source, targetFingerprint) {
|
|
|
20092
20588
|
function mergeDestinationManifest(existing, state, generatedAt, desiredEntries, replaceOwners = /* @__PURE__ */ new Set()) {
|
|
20093
20589
|
if (existing) {
|
|
20094
20590
|
const manifest2 = existing.manifest;
|
|
20095
|
-
if (manifest2.adapter !== state.adapter || manifest2.installationType !== state.installationType || manifest2.stateKey !== state.stateKey ||
|
|
20591
|
+
if (manifest2.adapter !== state.adapter || manifest2.installationType !== state.installationType || manifest2.stateKey !== state.stateKey || resolve43(manifest2.targetRoot) !== state.installRoot) {
|
|
20096
20592
|
throw new Error(`Destination manifest identity diverges from the derived target: ${existing.path}`);
|
|
20097
20593
|
}
|
|
20098
20594
|
}
|
|
@@ -20152,7 +20648,7 @@ async function assertEquivalentRuntimeState(entry, sourcePath, destinationPath)
|
|
|
20152
20648
|
const content = await readFile44(path, "utf8");
|
|
20153
20649
|
if (hasMergeRemovalContent(entry.mergeRemoval)) {
|
|
20154
20650
|
assertExactMergeContribution(entry.mergeRemoval, entry.mergeStrategy, content);
|
|
20155
|
-
} else if (
|
|
20651
|
+
} else if (resolve43(sourcePath) === resolve43(destinationPath) && entry.mergeStrategy !== "codex-toml-mcp") {
|
|
20156
20652
|
assertExactMergeContribution({}, entry.mergeStrategy, content);
|
|
20157
20653
|
} else {
|
|
20158
20654
|
throw new Error(`Installed-state normalization cannot prove incomplete merge ownership at ${path}.`);
|
|
@@ -20163,8 +20659,8 @@ async function assertEquivalentRuntimeState(entry, sourcePath, destinationPath)
|
|
|
20163
20659
|
await assertEquivalentRuntimeBytes(sourcePath, destinationPath, entry.hash);
|
|
20164
20660
|
}
|
|
20165
20661
|
function renderedEntryPathForRoot(root, entryPath) {
|
|
20166
|
-
const normalizedRoot =
|
|
20167
|
-
const candidate =
|
|
20662
|
+
const normalizedRoot = resolve43(root);
|
|
20663
|
+
const candidate = resolve43(normalizedRoot, entryPath);
|
|
20168
20664
|
if (candidate !== normalizedRoot && !candidate.startsWith(`${normalizedRoot}/`)) {
|
|
20169
20665
|
throw new Error(`Install manifest entry escapes its target root: ${entryPath}`);
|
|
20170
20666
|
}
|
|
@@ -20255,8 +20751,8 @@ function ownerMatchesGraphPackage(lock, owner, selected) {
|
|
|
20255
20751
|
return ownerMatchesPackage(owner, selected) || lock.canonical.roots.some((root) => selected.has(root.rootId) && root.graphNodeId === owner);
|
|
20256
20752
|
}
|
|
20257
20753
|
function renderedEntryPath(manifest, entry) {
|
|
20258
|
-
const root =
|
|
20259
|
-
const candidate =
|
|
20754
|
+
const root = resolve43(manifest.targetRoot);
|
|
20755
|
+
const candidate = resolve43(root, entry.path);
|
|
20260
20756
|
if (candidate !== root && !candidate.startsWith(`${root}/`)) {
|
|
20261
20757
|
throw new Error(`Install manifest entry escapes its target root: ${entry.path}`);
|
|
20262
20758
|
}
|
|
@@ -20275,7 +20771,7 @@ function assertSimpleVerifiableEntry(entry, path) {
|
|
|
20275
20771
|
if (entry.kind !== "file" && entry.kind !== "dir") throw new Error(`Unsupported installed entry kind at ${path}.`);
|
|
20276
20772
|
}
|
|
20277
20773
|
function entryIdentity(entry) {
|
|
20278
|
-
return
|
|
20774
|
+
return canonicalJson8({
|
|
20279
20775
|
path: entry.path,
|
|
20280
20776
|
artifactType: entry.artifactType,
|
|
20281
20777
|
artifactName: entry.artifactName,
|
|
@@ -20293,7 +20789,7 @@ function entryIdentity(entry) {
|
|
|
20293
20789
|
});
|
|
20294
20790
|
}
|
|
20295
20791
|
function graphArtifactIdentity(artifact) {
|
|
20296
|
-
return
|
|
20792
|
+
return canonicalJson8({
|
|
20297
20793
|
graphNodeId: artifact.graphNodeId,
|
|
20298
20794
|
type: artifact.type,
|
|
20299
20795
|
name: artifact.name,
|
|
@@ -20307,7 +20803,7 @@ function graphArtifactIdentity(artifact) {
|
|
|
20307
20803
|
});
|
|
20308
20804
|
}
|
|
20309
20805
|
function graphEntryIdentity(entry) {
|
|
20310
|
-
return
|
|
20806
|
+
return canonicalJson8({
|
|
20311
20807
|
graphNodeId: entry.graphNodeId,
|
|
20312
20808
|
type: entry.artifactType,
|
|
20313
20809
|
name: entry.artifactName,
|
|
@@ -20411,7 +20907,7 @@ async function buildJournalGraphLockStates(plan) {
|
|
|
20411
20907
|
for (const transfer of plan.installedState.graphTransfers) {
|
|
20412
20908
|
const sourceBefore = JSON.parse(await readFile44(transfer.sourceGraphLockPath, "utf8"));
|
|
20413
20909
|
const sourceLock = await readGraphLock(transfer.sourceGraphLockPath);
|
|
20414
|
-
const sourceDigest = sha2567(
|
|
20910
|
+
const sourceDigest = sha2567(canonicalJson8(sourceLock));
|
|
20415
20911
|
if (sourceDigest !== transfer.sourceGraphLockDigest) {
|
|
20416
20912
|
throw new Error(`Source graph lock changed after planning: ${transfer.sourceGraphLockPath}`);
|
|
20417
20913
|
}
|
|
@@ -20429,9 +20925,9 @@ async function buildJournalGraphLockStates(plan) {
|
|
|
20429
20925
|
continue;
|
|
20430
20926
|
}
|
|
20431
20927
|
const destinationBefore = await readOptionalRecord(transfer.destinationGraphLockPath);
|
|
20432
|
-
const destinationBeforeDigest = destinationBefore ? sha2567(
|
|
20928
|
+
const destinationBeforeDigest = destinationBefore ? sha2567(canonicalJson8(await readGraphLock(transfer.destinationGraphLockPath))) : null;
|
|
20433
20929
|
const destinationAfter = destinationBefore ? await readGraphLock(transfer.destinationGraphLockPath) : destinationGraphFromSource(sourceLock, transfer.targetFingerprint);
|
|
20434
|
-
const destinationAfterDigest = sha2567(
|
|
20930
|
+
const destinationAfterDigest = sha2567(canonicalJson8(destinationAfter));
|
|
20435
20931
|
if (destinationBeforeDigest !== transfer.destinationGraphLockDigest || destinationAfterDigest !== transfer.destinationGraphLockAfterDigest) {
|
|
20436
20932
|
throw new Error(`Destination graph lock changed after planning: ${transfer.destinationGraphLockPath}`);
|
|
20437
20933
|
}
|
|
@@ -20541,8 +21037,8 @@ async function assertJournalRecoverable(journal) {
|
|
|
20541
21037
|
}
|
|
20542
21038
|
for (const graphLock of journal.graphLocks) {
|
|
20543
21039
|
const raw = await readOptionalRecord(graphLock.path);
|
|
20544
|
-
const
|
|
20545
|
-
if (
|
|
21040
|
+
const digest3 = raw ? sha2567(canonicalJson8(await readGraphLock(graphLock.path))) : null;
|
|
21041
|
+
if (digest3 !== graphLock.beforeDigest && digest3 !== graphLock.afterDigest) {
|
|
20546
21042
|
throw new Error(`Cannot recover fleet normalization because graph lock changed outside the transaction: ${graphLock.path}`);
|
|
20547
21043
|
}
|
|
20548
21044
|
}
|
|
@@ -20594,12 +21090,12 @@ function packageMap(config) {
|
|
|
20594
21090
|
return result;
|
|
20595
21091
|
}
|
|
20596
21092
|
function configRevision(config) {
|
|
20597
|
-
return sha2567(
|
|
21093
|
+
return sha2567(canonicalJson8(config));
|
|
20598
21094
|
}
|
|
20599
21095
|
function normalizationJournalPath(sourceRoot, destinationFleet) {
|
|
20600
|
-
return
|
|
21096
|
+
return join60(sourceRoot, ".agentwheel", `fleet-normalize-${destinationFleet}.journal.json`);
|
|
20601
21097
|
}
|
|
20602
|
-
function
|
|
21098
|
+
function canonicalJson8(value) {
|
|
20603
21099
|
return JSON.stringify(sortValue(value));
|
|
20604
21100
|
}
|
|
20605
21101
|
function sortValue(value) {
|
|
@@ -20610,7 +21106,7 @@ function sortValue(value) {
|
|
|
20610
21106
|
return value;
|
|
20611
21107
|
}
|
|
20612
21108
|
function sha2567(value) {
|
|
20613
|
-
return
|
|
21109
|
+
return createHash27("sha256").update(value).digest("hex");
|
|
20614
21110
|
}
|
|
20615
21111
|
function assertDigest(value) {
|
|
20616
21112
|
if (!/^[a-f0-9]{64}$/.test(value)) throw new Error(`Invalid plan digest: ${value}`);
|
|
@@ -20655,6 +21151,7 @@ var commandGovernance = {
|
|
|
20655
21151
|
"ownership handoff": "governed-unless-dry-run",
|
|
20656
21152
|
"ownership recovery-plan": "incidental-cache",
|
|
20657
21153
|
"ownership retire-stale": "governed-apply",
|
|
21154
|
+
"ownership release-claim": "governed-apply",
|
|
20658
21155
|
"ownership adopt-legacy": "governed-apply",
|
|
20659
21156
|
"mcp retire": "governed-apply",
|
|
20660
21157
|
eject: "governed-always",
|
|
@@ -20761,7 +21258,7 @@ fleetCommand.command("normalize").description("plan or apply duplicate desired-s
|
|
|
20761
21258
|
});
|
|
20762
21259
|
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) => {
|
|
20763
21260
|
const targetRoot = await workspaceContextRootFromOptions(normalizeRuntimeScopeOptions(options));
|
|
20764
|
-
const cacheRoot =
|
|
21261
|
+
const cacheRoot = join61(targetRoot, ".agentwheel", "cache");
|
|
20765
21262
|
const result = await pruneGitCache(cacheRoot, { keepSnapshots: options.keep, dryRun: !options.apply });
|
|
20766
21263
|
const verb = options.apply ? "Removed" : "Would remove";
|
|
20767
21264
|
for (const path of result.removedPaths) console.log(`${verb} ${path}`);
|
|
@@ -20779,7 +21276,7 @@ program.command("list").description("list artifacts exposed by a package source"
|
|
|
20779
21276
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
20780
21277
|
const selectedArtifacts = selectedArtifactsFromOptionsOrRegistry(options, resolvedInput.registryEntry);
|
|
20781
21278
|
const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
|
|
20782
|
-
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot:
|
|
21279
|
+
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join61(targetRoot, ".agentwheel", "cache") }))));
|
|
20783
21280
|
try {
|
|
20784
21281
|
const artifacts = filterArtifactsBySelection(await driver.list(resolved), selectedArtifacts);
|
|
20785
21282
|
for (const artifact of artifacts) {
|
|
@@ -20848,7 +21345,7 @@ program.command("try").description("read and validate one skill for the current
|
|
|
20848
21345
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
20849
21346
|
const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
|
|
20850
21347
|
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, {
|
|
20851
|
-
cacheRoot:
|
|
21348
|
+
cacheRoot: join61(targetRoot, ".agentwheel", "cache")
|
|
20852
21349
|
}))));
|
|
20853
21350
|
try {
|
|
20854
21351
|
const trial = await createSkillTrial(driver, resolved, selectedArtifactsFromOptionsOrRegistry(options, resolvedInput.registryEntry));
|
|
@@ -20870,7 +21367,7 @@ program.command("scan").description("scan a package source for validation findin
|
|
|
20870
21367
|
const targetRoot = await workspaceContextRootFromOptions(normalizeRuntimeScopeOptions(options));
|
|
20871
21368
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
20872
21369
|
const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
|
|
20873
|
-
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot:
|
|
21370
|
+
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join61(targetRoot, ".agentwheel", "cache") }))));
|
|
20874
21371
|
try {
|
|
20875
21372
|
const result = await driver.scan(resolved);
|
|
20876
21373
|
if (result.findings.length === 0) {
|
|
@@ -20885,10 +21382,10 @@ program.command("scan").description("scan a package source for validation findin
|
|
|
20885
21382
|
await releaseGitSnapshotLease(resolved.cacheLeasePath);
|
|
20886
21383
|
}
|
|
20887
21384
|
});
|
|
20888
|
-
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) => {
|
|
21385
|
+
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("--adopt", "adopt unmanaged destinations; managed drift and foreign ownership remain protected", 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) => {
|
|
20889
21386
|
await runInstallCommand(source, { ...options, dryRun: true }, { apply: false });
|
|
20890
21387
|
});
|
|
20891
|
-
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) => {
|
|
21388
|
+
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("--adopt", "adopt unmanaged destinations; managed drift and foreign ownership remain protected", 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) => {
|
|
20892
21389
|
await runInstallCommand(source, options, { apply: !options.dryRun });
|
|
20893
21390
|
});
|
|
20894
21391
|
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) => {
|
|
@@ -21051,6 +21548,48 @@ program.command("remember").description("append text to the local instructions o
|
|
|
21051
21548
|
console.log(nextInstallNudge());
|
|
21052
21549
|
});
|
|
21053
21550
|
var ownershipCommand = program.command("ownership").description("inspect and transfer manifest ownership without rewriting runtime artifacts");
|
|
21551
|
+
ownershipCommand.command("release-claim").description("release one exact plain ownership claim while preserving runtime bytes and package declarations").argument("<selector>", "exact artifact selector in type/name form").requiredOption("--path <relative-path>", "exact owned runtime path from the install manifest").requiredOption("--state-key <key>", "exact source install-manifest state key").requiredOption("--from-workspace-root <path>", "exact current workspace owner root").option("--from-fleet-id <id>", "qualify the recorded owner with its Fleet id").option("--fleet <id>", "governing registered Fleet workspace").option("--user", "use the user workspace", false).option("--local", "use the nearest local workspace", false).option("--adapter <adapter>", "built-in adapter").option("--adapter-config <path>", "adapter JSON/JSONC file").option("-i, --installation-type <type>", "exact installation type").option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "configured agent resolving to one local target").option("--profile <name>", "profile resolving to one local 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-inventory-revision <sha256>", "reviewed manifest inventory revision; required with --apply").option("--apply", "apply the exact reviewed metadata-only release", false).option("--json", "print the complete plan or result as JSON", false).action(async (selector, _options, command) => {
|
|
21552
|
+
const options = command.optsWithGlobals();
|
|
21553
|
+
if (!options.reason) throw new Error("Ownership release requires --reason <why> for planning and applying.");
|
|
21554
|
+
const normalized = normalizeRuntimeScopeOptions(options);
|
|
21555
|
+
const targets = await resolveCliTargets(normalized);
|
|
21556
|
+
if (targets.length !== 1) throw new Error("Ownership release requires exactly one target.");
|
|
21557
|
+
const target = targets[0];
|
|
21558
|
+
const adapterOptions = adapterOptionsForTarget(target, normalized);
|
|
21559
|
+
const adapter = await resolveAdapterForTarget(target, adapterOptions);
|
|
21560
|
+
const installationType = normalized.installationType ?? target.installationType ?? resolveInstallationTypeForAdapter(adapter);
|
|
21561
|
+
const state = installStateForTarget(target, adapter, adapterOptions, installationType);
|
|
21562
|
+
const request = {
|
|
21563
|
+
workspaceRoot: await workspaceRootFromOptions(normalized),
|
|
21564
|
+
fromWorkspaceRoot: normalizeCliPath(options.fromWorkspaceRoot),
|
|
21565
|
+
fromFleetId: options.fromFleetId,
|
|
21566
|
+
targetRoot: state.installRoot,
|
|
21567
|
+
adapter: adapter.name,
|
|
21568
|
+
installationType,
|
|
21569
|
+
stateKey: options.stateKey,
|
|
21570
|
+
selector,
|
|
21571
|
+
path: options.path,
|
|
21572
|
+
reason: options.reason,
|
|
21573
|
+
planDigest: options.planDigest,
|
|
21574
|
+
expectedSourceRevision: options.expectedSourceRevision,
|
|
21575
|
+
expectedInventoryRevision: options.expectedInventoryRevision,
|
|
21576
|
+
operationId: options.operationId,
|
|
21577
|
+
noCommit: options.commit === false,
|
|
21578
|
+
transport: transportForTarget(target)
|
|
21579
|
+
};
|
|
21580
|
+
const result = options.apply ? await applyReleaseOwnership(request) : await planReleaseOwnership(request);
|
|
21581
|
+
if (options.json) {
|
|
21582
|
+
console.log(JSON.stringify(result, null, 2));
|
|
21583
|
+
return;
|
|
21584
|
+
}
|
|
21585
|
+
console.log(`${options.apply ? "Released ownership claim" : "Ownership release plan"}: ${result.selector} at ${result.path}`);
|
|
21586
|
+
console.log(`Owner: ${result.owner}`);
|
|
21587
|
+
console.log(`Source: ${result.source.stateKey} revision ${result.source.revision}`);
|
|
21588
|
+
console.log(`Recorded hash: ${result.recordedHash}; actual runtime hash: ${result.runtimeHash}`);
|
|
21589
|
+
console.log(`Inventory revision: ${result.manifestInventoryRevision}`);
|
|
21590
|
+
console.log(`Plan digest: ${result.planDigest}`);
|
|
21591
|
+
if (!options.apply) console.log("Apply with this same scope and reason plus --apply --plan-digest, --expected-source-revision and --expected-inventory-revision from this plan.");
|
|
21592
|
+
});
|
|
21054
21593
|
ownershipCommand.command("handoff").description("transfer one managed artifact between Agentwheel workspace roots").argument("<selector>", "exact artifact selector in type/name form").option("--from-workspace-root <path>", "current owning workspace root").option("--from-unknown-owner", "hand off an artifact recorded as workspace:unknown", false).requiredOption("--to-workspace-root <path>", "new owning workspace root").option("--to-fleet <id>", "qualify the new owner with a registered fleet id").option("--expected-hash <sha256>", "expected recorded artifact hash; required when applying").option("--expected-revision <sha256>", "expected install manifest revision; required when applying").option("--carry-drift", "accept a file or directory whose runtime differs from the recorded hash; the recorded hash is kept", false).option("--expected-runtime-hash <sha256>", "expected drifted runtime hash; required when applying with --carry-drift").option("--adapter <adapter>", "built-in adapter").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("--profile <name>", "workspace runtime profile (must resolve to one target)").option("--dry-run", "validate all preconditions without writing the manifest", false).action(async (selector, options) => {
|
|
21055
21594
|
if (!options.dryRun && (!options.expectedHash || !options.expectedRevision)) {
|
|
21056
21595
|
throw new Error("Applying an ownership handoff requires --expected-hash and --expected-revision from a reviewed --dry-run.");
|
|
@@ -21078,7 +21617,7 @@ ownershipCommand.command("handoff").description("transfer one managed artifact b
|
|
|
21078
21617
|
const toWorkspaceRoot = normalizeCliPath(options.toWorkspaceRoot);
|
|
21079
21618
|
if (options.toFleet) {
|
|
21080
21619
|
const fleet = await showRegisteredFleet(options.toFleet);
|
|
21081
|
-
if (
|
|
21620
|
+
if (resolve44(fleet.root) !== resolve44(toWorkspaceRoot)) {
|
|
21082
21621
|
throw new Error(`Destination fleet '${options.toFleet}' is registered at ${fleet.root}, not ${toWorkspaceRoot}.`);
|
|
21083
21622
|
}
|
|
21084
21623
|
}
|
|
@@ -21119,8 +21658,8 @@ ownershipCommand.command("recovery-plan").description("write a read-only per-pat
|
|
|
21119
21658
|
const adapter = await resolveAdapterForTarget(target, {});
|
|
21120
21659
|
const transport = transportForTarget(target);
|
|
21121
21660
|
const requestedReport = normalizeCliPath(options.report);
|
|
21122
|
-
const reportPath =
|
|
21123
|
-
const reportInsideTarget =
|
|
21661
|
+
const reportPath = join61(await realpath4(dirname37(requestedReport)), basename24(requestedReport));
|
|
21662
|
+
const reportInsideTarget = relative19(resolve44(target.targetRoot), reportPath);
|
|
21124
21663
|
if (reportInsideTarget === "" || !reportInsideTarget.startsWith("..") && !isAbsolute14(reportInsideTarget)) {
|
|
21125
21664
|
throw new Error("Recovery report must be outside the runtime target root.");
|
|
21126
21665
|
}
|
|
@@ -21151,7 +21690,7 @@ ownershipCommand.command("retire-stale").description("retire stale source-manife
|
|
|
21151
21690
|
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
21152
21691
|
const fleet = await showRegisteredFleet(options.fleet);
|
|
21153
21692
|
const toWorkspaceRoot = normalizeCliPath(options.toWorkspaceRoot);
|
|
21154
|
-
if (
|
|
21693
|
+
if (resolve44(fleet.root) !== resolve44(toWorkspaceRoot)) {
|
|
21155
21694
|
throw new Error(`Destination Fleet '${options.fleet}' is registered at ${fleet.root}, not ${toWorkspaceRoot}.`);
|
|
21156
21695
|
}
|
|
21157
21696
|
const targets = await resolveCliTargets(normalizedOptions);
|
|
@@ -21159,7 +21698,7 @@ ownershipCommand.command("retire-stale").description("retire stale source-manife
|
|
|
21159
21698
|
throw new Error(`Stale ownership retirement requires exactly one destination Fleet target, found ${targets.length}.`);
|
|
21160
21699
|
}
|
|
21161
21700
|
const target = targets[0];
|
|
21162
|
-
if (target.fleetId !== options.fleet ||
|
|
21701
|
+
if (target.fleetId !== options.fleet || resolve44(target.workspaceRoot) !== resolve44(fleet.root)) {
|
|
21163
21702
|
throw new Error(`Resolved target does not belong to destination Fleet '${options.fleet}'.`);
|
|
21164
21703
|
}
|
|
21165
21704
|
const adapterOptions = adapterOptionsForTarget(target, normalizedOptions);
|
|
@@ -21231,7 +21770,7 @@ ownershipCommand.command("retire-stale").description("retire stale source-manife
|
|
|
21231
21770
|
console.log(`Plan digest: ${result.planDigest}`);
|
|
21232
21771
|
if (!options.apply) console.log(`Apply: ${applyCommand}`);
|
|
21233
21772
|
});
|
|
21234
|
-
ownershipCommand.command("adopt-legacy").description("move proven legacy-keyed ownership into this workspace's stable target state without rewriting runtime artifacts").requiredOption("--source-state-key <key>", "exact legacy install-manifest state key").requiredOption("--from-workspace-root <path>", "exact plain workspace owner root of the entries to move").option("--agent <name>", "named agent of this workspace").option("--profile <name>", "workspace profile resolving to one target").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("--carry-drift", "also move
|
|
21773
|
+
ownershipCommand.command("adopt-legacy").description("move proven legacy-keyed ownership into this workspace's stable target state without rewriting runtime artifacts").requiredOption("--source-state-key <key>", "exact legacy install-manifest state key").requiredOption("--from-workspace-root <path>", "exact plain workspace owner root of the entries to move").option("--agent <name>", "named agent of this workspace").option("--profile <name>", "workspace profile resolving to one target").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("--carry-drift", "also move drifted plain entries while keeping the recorded hash; forbidden for managed blocks", false).option("--plan-digest <sha256>", "reviewed plan digest; required with --apply").option("--expected-source-revision <sha256>", "reviewed source manifest revision; required with --apply").option("--expected-destination-revision <sha256|absent>", "reviewed destination manifest revision; required with --apply").option("--expected-inventory-revision <sha256>", "reviewed runtime manifest inventory revision; required with --apply").option("--apply", "apply the exact reviewed metadata-only adoption", false).option("--json", "print the complete deterministic plan as JSON", false).action(async (options) => {
|
|
21235
21774
|
if (Boolean(options.agent) === Boolean(options.profile)) {
|
|
21236
21775
|
throw new Error("Legacy ownership adoption requires exactly one of --agent or --profile.");
|
|
21237
21776
|
}
|
|
@@ -21281,7 +21820,7 @@ ownershipCommand.command("adopt-legacy").description("move proven legacy-keyed o
|
|
|
21281
21820
|
];
|
|
21282
21821
|
for (const { flag, requested, resolved, none } of requestedAdapterSettings) {
|
|
21283
21822
|
if (requested === void 0) continue;
|
|
21284
|
-
if (resolved !== void 0 &&
|
|
21823
|
+
if (resolved !== void 0 && resolve44(target.workspaceRoot, requested) === resolve44(target.workspaceRoot, resolved)) continue;
|
|
21285
21824
|
throw new Error(`${flag} ${requested} does not match what install resolves for the configured packages: ${resolved ?? none}.`);
|
|
21286
21825
|
}
|
|
21287
21826
|
const adapter = await resolveAdapterForTarget(group.target, adapterOptions);
|
|
@@ -21451,7 +21990,7 @@ journalCommand.command("list").description("show pending apply journals for reso
|
|
|
21451
21990
|
if (!journal) continue;
|
|
21452
21991
|
pending += 1;
|
|
21453
21992
|
console.log(`PENDING ${state.adapter.name}/${state.installationType} at ${state.installRoot}`);
|
|
21454
|
-
console.log(` journal: ${
|
|
21993
|
+
console.log(` journal: ${join61(state.installRoot, ".agentwheel", `${state.state.stateKey}.apply-journal.json`)}`);
|
|
21455
21994
|
console.log(` stateKey: ${state.state.stateKey}`);
|
|
21456
21995
|
console.log(` createdAt: ${journal.createdAt}`);
|
|
21457
21996
|
console.log(` updatedAt: ${journal.updatedAt}`);
|
|
@@ -21515,7 +22054,11 @@ program.command("doctor").description("check agentwheel runtime setup and compan
|
|
|
21515
22054
|
await printDoctor(targets[0], normalizedOptions);
|
|
21516
22055
|
});
|
|
21517
22056
|
async function runInstallCommand(nameOrSource, options, behavior) {
|
|
21518
|
-
const normalizedOptions = normalizeRuntimeScopeOptions(
|
|
22057
|
+
const normalizedOptions = normalizeRuntimeScopeOptions({
|
|
22058
|
+
...options,
|
|
22059
|
+
forceConflict: options.forceConflict || options.adopt,
|
|
22060
|
+
replaceConflict: options.replaceConflict || options.adopt
|
|
22061
|
+
}, { defaultUser: shouldDefaultUserInstall(nameOrSource, options) });
|
|
21519
22062
|
const outputFormat = effectivePlanOutputFormat(normalizedOptions);
|
|
21520
22063
|
const composite = await resolveSelectedCompositeProfile(normalizedOptions);
|
|
21521
22064
|
if (composite) {
|
|
@@ -21797,14 +22340,14 @@ async function packageEntryFromSource(source, targetRoot, options) {
|
|
|
21797
22340
|
);
|
|
21798
22341
|
}
|
|
21799
22342
|
const bundle = options.deferGraphRendering ? await stageSourceRaw(driver, resolvedSource, {
|
|
21800
|
-
cacheRoot:
|
|
22343
|
+
cacheRoot: join61(targetRoot, ".agentwheel", "cache"),
|
|
21801
22344
|
mode: options.mode,
|
|
21802
22345
|
ref: initialVersion?.ref,
|
|
21803
22346
|
frozenLock: lockMode
|
|
21804
22347
|
}) : await stageSource(driver, resolvedSource, {
|
|
21805
22348
|
workspaceRoot: targetRoot,
|
|
21806
22349
|
adapter,
|
|
21807
|
-
cacheRoot:
|
|
22350
|
+
cacheRoot: join61(targetRoot, ".agentwheel", "cache"),
|
|
21808
22351
|
mode: options.mode,
|
|
21809
22352
|
ref: initialVersion?.ref,
|
|
21810
22353
|
frozenLock: lockMode,
|
|
@@ -22412,7 +22955,7 @@ function scopeUpdatePlanToDependencies(result, selectors, previousLock, manifest
|
|
|
22412
22955
|
selectedPreviousNodeIds,
|
|
22413
22956
|
selectedRootIds
|
|
22414
22957
|
);
|
|
22415
|
-
const graphLockDigest =
|
|
22958
|
+
const graphLockDigest = createHash28("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
|
|
22416
22959
|
return {
|
|
22417
22960
|
...result,
|
|
22418
22961
|
bundle: { ...result.bundle, graphLock },
|
|
@@ -22538,7 +23081,7 @@ function operationBelongsToDependencyUpdate(operation, allowedNodeIds, allowedNa
|
|
|
22538
23081
|
function scopeInstallPlanToArtifact(result, rootId, focused, manifest) {
|
|
22539
23082
|
const scoped = scopePlanOperationsToArtifact(result, rootId, focused, manifest);
|
|
22540
23083
|
const graphLock = focusedGraphLockForInstall(scoped.bundle.graphLock, rootId, focused);
|
|
22541
|
-
const graphLockDigest =
|
|
23084
|
+
const graphLockDigest = createHash28("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
|
|
22542
23085
|
return {
|
|
22543
23086
|
...scoped,
|
|
22544
23087
|
bundle: { ...scoped.bundle, graphLock },
|
|
@@ -22549,7 +23092,7 @@ function scopeInstallPlanToArtifact(result, rootId, focused, manifest) {
|
|
|
22549
23092
|
function scopeUpdatePlanToArtifact(result, rootId, focused, previousLock, manifest) {
|
|
22550
23093
|
const scoped = scopePlanOperationsToArtifact(result, rootId, focused, manifest, previousLock);
|
|
22551
23094
|
const graphLock = mergeFocusedArtifactGraphLock(scoped.bundle.graphLock, previousLock, rootId, focused);
|
|
22552
|
-
const graphLockDigest =
|
|
23095
|
+
const graphLockDigest = createHash28("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
|
|
22553
23096
|
return {
|
|
22554
23097
|
...scoped,
|
|
22555
23098
|
bundle: { ...scoped.bundle, graphLock },
|
|
@@ -22844,7 +23387,7 @@ function scopeUpdatePlanToRoot(result, rootId, previousLock, manifest) {
|
|
|
22844
23387
|
selectedPreviousNodeIds,
|
|
22845
23388
|
/* @__PURE__ */ new Set([rootId])
|
|
22846
23389
|
);
|
|
22847
|
-
const graphLockDigest =
|
|
23390
|
+
const graphLockDigest = createHash28("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
|
|
22848
23391
|
return {
|
|
22849
23392
|
...scoped,
|
|
22850
23393
|
bundle: { ...scoped.bundle, graphLock },
|
|
@@ -22916,7 +23459,7 @@ function keepManifestEntryOperation(entry, targetRoot, scopeDescription, operati
|
|
|
22916
23459
|
artifactType: entry.artifactType,
|
|
22917
23460
|
artifactName: entry.artifactName,
|
|
22918
23461
|
kind: entry.kind,
|
|
22919
|
-
destPath: operation?.destPath ??
|
|
23462
|
+
destPath: operation?.destPath ?? join61(targetRoot, entry.path),
|
|
22920
23463
|
relativeDestPath: entry.path,
|
|
22921
23464
|
desiredHash: entry.sourceHash,
|
|
22922
23465
|
currentHash: operation?.currentHash ?? entry.hash,
|
|
@@ -23117,7 +23660,7 @@ function installRootContextForTarget(target) {
|
|
|
23117
23660
|
return { kind: target.transport, homeRoot: target.runAs?.home };
|
|
23118
23661
|
}
|
|
23119
23662
|
async function withFreshReadOnlyGraphPlan(target, adapter, adapterOptions, installationType, packages, use) {
|
|
23120
|
-
const stage = await mkdtemp6(
|
|
23663
|
+
const stage = await mkdtemp6(join61(tmpdir7(), "agentwheel-recovery-"));
|
|
23121
23664
|
const priorTmpdir = process.env.TMPDIR;
|
|
23122
23665
|
try {
|
|
23123
23666
|
process.env.TMPDIR = stage;
|
|
@@ -23149,8 +23692,8 @@ async function withFreshReadOnlyGraphPlan(target, adapter, adapterOptions, insta
|
|
|
23149
23692
|
yes: true,
|
|
23150
23693
|
freshGraphOnly: true,
|
|
23151
23694
|
deferForeignStateCheck: true,
|
|
23152
|
-
cacheRoot:
|
|
23153
|
-
registryCachePath:
|
|
23695
|
+
cacheRoot: join61(stage, "source-cache"),
|
|
23696
|
+
registryCachePath: join61(stage, "registry.json")
|
|
23154
23697
|
});
|
|
23155
23698
|
return await use(graphPlan);
|
|
23156
23699
|
} finally {
|
|
@@ -23237,7 +23780,7 @@ async function resolveCliTargetState(target, options) {
|
|
|
23237
23780
|
migration: prior.migration,
|
|
23238
23781
|
targetStateFilePreconditions: {
|
|
23239
23782
|
graphLockPath: stableGraphLockPath,
|
|
23240
|
-
graphLockRevision: stableLock ?
|
|
23783
|
+
graphLockRevision: stableLock ? createHash28("sha256").update(canonicalGraphLockJson(stableLock)).digest("hex") : null,
|
|
23241
23784
|
sourceLockRevision: computeSourceLockRevision(stableSourceLock)
|
|
23242
23785
|
}
|
|
23243
23786
|
};
|
|
@@ -23260,7 +23803,7 @@ async function collectTargetStatus(target, options) {
|
|
|
23260
23803
|
const pending = await collectPendingInstallWork(target, options);
|
|
23261
23804
|
const foreignObservations = pending.foreignStateObservations;
|
|
23262
23805
|
const effectiveGraphLock = graphLock ?? pending.observedGraphLock;
|
|
23263
|
-
const observedManifestRevision = foreignObservations.length > 0 ?
|
|
23806
|
+
const observedManifestRevision = foreignObservations.length > 0 ? createHash28("sha256").update(JSON.stringify(foreignObservations.map((observation) => [
|
|
23264
23807
|
observation.fileName,
|
|
23265
23808
|
observation.manifestRevision
|
|
23266
23809
|
]).sort())).digest("hex") : null;
|
|
@@ -23507,7 +24050,7 @@ async function assertCompositeMutationSupported(workspaceRoot, profile, readOnly
|
|
|
23507
24050
|
`Composite-profile mutation is refused before member execution because ${member.id} uses ${member.transport}; durable remote policy and recovery preflight is not implemented.`
|
|
23508
24051
|
);
|
|
23509
24052
|
}
|
|
23510
|
-
const memberRoot =
|
|
24053
|
+
const memberRoot = resolve44(workspaceRoot, member.workspace);
|
|
23511
24054
|
const memberPolicy = await mutationPolicyForWorkspace(memberRoot);
|
|
23512
24055
|
if (memberPolicy?.revisioning.mode === "commit-after-verify") {
|
|
23513
24056
|
throw new Error(
|
|
@@ -23744,12 +24287,12 @@ async function printDoctor(target, options) {
|
|
|
23744
24287
|
const requestedSkills = doctorSkillRequests(target, options);
|
|
23745
24288
|
const skills = [];
|
|
23746
24289
|
for (const request of requestedSkills) {
|
|
23747
|
-
const skillPath =
|
|
24290
|
+
const skillPath = join61(state.installRoot, targetMapping.dest, request.name);
|
|
23748
24291
|
const exists2 = await pathExists(skillPath);
|
|
23749
24292
|
const manifestEntry = manifest?.entries.find((entry) => {
|
|
23750
24293
|
if (entry.artifactType !== "skills") return false;
|
|
23751
24294
|
const legacyInstallName = "installName" in entry && typeof entry.installName === "string" ? entry.installName : void 0;
|
|
23752
|
-
return entry.artifactName === request.name || legacyInstallName === request.name || entry.path ===
|
|
24295
|
+
return entry.artifactName === request.name || legacyInstallName === request.name || entry.path === join61(targetMapping.dest, request.name);
|
|
23753
24296
|
});
|
|
23754
24297
|
const status = manifestEntry ? "managed" : exists2 ? "present-unmanaged" : "missing";
|
|
23755
24298
|
skills.push({
|
|
@@ -23829,7 +24372,7 @@ function doctorSkillLabel(name) {
|
|
|
23829
24372
|
return `${name} skill`;
|
|
23830
24373
|
}
|
|
23831
24374
|
function isSyncwheelWorkspace(targetRoot) {
|
|
23832
|
-
return existsSync(
|
|
24375
|
+
return existsSync(join61(targetRoot, ".syncwheel", "manifest.json"));
|
|
23833
24376
|
}
|
|
23834
24377
|
function skillInstallCommand(adapter, installationType, options, skill, behavior = {}) {
|
|
23835
24378
|
const args = [
|
|
@@ -23992,14 +24535,14 @@ function looksLikeSourceSpecifier(value) {
|
|
|
23992
24535
|
}
|
|
23993
24536
|
function normalizeCliPath(value) {
|
|
23994
24537
|
if (value === "~") return homedir18();
|
|
23995
|
-
if (value.startsWith("~/")) return
|
|
23996
|
-
return
|
|
24538
|
+
if (value.startsWith("~/")) return resolve44(homedir18(), value.slice(2));
|
|
24539
|
+
return resolve44(value);
|
|
23997
24540
|
}
|
|
23998
24541
|
function shellQuoteArgument(value) {
|
|
23999
24542
|
return /^[a-z0-9_./:@%+=,-]+$/i.test(value) ? value : `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
24000
24543
|
}
|
|
24001
24544
|
function isHomePath(path) {
|
|
24002
|
-
return
|
|
24545
|
+
return resolve44(path) === resolve44(homedir18());
|
|
24003
24546
|
}
|
|
24004
24547
|
function adapterListFromOption(adapter) {
|
|
24005
24548
|
if (!adapter) return [];
|
|
@@ -24054,11 +24597,11 @@ function filterUninstallPlanBySelection(plan, selected) {
|
|
|
24054
24597
|
};
|
|
24055
24598
|
}
|
|
24056
24599
|
async function initPackage(root) {
|
|
24057
|
-
await
|
|
24058
|
-
await
|
|
24059
|
-
await
|
|
24060
|
-
const manifestPath =
|
|
24061
|
-
const instructionsPath =
|
|
24600
|
+
await mkdir27(join61(root, "instructions"), { recursive: true });
|
|
24601
|
+
await mkdir27(join61(root, "rules"), { recursive: true });
|
|
24602
|
+
await mkdir27(join61(root, "skills"), { recursive: true });
|
|
24603
|
+
const manifestPath = join61(root, "openpack.json");
|
|
24604
|
+
const instructionsPath = join61(root, "instructions", "AGENTS.md");
|
|
24062
24605
|
const manifest = {
|
|
24063
24606
|
schemaVersion: CURRENT_OPENPACK_SCHEMA_VERSION,
|
|
24064
24607
|
name: "example/agentwheel-package",
|
|
@@ -24120,7 +24663,7 @@ function withFleetExample(config) {
|
|
|
24120
24663
|
};
|
|
24121
24664
|
}
|
|
24122
24665
|
async function findAgentwheelPackageRoot(start) {
|
|
24123
|
-
let current =
|
|
24666
|
+
let current = resolve44(start);
|
|
24124
24667
|
while (true) {
|
|
24125
24668
|
if (await findPackageManifestPath(current, { warnLegacy: false })) return current;
|
|
24126
24669
|
const parent = dirname37(current);
|
|
@@ -24248,14 +24791,14 @@ async function governedCommandContext(command, options) {
|
|
|
24248
24791
|
const kind = String(command.args[0] ?? "workspace");
|
|
24249
24792
|
return {
|
|
24250
24793
|
workspaceRoot: workspaceRoot2,
|
|
24251
|
-
anticipatedPaths: kind === "package" ? [
|
|
24794
|
+
anticipatedPaths: kind === "package" ? [join61(targetRoot, "openpack.json"), join61(targetRoot, "instructions", "AGENTS.md")] : [workspaceConfigPath(targetRoot)]
|
|
24252
24795
|
};
|
|
24253
24796
|
}
|
|
24254
24797
|
if (path === "package migrate") {
|
|
24255
24798
|
const packageRoot = normalizeTargetRoot(String(command.args[0] ?? "."));
|
|
24256
24799
|
return {
|
|
24257
24800
|
workspaceRoot: await findExistingWorkspaceRoot(packageRoot) ?? packageRoot,
|
|
24258
|
-
anticipatedPaths: ["agentwheel.json", "agentwheel.jsonc", "openpack.json", "openpack.jsonc"].map((name) =>
|
|
24801
|
+
anticipatedPaths: ["agentwheel.json", "agentwheel.jsonc", "openpack.json", "openpack.jsonc"].map((name) => join61(packageRoot, name))
|
|
24259
24802
|
};
|
|
24260
24803
|
}
|
|
24261
24804
|
const source = typeof command.args[0] === "string" ? command.args[0] : void 0;
|
|
@@ -24267,7 +24810,7 @@ async function governedCommandContext(command, options) {
|
|
|
24267
24810
|
const anticipatedPaths = [];
|
|
24268
24811
|
if (path === "add") anticipatedPaths.push(workspaceConfigPath(workspaceRoot));
|
|
24269
24812
|
if (path === "remember" && options.runtime) {
|
|
24270
|
-
anticipatedPaths.push(
|
|
24813
|
+
anticipatedPaths.push(join61(workspaceRoot, ".agentwheel", "overlays", options.runtime, "instructions.local.md"));
|
|
24271
24814
|
}
|
|
24272
24815
|
if (path === "uninstall" && source) anticipatedPaths.push(workspaceConfigPath(workspaceRoot));
|
|
24273
24816
|
return {
|