agentwheel 0.21.0 → 0.21.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -5
- package/dist/index.js +1031 -420
- 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)}`);
|
|
@@ -2805,6 +2805,62 @@ var init_semver = __esm({
|
|
|
2805
2805
|
}
|
|
2806
2806
|
});
|
|
2807
2807
|
|
|
2808
|
+
// src/model/selection.ts
|
|
2809
|
+
function artifactSelectorKey(artifact) {
|
|
2810
|
+
return `${artifact.type}/${artifact.name}`;
|
|
2811
|
+
}
|
|
2812
|
+
function artifactSelectorAliases(artifact) {
|
|
2813
|
+
const primary = artifactSelectorKey(artifact);
|
|
2814
|
+
if (artifact.type !== "subagents") return [primary];
|
|
2815
|
+
const baseName = subagentBaseName(artifact.name);
|
|
2816
|
+
if (baseName === artifact.name) return [primary];
|
|
2817
|
+
return [primary, `subagents/${baseName}`];
|
|
2818
|
+
}
|
|
2819
|
+
function normalizeArtifactSelectors(select, legacySkills) {
|
|
2820
|
+
const selected = [
|
|
2821
|
+
...select ?? [],
|
|
2822
|
+
...(legacySkills ?? []).map((name) => `skills/${name}`)
|
|
2823
|
+
].flatMap(splitSelectorList);
|
|
2824
|
+
if (selected.length === 0) return void 0;
|
|
2825
|
+
return [...new Set(selected.map(parseArtifactSelector))];
|
|
2826
|
+
}
|
|
2827
|
+
function filterArtifactsBySelection(artifacts, selectors, legacySkills, options = {}) {
|
|
2828
|
+
const selected = normalizeArtifactSelectors(selectors, legacySkills);
|
|
2829
|
+
if (!selected?.length) return artifacts;
|
|
2830
|
+
const selectedSet = new Set(selected);
|
|
2831
|
+
const available = new Set((options.validationArtifacts ?? artifacts).flatMap(artifactSelectorAliases));
|
|
2832
|
+
const missing = selected.filter((selector) => !available.has(selector));
|
|
2833
|
+
if (missing.length > 0) {
|
|
2834
|
+
throw new Error(`Selected artifact not found in package: ${missing.join(", ")}`);
|
|
2835
|
+
}
|
|
2836
|
+
return artifacts.filter((artifact) => artifact.required || artifactSelectorAliases(artifact).some((selector) => selectedSet.has(selector)));
|
|
2837
|
+
}
|
|
2838
|
+
function splitSelectorList(value) {
|
|
2839
|
+
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
2840
|
+
}
|
|
2841
|
+
function parseArtifactSelector(value) {
|
|
2842
|
+
const slash = value.indexOf("/");
|
|
2843
|
+
if (slash <= 0 || slash === value.length - 1) {
|
|
2844
|
+
throw new Error(`Invalid artifact selector: ${value}. Expected <type>/<name>.`);
|
|
2845
|
+
}
|
|
2846
|
+
const type = value.slice(0, slash);
|
|
2847
|
+
const name = value.slice(slash + 1);
|
|
2848
|
+
const parsedType = artifactTypeSchema.safeParse(type);
|
|
2849
|
+
if (!parsedType.success) {
|
|
2850
|
+
throw new Error(`Invalid artifact selector type: ${type}`);
|
|
2851
|
+
}
|
|
2852
|
+
return `${parsedType.data}/${name}`;
|
|
2853
|
+
}
|
|
2854
|
+
function subagentBaseName(name) {
|
|
2855
|
+
return name.replace(/\.agent\.md$/i, "").replace(/\.toml$/i, "").replace(/\.md$/i, "");
|
|
2856
|
+
}
|
|
2857
|
+
var init_selection = __esm({
|
|
2858
|
+
"src/model/selection.ts"() {
|
|
2859
|
+
"use strict";
|
|
2860
|
+
init_artifact();
|
|
2861
|
+
}
|
|
2862
|
+
});
|
|
2863
|
+
|
|
2808
2864
|
// src/model/workspace.ts
|
|
2809
2865
|
import { readFile as readFile10 } from "fs/promises";
|
|
2810
2866
|
import { homedir as homedir4 } from "os";
|
|
@@ -2829,6 +2885,43 @@ async function writeWorkspaceConfig(workspaceRoot, config) {
|
|
|
2829
2885
|
declareMutationPath(path);
|
|
2830
2886
|
await writeJsonAtomic(path, workspaceConfigSchema.parse(config));
|
|
2831
2887
|
}
|
|
2888
|
+
async function pinConfiguredPackages(workspaceRoot, input) {
|
|
2889
|
+
const pins = z10.array(z10.object({
|
|
2890
|
+
alias: z10.string().min(1),
|
|
2891
|
+
commit: z10.string().regex(/^[a-f0-9]{40}$/i, "Pin requires an exact 40-hex commit SHA."),
|
|
2892
|
+
select: z10.array(z10.string().min(1)).min(1).optional()
|
|
2893
|
+
}).strict()).min(1).parse(input).map((pin) => {
|
|
2894
|
+
const select = pin.select === void 0 ? void 0 : normalizeArtifactSelectors(pin.select);
|
|
2895
|
+
if (pin.select !== void 0 && !select?.length) throw new Error("Explicit pin selection must not be empty.");
|
|
2896
|
+
return { ...pin, select };
|
|
2897
|
+
});
|
|
2898
|
+
if (new Set(pins.map((pin) => pin.alias)).size !== pins.length) {
|
|
2899
|
+
throw new Error("Pin requires unique configured aliases.");
|
|
2900
|
+
}
|
|
2901
|
+
const path = workspaceConfigPath(workspaceRoot);
|
|
2902
|
+
const config = JSON.parse(await readFile10(path, "utf8"));
|
|
2903
|
+
workspaceConfigSchema.parse(config);
|
|
2904
|
+
const updates = pins.map(({ alias, commit, select }) => {
|
|
2905
|
+
const matches = (config.packages ?? []).filter((pkg2) => pkg2.name === alias);
|
|
2906
|
+
if (matches.length !== 1) {
|
|
2907
|
+
throw new Error(matches.length === 0 ? `Configured package alias not found: ${alias}` : `Ambiguous configured package alias: ${alias}`);
|
|
2908
|
+
}
|
|
2909
|
+
const pkg = matches[0];
|
|
2910
|
+
if (pkg.mode === "tracking") throw new Error(`Configured package ${alias} is tracking; pin requires pinned mode.`);
|
|
2911
|
+
return { pkg, requestedRef: commit, select };
|
|
2912
|
+
});
|
|
2913
|
+
for (const { pkg, requestedRef, select } of updates) {
|
|
2914
|
+
pkg.requestedRef = requestedRef;
|
|
2915
|
+
if (select !== void 0) {
|
|
2916
|
+
pkg.select = select;
|
|
2917
|
+
delete pkg.skills;
|
|
2918
|
+
delete pkg.selection;
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2921
|
+
workspaceConfigSchema.parse(config);
|
|
2922
|
+
declareMutationPath(path);
|
|
2923
|
+
await writeJsonAtomic(path, config);
|
|
2924
|
+
}
|
|
2832
2925
|
function upsertPackage(config, entry) {
|
|
2833
2926
|
const parsed = workspaceConfigSchema.parse(config);
|
|
2834
2927
|
const packages = parsed.packages.filter((candidate) => candidate.name !== entry.name);
|
|
@@ -2920,6 +3013,7 @@ var init_workspace = __esm({
|
|
|
2920
3013
|
init_mutation();
|
|
2921
3014
|
init_declarations();
|
|
2922
3015
|
init_adapters();
|
|
3016
|
+
init_selection();
|
|
2923
3017
|
artifactSelectorListSchema = z10.array(z10.string().min(1));
|
|
2924
3018
|
CURRENT_WORKSPACE_SCHEMA_VERSION = 4;
|
|
2925
3019
|
workspaceSelectionImportSchema = z10.object({
|
|
@@ -3236,15 +3330,15 @@ async function snapshotRepository(repositoryRoot) {
|
|
|
3236
3330
|
async function collectIntroducedPaths(repositoryRoot, expectedHead, before, declaredPaths) {
|
|
3237
3331
|
const after = await snapshotRepository(repositoryRoot);
|
|
3238
3332
|
const touchedPreexisting = /* @__PURE__ */ new Set();
|
|
3239
|
-
for (const [path,
|
|
3333
|
+
for (const [path, hash2] of before.changed) {
|
|
3240
3334
|
if (declaredPaths.includes(path)) touchedPreexisting.add(path);
|
|
3241
3335
|
else if (!after.changed.has(path)) {
|
|
3242
3336
|
const head = await currentHead(repositoryRoot);
|
|
3243
3337
|
const ancestor = await runProcess("git", ["-C", repositoryRoot, "merge-base", "--is-ancestor", expectedHead, head], {
|
|
3244
3338
|
allowExitCodes: [0, 1]
|
|
3245
3339
|
});
|
|
3246
|
-
if (head === expectedHead || ancestor.exitCode !== 0 || await hashWorkingPath(repositoryRoot, path) !==
|
|
3247
|
-
} else if (after.changed.get(path) !==
|
|
3340
|
+
if (head === expectedHead || ancestor.exitCode !== 0 || await hashWorkingPath(repositoryRoot, path) !== hash2 || await hashHeadPath(repositoryRoot, head, path) !== hash2) touchedPreexisting.add(path);
|
|
3341
|
+
} else if (after.changed.get(path) !== hash2) touchedPreexisting.add(path);
|
|
3248
3342
|
}
|
|
3249
3343
|
if (touchedPreexisting.size > 0) {
|
|
3250
3344
|
throw new Error(`Mutation touched pre-existing dirty paths: ${[...touchedPreexisting].sort().join(", ")}.`);
|
|
@@ -3310,19 +3404,19 @@ async function hashWorkingPath(repositoryRoot, path) {
|
|
|
3310
3404
|
throw new Error(`Unsupported repository path kind: ${path}`);
|
|
3311
3405
|
}
|
|
3312
3406
|
async function hashDirectory(root) {
|
|
3313
|
-
const
|
|
3407
|
+
const hash2 = createHash7("sha256");
|
|
3314
3408
|
async function walk2(dir) {
|
|
3315
3409
|
for (const entry of (await readdir4(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
3316
3410
|
if (entry.name === ".git" || entry.name === "node_modules") continue;
|
|
3317
3411
|
const absolute = resolve12(dir, entry.name);
|
|
3318
3412
|
const relativePath = normalizeRepoPath(relative5(root, absolute));
|
|
3319
3413
|
if (entry.isDirectory()) await walk2(absolute);
|
|
3320
|
-
else if (entry.isSymbolicLink())
|
|
3321
|
-
else if (entry.isFile())
|
|
3414
|
+
else if (entry.isSymbolicLink()) hash2.update(relativePath).update("\0").update(await readlink(absolute)).update("\0");
|
|
3415
|
+
else if (entry.isFile()) hash2.update(relativePath).update("\0").update(await readFile11(absolute)).update("\0");
|
|
3322
3416
|
}
|
|
3323
3417
|
}
|
|
3324
3418
|
await walk2(root);
|
|
3325
|
-
return
|
|
3419
|
+
return hash2.digest("hex");
|
|
3326
3420
|
}
|
|
3327
3421
|
function splitNull(value) {
|
|
3328
3422
|
return value.toString("utf8").split("\0").filter(Boolean);
|
|
@@ -3938,11 +4032,11 @@ function dirtyRepositoryPaths(repositoryRoot, baseline, paths) {
|
|
|
3938
4032
|
function activeMutationMetadata() {
|
|
3939
4033
|
return activeMutation?.metadata();
|
|
3940
4034
|
}
|
|
3941
|
-
async function reserveActiveRuntimeJournal(
|
|
3942
|
-
await activeMutation?.transitionRuntimeJournal(
|
|
4035
|
+
async function reserveActiveRuntimeJournal(link3) {
|
|
4036
|
+
await activeMutation?.transitionRuntimeJournal(link3, "reserved");
|
|
3943
4037
|
}
|
|
3944
|
-
async function activateActiveRuntimeJournal(
|
|
3945
|
-
await activeMutation?.transitionRuntimeJournal(
|
|
4038
|
+
async function activateActiveRuntimeJournal(link3) {
|
|
4039
|
+
await activeMutation?.transitionRuntimeJournal(link3, "pending");
|
|
3946
4040
|
}
|
|
3947
4041
|
async function beginResolveMutationRuntimeJournal(operationId, path) {
|
|
3948
4042
|
await transitionMutationRuntimeJournal(operationId, path, "resolving");
|
|
@@ -4208,14 +4302,14 @@ function receiptStatusForProvider(status) {
|
|
|
4208
4302
|
if (status === "no-repository-delta") return "no-repository-delta";
|
|
4209
4303
|
return "succeeded";
|
|
4210
4304
|
}
|
|
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 ${
|
|
4305
|
+
function upsertRuntimeJournal(entries, link3, status) {
|
|
4306
|
+
const existing = entries.find((entry) => entry.path === link3.path);
|
|
4307
|
+
if (existing && (existing.transport !== link3.transport || existing.transportDescription !== link3.transportDescription || existing.journalDigest !== link3.journalDigest)) {
|
|
4308
|
+
throw new Error(`Runtime journal link collision for ${link3.path}.`);
|
|
4215
4309
|
}
|
|
4216
4310
|
return [
|
|
4217
|
-
...entries.filter((entry) => entry.path !==
|
|
4218
|
-
{ ...
|
|
4311
|
+
...entries.filter((entry) => entry.path !== link3.path),
|
|
4312
|
+
{ ...link3, status }
|
|
4219
4313
|
].sort((a, b) => a.path.localeCompare(b.path));
|
|
4220
4314
|
}
|
|
4221
4315
|
var GovernedMutation, activeMutation;
|
|
@@ -4416,11 +4510,12 @@ var init_coordinator = __esm({
|
|
|
4416
4510
|
operationId: this.receipt.operationId,
|
|
4417
4511
|
reason: this.receipt.reason,
|
|
4418
4512
|
noCommit: this.receipt.noCommit,
|
|
4419
|
-
revisionMode: this.receipt.revisionMode
|
|
4513
|
+
revisionMode: this.receipt.revisionMode,
|
|
4514
|
+
workspaceRoot: this.receipt.workspaceRoot
|
|
4420
4515
|
};
|
|
4421
4516
|
}
|
|
4422
|
-
async transitionRuntimeJournal(
|
|
4423
|
-
const runtimeJournals = upsertRuntimeJournal(this.receipt.runtimeJournals,
|
|
4517
|
+
async transitionRuntimeJournal(link3, status) {
|
|
4518
|
+
const runtimeJournals = upsertRuntimeJournal(this.receipt.runtimeJournals, link3, status);
|
|
4424
4519
|
this.receipt = await updateMutationReceipt(this.receipt, { runtimeJournals });
|
|
4425
4520
|
}
|
|
4426
4521
|
async transitionExistingRuntimeJournal(path, status) {
|
|
@@ -4488,20 +4583,88 @@ __export(transaction_exports, {
|
|
|
4488
4583
|
applyLockPath: () => applyLockPath,
|
|
4489
4584
|
assertApplyJournalRecoveryAllowed: () => assertApplyJournalRecoveryAllowed,
|
|
4490
4585
|
assertGovernedRuntimeTransportSupported: () => assertGovernedRuntimeTransportSupported,
|
|
4586
|
+
assertNoSymlinkPath: () => assertNoSymlinkPath,
|
|
4491
4587
|
listAllApplyJournals: () => listAllApplyJournals,
|
|
4492
4588
|
listApplyJournals: () => listApplyJournals,
|
|
4493
4589
|
localPathExists: () => localPathExists,
|
|
4590
|
+
metadataAuditPath: () => metadataAuditPath,
|
|
4591
|
+
metadataRuntimePath: () => metadataRuntimePath,
|
|
4494
4592
|
mutationMetadataForApplyJournal: () => mutationMetadataForApplyJournal,
|
|
4593
|
+
ownershipRuntimeSnapshot: () => ownershipRuntimeSnapshot,
|
|
4495
4594
|
readApplyJournal: () => readApplyJournal,
|
|
4496
4595
|
readLinkedLocalApplyJournal: () => readLinkedLocalApplyJournal,
|
|
4497
4596
|
recordBackup: () => recordBackup,
|
|
4498
4597
|
removeApplyJournal: () => removeApplyJournal,
|
|
4499
4598
|
rollbackCompletedOperations: () => rollbackCompletedOperations,
|
|
4599
|
+
syncMetadataPath: () => syncMetadataPath,
|
|
4500
4600
|
writeApplyJournal: () => writeApplyJournal
|
|
4501
4601
|
});
|
|
4502
4602
|
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";
|
|
4603
|
+
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";
|
|
4604
|
+
import { dirname as dirname9, join as join11, relative as relative7, resolve as resolve15, sep as sep5 } from "path";
|
|
4605
|
+
async function assertNoSymlinkPath(path) {
|
|
4606
|
+
const absolute = resolve15(path);
|
|
4607
|
+
let current = absolute;
|
|
4608
|
+
while (true) {
|
|
4609
|
+
try {
|
|
4610
|
+
if ((await lstat2(current)).isSymbolicLink()) throw new Error(`Symlink path is unsupported: ${current}`);
|
|
4611
|
+
} catch (error) {
|
|
4612
|
+
if (error.code !== "ENOENT") throw error;
|
|
4613
|
+
}
|
|
4614
|
+
const parent = dirname9(current);
|
|
4615
|
+
if (parent === current) break;
|
|
4616
|
+
current = parent;
|
|
4617
|
+
}
|
|
4618
|
+
}
|
|
4619
|
+
async function ownershipRuntimeSnapshot(path) {
|
|
4620
|
+
await assertNoSymlinkPath(path);
|
|
4621
|
+
const hash2 = createHash9("sha256");
|
|
4622
|
+
async function visit(candidate) {
|
|
4623
|
+
const info = await lstat2(candidate);
|
|
4624
|
+
if (info.isSymbolicLink() || !info.isDirectory() && !info.isFile()) {
|
|
4625
|
+
throw new Error(`Unsupported ownership runtime path: ${candidate}`);
|
|
4626
|
+
}
|
|
4627
|
+
hash2.update(JSON.stringify([relative7(path, candidate), info.isDirectory() ? "dir" : "file", info.mode & 4095]));
|
|
4628
|
+
if (info.isDirectory()) {
|
|
4629
|
+
for (const name of (await readdir5(candidate)).sort()) await visit(join11(candidate, name));
|
|
4630
|
+
} else {
|
|
4631
|
+
const content = await readFile13(candidate);
|
|
4632
|
+
hash2.update(String(content.length)).update("\0").update(content);
|
|
4633
|
+
}
|
|
4634
|
+
}
|
|
4635
|
+
await visit(path);
|
|
4636
|
+
return hash2.digest("hex");
|
|
4637
|
+
}
|
|
4638
|
+
function metadataAuditPath(audit, path) {
|
|
4639
|
+
mutationOperationIdSchema.parse(audit.operationId);
|
|
4640
|
+
if (resolve15(audit.workspaceRoot) !== audit.workspaceRoot || path !== `.agentwheel/ownership-releases/${audit.operationId}.json`) {
|
|
4641
|
+
throw new Error("Invalid ownership release audit path.");
|
|
4642
|
+
}
|
|
4643
|
+
return join11(audit.workspaceRoot, ...path.split("/"));
|
|
4644
|
+
}
|
|
4645
|
+
function metadataRuntimePath(root, path) {
|
|
4646
|
+
const result = resolve15(root, path);
|
|
4647
|
+
if (!path || relative7(resolve15(root), result).split(sep5).join("/") !== path || path === ".agentwheel" || path.startsWith(".agentwheel/") || result === resolve15(root) || !result.startsWith(`${resolve15(root)}${sep5}`)) {
|
|
4648
|
+
throw new Error(`Unsafe ownership release path: ${path}`);
|
|
4649
|
+
}
|
|
4650
|
+
return result;
|
|
4651
|
+
}
|
|
4652
|
+
async function syncMetadataPath(path, directoryOnly = false) {
|
|
4653
|
+
if (!directoryOnly) {
|
|
4654
|
+
const file = await open3(path, "r");
|
|
4655
|
+
try {
|
|
4656
|
+
await file.sync();
|
|
4657
|
+
} finally {
|
|
4658
|
+
await file.close();
|
|
4659
|
+
}
|
|
4660
|
+
}
|
|
4661
|
+
const directory = await open3(directoryOnly ? path : dirname9(path), "r");
|
|
4662
|
+
try {
|
|
4663
|
+
await directory.sync();
|
|
4664
|
+
} finally {
|
|
4665
|
+
await directory.close();
|
|
4666
|
+
}
|
|
4667
|
+
}
|
|
4505
4668
|
function applyLockPath(targetRoot, adapter, scope = {}) {
|
|
4506
4669
|
const installationType = scope.installationType ?? "local";
|
|
4507
4670
|
return join11(metadataDir(targetRoot), `${stateKeyFor(adapter, { installationType })}.runtime-apply-lock`);
|
|
@@ -4543,30 +4706,31 @@ async function writeApplyJournal(journal, transport = localTransport) {
|
|
|
4543
4706
|
installationType: journal.installationType,
|
|
4544
4707
|
stateKey: journal.stateKey
|
|
4545
4708
|
});
|
|
4546
|
-
let
|
|
4709
|
+
let link3;
|
|
4547
4710
|
if (journal.mutation) {
|
|
4548
|
-
const
|
|
4549
|
-
if (journal.mutation.journalDigest && journal.mutation.journalDigest !==
|
|
4711
|
+
const digest3 = applyJournalLinkDigest(journal, transport);
|
|
4712
|
+
if (journal.mutation.journalDigest && journal.mutation.journalDigest !== digest3) {
|
|
4550
4713
|
throw new Error(`Runtime apply journal identity changed for ${path}.`);
|
|
4551
4714
|
}
|
|
4552
4715
|
if (journal.mutation.transport && (journal.mutation.transport.kind !== transport.kind || journal.mutation.transport.description !== transport.description)) {
|
|
4553
4716
|
throw new Error(`Runtime apply journal transport changed for ${path}.`);
|
|
4554
4717
|
}
|
|
4555
|
-
journal.mutation.journalDigest =
|
|
4718
|
+
journal.mutation.journalDigest = digest3;
|
|
4556
4719
|
journal.mutation.transport = { kind: transport.kind, description: transport.description };
|
|
4557
|
-
|
|
4720
|
+
link3 = {
|
|
4558
4721
|
path,
|
|
4559
4722
|
transport: transport.kind,
|
|
4560
4723
|
transportDescription: transport.description,
|
|
4561
|
-
journalDigest:
|
|
4724
|
+
journalDigest: digest3
|
|
4562
4725
|
};
|
|
4563
|
-
await reserveActiveRuntimeJournal(
|
|
4726
|
+
await reserveActiveRuntimeJournal(link3);
|
|
4564
4727
|
}
|
|
4565
4728
|
await transport.writeJsonAtomic(path, {
|
|
4566
4729
|
...journal,
|
|
4567
4730
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4568
4731
|
});
|
|
4569
|
-
if (
|
|
4732
|
+
if ((journal.metadataAudit || journal.metadataGuards) && transport.kind === "local") await syncMetadataPath(path);
|
|
4733
|
+
if (link3) await activateActiveRuntimeJournal(link3);
|
|
4570
4734
|
}
|
|
4571
4735
|
async function readApplyJournal(targetRoot, adapter, transport = localTransport, scope = {}) {
|
|
4572
4736
|
const path = applyJournalPath(targetRoot, adapter, scope);
|
|
@@ -4593,7 +4757,7 @@ async function listAllApplyJournals(targetRoot, adapter, transport = localTransp
|
|
|
4593
4757
|
if (!fileName.endsWith(suffix)) continue;
|
|
4594
4758
|
const path = join11(metadataDir(targetRoot), fileName);
|
|
4595
4759
|
const journal = parseApplyJournal(JSON.parse(await transport.readFile(path)));
|
|
4596
|
-
if (journal.adapter === adapter) found.push({ path, journal });
|
|
4760
|
+
if (adapter === void 0 || journal.adapter === adapter) found.push({ path, journal });
|
|
4597
4761
|
}
|
|
4598
4762
|
return found.sort((a, b) => a.path.localeCompare(b.path));
|
|
4599
4763
|
}
|
|
@@ -4632,6 +4796,7 @@ async function removeApplyJournal(targetRoot, adapter, transport = localTranspor
|
|
|
4632
4796
|
if (existing?.mutation) await beginResolveMutationRuntimeJournal(existing.mutation.operationId, path);
|
|
4633
4797
|
await transport.rm(applyJournalPath(targetRoot, adapter, scope));
|
|
4634
4798
|
await transport.rm(applyBackupDir(targetRoot, adapter, scope));
|
|
4799
|
+
if ((existing?.metadataAudit || existing?.metadataGuards) && transport.kind === "local") await syncMetadataPath(dirname9(path), true);
|
|
4635
4800
|
if (existing?.mutation) await resolveMutationRuntimeJournal(existing.mutation.operationId, path);
|
|
4636
4801
|
}
|
|
4637
4802
|
async function abortApplyJournal(targetRoot, adapter, transport = localTransport, scope = {}) {
|
|
@@ -4644,6 +4809,9 @@ async function abortApplyJournal(targetRoot, adapter, transport = localTransport
|
|
|
4644
4809
|
const archivePath = join11(metadataDir(targetRoot), "archive", `${stateKey}.apply-journal.failed-${journalTimestamp(/* @__PURE__ */ new Date())}.json`);
|
|
4645
4810
|
const content = await transport.readFile(journalPath);
|
|
4646
4811
|
const journal = parseApplyJournal(JSON.parse(content));
|
|
4812
|
+
if (journal.metadataAudit || journal.metadataGuards) {
|
|
4813
|
+
throw new Error("A guarded metadata transaction must be recovered and verified; abort cannot discard its partial state.");
|
|
4814
|
+
}
|
|
4647
4815
|
await transport.writeFileAtomic(archivePath, content.endsWith("\n") ? content : `${content}
|
|
4648
4816
|
`);
|
|
4649
4817
|
if (journal.mutation) await beginResolveMutationRuntimeJournal(journal.mutation.operationId, journalPath);
|
|
@@ -4695,7 +4863,9 @@ function applyJournalLinkDigest(journal, transport) {
|
|
|
4695
4863
|
graphLockRemovePath: journal.graphLockRemovePath ?? null,
|
|
4696
4864
|
stateRemoveKey: journal.stateRemoveKey ?? null,
|
|
4697
4865
|
workspaceConfigPath: journal.workspaceConfigPath ?? null,
|
|
4698
|
-
operations: journal.operations
|
|
4866
|
+
operations: journal.operations,
|
|
4867
|
+
...journal.metadataAudit ? { metadataAudit: journal.metadataAudit, manifest: journal.manifest } : {},
|
|
4868
|
+
...journal.metadataGuards ? { metadataGuards: journal.metadataGuards, manifest: journal.manifest } : {}
|
|
4699
4869
|
})).digest("hex");
|
|
4700
4870
|
}
|
|
4701
4871
|
function assertApplyJournalRecoveryAllowed(journal) {
|
|
@@ -5363,7 +5533,56 @@ var init_desired = __esm({
|
|
|
5363
5533
|
import { createHash as createHash10 } from "crypto";
|
|
5364
5534
|
import { mkdtemp as mkdtemp2, readFile as readFile17, realpath, rm as rm6, writeFile as writeFile11 } from "fs/promises";
|
|
5365
5535
|
import { tmpdir as tmpdir3 } from "os";
|
|
5366
|
-
import { basename as basename2, dirname as dirname13, join as join12, relative as
|
|
5536
|
+
import { basename as basename2, dirname as dirname13, join as join12, relative as relative8 } from "path";
|
|
5537
|
+
function exactManagedInstructionBlockSnapshot(content, selector) {
|
|
5538
|
+
const beginCount = [...content.matchAll(new RegExp(`<!-- BEGIN openpack:include ${escapeRegex(selector)}(?=\\s|-->)`, "gu"))].length;
|
|
5539
|
+
const endCount = [...content.matchAll(new RegExp(`<!-- END openpack:include ${escapeRegex(selector)}(?=\\s|-->)`, "gu"))].length;
|
|
5540
|
+
if (beginCount !== 1 || endCount !== 1) throw new Error("Duplicate or missing managed instruction markers; adoption is refused.");
|
|
5541
|
+
const stack = [];
|
|
5542
|
+
let selectedBegin;
|
|
5543
|
+
let selectedEnd;
|
|
5544
|
+
let begins = 0;
|
|
5545
|
+
let ends = 0;
|
|
5546
|
+
for (const token of content.matchAll(/<!-- (BEGIN|END) openpack:include ([^\r\n]*?) -->/gu)) {
|
|
5547
|
+
const at = token.index;
|
|
5548
|
+
if (token[1] === "BEGIN") {
|
|
5549
|
+
const begin = /^(.*?) sha256:([a-f0-9]{64})$/u.exec(token[2]);
|
|
5550
|
+
if (!begin) throw new Error("Malformed managed instruction begin marker; adoption is refused.");
|
|
5551
|
+
const name = begin[1];
|
|
5552
|
+
if (name === selector) {
|
|
5553
|
+
if (++begins !== 1 || stack.length !== 0) throw new Error("Duplicate or overlapping managed instruction block; adoption is refused.");
|
|
5554
|
+
let bodyStart = at + token[0].length;
|
|
5555
|
+
if (content.slice(bodyStart, bodyStart + 2) === "\r\n") bodyStart += 2;
|
|
5556
|
+
else if (content[bodyStart] === "\n") bodyStart++;
|
|
5557
|
+
selectedBegin = { start: at, bodyStart, hash: begin[2] };
|
|
5558
|
+
}
|
|
5559
|
+
stack.push(name);
|
|
5560
|
+
} else {
|
|
5561
|
+
const name = token[2];
|
|
5562
|
+
if (stack.pop() !== name) throw new Error("Overlapping or unmatched managed instruction markers; adoption is refused.");
|
|
5563
|
+
if (name === selector) {
|
|
5564
|
+
if (++ends !== 1 || !selectedBegin) throw new Error("Duplicate or missing managed instruction marker; adoption is refused.");
|
|
5565
|
+
let end = at + token[0].length;
|
|
5566
|
+
if (content.slice(end, end + 2) === "\r\n") end += 2;
|
|
5567
|
+
else if (content[end] === "\n") end++;
|
|
5568
|
+
selectedEnd = { bodyEnd: at, end };
|
|
5569
|
+
}
|
|
5570
|
+
}
|
|
5571
|
+
}
|
|
5572
|
+
if (stack.length || begins !== 1 || ends !== 1 || !selectedBegin || !selectedEnd) {
|
|
5573
|
+
throw new Error(`Missing or incomplete managed instruction block for ${selector}; adoption is refused.`);
|
|
5574
|
+
}
|
|
5575
|
+
const hash2 = hashText(content.slice(selectedBegin.bodyStart, selectedEnd.bodyEnd));
|
|
5576
|
+
if (hash2 !== selectedBegin.hash) throw new Error(`Managed instruction block drift detected for ${selector}; adoption is refused.`);
|
|
5577
|
+
return {
|
|
5578
|
+
selector,
|
|
5579
|
+
hash: hash2,
|
|
5580
|
+
markerHash: selectedBegin.hash,
|
|
5581
|
+
prefixHash: hashText(content.slice(0, selectedBegin.start)),
|
|
5582
|
+
suffixHash: hashText(content.slice(selectedEnd.end)),
|
|
5583
|
+
blockBytesHash: hashText(content.slice(selectedBegin.start, selectedEnd.end))
|
|
5584
|
+
};
|
|
5585
|
+
}
|
|
5367
5586
|
async function desiredManagedInstructionBlockHash(sourcePath) {
|
|
5368
5587
|
const source = await readFile17(sourcePath, "utf8");
|
|
5369
5588
|
return hashText(managedBlockBody(source));
|
|
@@ -5375,13 +5594,13 @@ async function readManagedInstructionBlockState(destPath, selector, transport) {
|
|
|
5375
5594
|
const content = await transport.readFile(destPath);
|
|
5376
5595
|
const block = findManagedInstructionBlock(content, selector);
|
|
5377
5596
|
if (!block) return { exists: true, hasBlock: false, drifted: false };
|
|
5378
|
-
const
|
|
5597
|
+
const hash2 = hashText(block.body);
|
|
5379
5598
|
return {
|
|
5380
5599
|
exists: true,
|
|
5381
5600
|
hasBlock: true,
|
|
5382
|
-
hash,
|
|
5601
|
+
hash: hash2,
|
|
5383
5602
|
markerHash: block.markerHash,
|
|
5384
|
-
drifted:
|
|
5603
|
+
drifted: hash2 !== block.markerHash
|
|
5385
5604
|
};
|
|
5386
5605
|
}
|
|
5387
5606
|
async function writeManagedInstructionBlock(sourcePath, destPath, selector, transport, options = {}) {
|
|
@@ -5420,12 +5639,12 @@ function managedInstructionSelector(selector, artifactType, artifactName) {
|
|
|
5420
5639
|
}
|
|
5421
5640
|
function renderManagedInstructionBlock(selector, source) {
|
|
5422
5641
|
const body = managedBlockBody(source);
|
|
5423
|
-
const
|
|
5642
|
+
const hash2 = hashText(body);
|
|
5424
5643
|
return {
|
|
5425
|
-
block: `<!-- BEGIN openpack:include ${selector} sha256:${
|
|
5644
|
+
block: `<!-- BEGIN openpack:include ${selector} sha256:${hash2} -->
|
|
5426
5645
|
${body}<!-- END openpack:include ${selector} -->
|
|
5427
5646
|
`,
|
|
5428
|
-
hash
|
|
5647
|
+
hash: hash2
|
|
5429
5648
|
};
|
|
5430
5649
|
}
|
|
5431
5650
|
function upsertManagedInstructionBlock(content, selector, block, options) {
|
|
@@ -5507,7 +5726,7 @@ function referencesAgentsMd(content, claudePath, agentsPath) {
|
|
|
5507
5726
|
const cleaned = referenced.trim().replace(/^["']|["']$/g, "");
|
|
5508
5727
|
if (!/AGENTS\.md$/i.test(cleaned)) continue;
|
|
5509
5728
|
const candidate = cleaned.startsWith("/") ? cleaned : join12(claudeDir, cleaned);
|
|
5510
|
-
if (
|
|
5729
|
+
if (relative8(dirname13(agentsPath), candidate).replaceAll("\\", "/") === "AGENTS.md") return true;
|
|
5511
5730
|
if (candidate === agentsPath) return true;
|
|
5512
5731
|
}
|
|
5513
5732
|
return false;
|
|
@@ -5564,8 +5783,8 @@ __export(apply_exports, {
|
|
|
5564
5783
|
uninstall: () => uninstall
|
|
5565
5784
|
});
|
|
5566
5785
|
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";
|
|
5786
|
+
import { createHash as createHash11, randomUUID as randomUUID3 } from "crypto";
|
|
5787
|
+
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
5788
|
import { tmpdir as tmpdir4 } from "os";
|
|
5570
5789
|
import { basename as basename3, dirname as dirname14, join as join13, resolve as resolve16 } from "path";
|
|
5571
5790
|
import { promisify as promisify4 } from "util";
|
|
@@ -5838,20 +6057,35 @@ async function commitManifestMetadataJournal(journal, transport = localTransport
|
|
|
5838
6057
|
async function commitJournalState(journal, transport, entries, now) {
|
|
5839
6058
|
const manifest = withManifestRevision({
|
|
5840
6059
|
...journal.manifest,
|
|
5841
|
-
generatedAt: now,
|
|
6060
|
+
generatedAt: journal.metadataAudit || journal.metadataGuards ? journal.createdAt : now,
|
|
5842
6061
|
entries: entries ? entries.sort((a, b) => a.path.localeCompare(b.path)) : journal.manifest.entries
|
|
5843
6062
|
});
|
|
5844
|
-
if (journal.
|
|
6063
|
+
if (journal.metadataGuards) await assertMetadataStateGuards(journal, manifest, journal.metadataGuards, transport);
|
|
6064
|
+
if (journal.metadataAudit) {
|
|
6065
|
+
await assertMetadataAuditGuards(journal, manifest, transport);
|
|
6066
|
+
for (const file of journal.metadataAudit.auditFiles) {
|
|
6067
|
+
const path = metadataAuditPath(journal.metadataAudit, file.path);
|
|
6068
|
+
declareMutationPath(path);
|
|
6069
|
+
await writeAuditFileExclusive(path, file.content);
|
|
6070
|
+
}
|
|
6071
|
+
await assertMetadataAuditGuards(journal, manifest, transport);
|
|
6072
|
+
}
|
|
6073
|
+
if (journal.mode === "uninstall" && manifest.entries.length === 0 && !journal.metadataAudit) {
|
|
5845
6074
|
await removeStateFiles(journal.targetRoot, journal.adapter, transport, {
|
|
5846
6075
|
installationType: journal.installationType,
|
|
5847
6076
|
stateKey: journal.stateKey
|
|
5848
6077
|
});
|
|
6078
|
+
if (journal.metadataGuards) await syncMetadataPath(metadataDir(journal.targetRoot), true);
|
|
5849
6079
|
} else {
|
|
5850
6080
|
if (journal.sourceLock) await writeSourceLock(journal.targetRoot, journal.adapter, journal.sourceLock, transport, {
|
|
5851
6081
|
installationType: journal.installationType,
|
|
5852
6082
|
stateKey: journal.stateKey
|
|
5853
6083
|
});
|
|
5854
6084
|
await writeInstallManifest(manifest, transport);
|
|
6085
|
+
if (journal.metadataAudit || journal.metadataGuards) await syncMetadataPath(installManifestPath(journal.targetRoot, journal.adapter, {
|
|
6086
|
+
installationType: journal.installationType,
|
|
6087
|
+
stateKey: journal.stateKey
|
|
6088
|
+
}));
|
|
5855
6089
|
}
|
|
5856
6090
|
if (journal.graphLockPath && journal.graphLock) await writeGraphLock(journal.graphLockPath, journal.graphLock);
|
|
5857
6091
|
if (journal.stateRemoveKey && journal.stateRemoveKey !== journal.stateKey) {
|
|
@@ -5869,6 +6103,8 @@ async function commitJournalState(journal, transport, entries, now) {
|
|
|
5869
6103
|
await writeJsonAtomic(journal.workspaceConfigPath, journal.workspaceConfig);
|
|
5870
6104
|
}
|
|
5871
6105
|
const verifiedManifest = await assertCommittedJournalState(journal, manifest, transport);
|
|
6106
|
+
if (journal.metadataAudit) await assertMetadataAuditGuards(journal, manifest, transport, true);
|
|
6107
|
+
if (journal.metadataGuards) await assertMetadataStateGuards(journal, manifest, journal.metadataGuards, transport, true);
|
|
5872
6108
|
await removeApplyJournal(journal.targetRoot, journal.adapter, transport, {
|
|
5873
6109
|
installationType: journal.installationType,
|
|
5874
6110
|
stateKey: journal.stateKey
|
|
@@ -5884,7 +6120,7 @@ async function commitJournalState(journal, transport, entries, now) {
|
|
|
5884
6120
|
async function assertCommittedJournalState(journal, expectedManifest, transport) {
|
|
5885
6121
|
const scope = { installationType: journal.installationType, stateKey: journal.stateKey };
|
|
5886
6122
|
const actualManifest = await readInstallManifest(journal.targetRoot, journal.adapter, transport, scope);
|
|
5887
|
-
if (journal.mode === "uninstall" && expectedManifest.entries.length === 0) {
|
|
6123
|
+
if (journal.mode === "uninstall" && expectedManifest.entries.length === 0 && !journal.metadataAudit) {
|
|
5888
6124
|
if (actualManifest) throw new Error("Uninstall postcheck found an install manifest that should have been removed.");
|
|
5889
6125
|
} else if (!actualManifest) {
|
|
5890
6126
|
throw new Error(
|
|
@@ -5924,6 +6160,88 @@ async function assertCommittedJournalState(journal, expectedManifest, transport)
|
|
|
5924
6160
|
}
|
|
5925
6161
|
return actualManifest;
|
|
5926
6162
|
}
|
|
6163
|
+
async function assertMetadataAuditGuards(journal, after, transport, requireAfter = false) {
|
|
6164
|
+
const audit = journal.metadataAudit;
|
|
6165
|
+
const sha = /^[a-f0-9]{64}$/;
|
|
6166
|
+
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) {
|
|
6167
|
+
throw new Error("Invalid metadata-audit transaction; no state was committed.");
|
|
6168
|
+
}
|
|
6169
|
+
await assertMetadataStateGuards(journal, after, audit, transport, requireAfter);
|
|
6170
|
+
for (const file of audit.auditFiles) {
|
|
6171
|
+
const path = metadataAuditPath(audit, file.path);
|
|
6172
|
+
await assertNoSymlinkPath(path);
|
|
6173
|
+
if (!sha.test(file.sha256) || createHash11("sha256").update(file.content).digest("hex") !== file.sha256) {
|
|
6174
|
+
throw new Error("Metadata-audit content digest is invalid.");
|
|
6175
|
+
}
|
|
6176
|
+
try {
|
|
6177
|
+
if (await readFile18(path, "utf8") !== file.content) throw new Error(`Metadata-audit file changed: ${path}`);
|
|
6178
|
+
} catch (error) {
|
|
6179
|
+
if (error.code !== "ENOENT" || requireAfter) throw error;
|
|
6180
|
+
}
|
|
6181
|
+
}
|
|
6182
|
+
}
|
|
6183
|
+
async function assertMetadataStateGuards(journal, after, guards, transport, requireAfter = false) {
|
|
6184
|
+
const sha = /^[a-f0-9]{64}$/;
|
|
6185
|
+
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) {
|
|
6186
|
+
throw new Error("Invalid guarded metadata transaction; no state was committed.");
|
|
6187
|
+
}
|
|
6188
|
+
await assertNoSymlinkPath(journal.targetRoot);
|
|
6189
|
+
await assertNoSymlinkPath(metadataDir(journal.targetRoot));
|
|
6190
|
+
const inventory = await listInstallManifests(journal.targetRoot, void 0, transport);
|
|
6191
|
+
for (const item of inventory) await assertNoSymlinkPath(item.path);
|
|
6192
|
+
const before = new Map(guards.inventory.map((item) => [item.stateKey, item.revision]));
|
|
6193
|
+
if (before.size !== guards.inventory.length || [...before.values()].some((revision) => !sha.test(revision)) || (before.get(journal.stateKey) ?? null) !== journal.baseRevision) {
|
|
6194
|
+
throw new Error("Invalid guarded metadata inventory baseline.");
|
|
6195
|
+
}
|
|
6196
|
+
const next = new Map(before);
|
|
6197
|
+
if (after.entries.length === 0 && !journal.metadataAudit) next.delete(journal.stateKey);
|
|
6198
|
+
else next.set(journal.stateKey, after.revision);
|
|
6199
|
+
const actual = new Map(inventory.map((item) => [item.stateKey, item.manifest.revision]));
|
|
6200
|
+
const equals = (expected) => actual.size === expected.size && [...expected].every(([key, revision]) => actual.get(key) === revision);
|
|
6201
|
+
if (!equals(next) && (requireAfter || !equals(before))) {
|
|
6202
|
+
throw new Error("Metadata inventory/manifest CAS failed; concurrent state was preserved.");
|
|
6203
|
+
}
|
|
6204
|
+
for (const guard of guards.runtime) {
|
|
6205
|
+
const path = metadataRuntimePath(journal.targetRoot, guard.path);
|
|
6206
|
+
if (!sha.test(guard.hash) || !sha.test(guard.snapshotDigest) || await ownershipRuntimeSnapshot(path) !== guard.snapshotDigest || await transport.hashPath(path) !== guard.hash) {
|
|
6207
|
+
throw new Error(`Metadata runtime CAS failed at ${guard.path}; runtime was preserved.`);
|
|
6208
|
+
}
|
|
6209
|
+
if (guard.managedBlock) {
|
|
6210
|
+
const current = exactManagedInstructionBlockSnapshot(await transport.readFile(path), guard.managedBlock.selector);
|
|
6211
|
+
if (canonicalJson5(current) !== canonicalJson5(guard.managedBlock)) {
|
|
6212
|
+
throw new Error(`Managed instruction snapshot CAS failed at ${guard.path}.`);
|
|
6213
|
+
}
|
|
6214
|
+
}
|
|
6215
|
+
}
|
|
6216
|
+
}
|
|
6217
|
+
async function writeAuditFileExclusive(path, content) {
|
|
6218
|
+
await assertNoSymlinkPath(path);
|
|
6219
|
+
await mkdir11(dirname14(path), { recursive: true });
|
|
6220
|
+
await syncMetadataPath(dirname14(dirname14(path)), true);
|
|
6221
|
+
const temporary = `${path}.pending-${randomUUID3()}`;
|
|
6222
|
+
try {
|
|
6223
|
+
const handle = await open4(temporary, "wx", 384);
|
|
6224
|
+
try {
|
|
6225
|
+
await handle.writeFile(content, "utf8");
|
|
6226
|
+
await handle.sync();
|
|
6227
|
+
} finally {
|
|
6228
|
+
await handle.close();
|
|
6229
|
+
}
|
|
6230
|
+
try {
|
|
6231
|
+
await link2(temporary, path);
|
|
6232
|
+
} catch (error) {
|
|
6233
|
+
if (error.code !== "EEXIST" || await readFile18(path, "utf8") !== content) throw error;
|
|
6234
|
+
}
|
|
6235
|
+
const directory = await open4(dirname14(path), "r");
|
|
6236
|
+
try {
|
|
6237
|
+
await directory.sync();
|
|
6238
|
+
} finally {
|
|
6239
|
+
await directory.close();
|
|
6240
|
+
}
|
|
6241
|
+
} finally {
|
|
6242
|
+
await rm7(temporary, { force: true });
|
|
6243
|
+
}
|
|
6244
|
+
}
|
|
5927
6245
|
function canonicalJson5(value) {
|
|
5928
6246
|
if (Array.isArray(value)) return `[${value.map((item) => canonicalJson5(item === void 0 ? null : item)).join(",")}]`;
|
|
5929
6247
|
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
@@ -5973,13 +6291,13 @@ async function applyOperation(operation, context) {
|
|
|
5973
6291
|
}
|
|
5974
6292
|
if (operation.mode === managedInstructionBlockMode) {
|
|
5975
6293
|
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 ${
|
|
6294
|
+
const hash3 = await writeManagedInstructionBlock(operation.sourcePath, operation.destPath, selector, transport, managedBlockMutationOptions(operation));
|
|
6295
|
+
if (hash3 !== operation.desiredHash) {
|
|
6296
|
+
throw new Error(`Managed block hash verification failed for ${operation.relativeDestPath}: expected ${operation.desiredHash}, got ${hash3}`);
|
|
5979
6297
|
}
|
|
5980
6298
|
return manifestEntryForOperation(operation, {
|
|
5981
6299
|
now,
|
|
5982
|
-
hash:
|
|
6300
|
+
hash: hash3,
|
|
5983
6301
|
sourceHash: operation.desiredHash,
|
|
5984
6302
|
graphLockDigest: context.graphLockDigest
|
|
5985
6303
|
});
|
|
@@ -5997,13 +6315,13 @@ async function applyOperation(operation, context) {
|
|
|
5997
6315
|
} else {
|
|
5998
6316
|
await transport.atomicCopy(operation.sourcePath, operation.destPath, operation.kind);
|
|
5999
6317
|
}
|
|
6000
|
-
const
|
|
6001
|
-
if (!operation.mergeStrategy &&
|
|
6002
|
-
throw new Error(`Hash verification failed for ${operation.relativeDestPath}: expected ${operation.desiredHash}, got ${
|
|
6318
|
+
const hash2 = await transport.hashPath(operation.destPath);
|
|
6319
|
+
if (!operation.mergeStrategy && hash2 !== operation.desiredHash) {
|
|
6320
|
+
throw new Error(`Hash verification failed for ${operation.relativeDestPath}: expected ${operation.desiredHash}, got ${hash2}`);
|
|
6003
6321
|
}
|
|
6004
6322
|
return manifestEntryForOperation(operation, {
|
|
6005
6323
|
now,
|
|
6006
|
-
hash,
|
|
6324
|
+
hash: hash2,
|
|
6007
6325
|
sourceHash: operation.desiredHash,
|
|
6008
6326
|
graphLockDigest: context.graphLockDigest
|
|
6009
6327
|
});
|
|
@@ -6599,6 +6917,7 @@ var init_apply = __esm({
|
|
|
6599
6917
|
init_openclaw_json_merge();
|
|
6600
6918
|
init_manifest2();
|
|
6601
6919
|
init_path_safety();
|
|
6920
|
+
init_paths();
|
|
6602
6921
|
init_transaction();
|
|
6603
6922
|
init_toml_merge();
|
|
6604
6923
|
init_yaml_merge();
|
|
@@ -6647,11 +6966,11 @@ var init_identify = __esm({
|
|
|
6647
6966
|
});
|
|
6648
6967
|
|
|
6649
6968
|
// src/cli/index.ts
|
|
6650
|
-
import { createHash as
|
|
6969
|
+
import { createHash as createHash28 } from "crypto";
|
|
6651
6970
|
import { existsSync } from "fs";
|
|
6652
|
-
import { mkdir as
|
|
6971
|
+
import { mkdir as mkdir27, mkdtemp as mkdtemp6, readFile as readFile45, realpath as realpath4, rm as rm17, writeFile as writeFile24 } from "fs/promises";
|
|
6653
6972
|
import { homedir as homedir18, tmpdir as tmpdir7 } from "os";
|
|
6654
|
-
import { basename as basename24, dirname as dirname37, isAbsolute as isAbsolute14, join as
|
|
6973
|
+
import { basename as basename24, dirname as dirname37, isAbsolute as isAbsolute14, join as join61, relative as relative19, resolve as resolve44 } from "path";
|
|
6655
6974
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
6656
6975
|
import { Command } from "commander";
|
|
6657
6976
|
|
|
@@ -6680,8 +6999,8 @@ async function loadProgrammaticAdapter(modulePath, options) {
|
|
|
6680
6999
|
if (!stats.isFile()) {
|
|
6681
7000
|
throw new Error(`Programmatic adapter module is not a file: ${resolvedPath}`);
|
|
6682
7001
|
}
|
|
6683
|
-
const
|
|
6684
|
-
const importPath = extname(resolvedPath) === ".ts" ? await transpileTypeScriptAdapter(resolvedPath,
|
|
7002
|
+
const hash2 = await hashPath(resolvedPath);
|
|
7003
|
+
const importPath = extname(resolvedPath) === ".ts" ? await transpileTypeScriptAdapter(resolvedPath, hash2) : resolvedPath;
|
|
6685
7004
|
const imported = await import(`${pathToFileURL(importPath).href}?agentwheel=${Date.now()}`);
|
|
6686
7005
|
const candidate = imported.adapter;
|
|
6687
7006
|
if (!candidate || typeof candidate !== "object") {
|
|
@@ -6690,7 +7009,7 @@ async function loadProgrammaticAdapter(modulePath, options) {
|
|
|
6690
7009
|
const parsed = adapterSchema.parse(candidate);
|
|
6691
7010
|
const runtime = {
|
|
6692
7011
|
modulePath: resolvedPath,
|
|
6693
|
-
hash,
|
|
7012
|
+
hash: hash2,
|
|
6694
7013
|
capabilities: Array.isArray(candidate.capabilities) ? candidate.capabilities.filter((item) => typeof item === "string") : [],
|
|
6695
7014
|
plan: typeof candidate.plan === "function" ? candidate.plan.bind(candidate) : void 0,
|
|
6696
7015
|
apply: typeof candidate.apply === "function" ? candidate.apply.bind(candidate) : void 0,
|
|
@@ -6698,7 +7017,7 @@ async function loadProgrammaticAdapter(modulePath, options) {
|
|
|
6698
7017
|
};
|
|
6699
7018
|
return { ...parsed, programmatic: runtime };
|
|
6700
7019
|
}
|
|
6701
|
-
async function transpileTypeScriptAdapter(modulePath,
|
|
7020
|
+
async function transpileTypeScriptAdapter(modulePath, hash2) {
|
|
6702
7021
|
const source = await readFile3(modulePath, "utf8");
|
|
6703
7022
|
const output = ts.transpileModule(source, {
|
|
6704
7023
|
compilerOptions: {
|
|
@@ -6710,7 +7029,7 @@ async function transpileTypeScriptAdapter(modulePath, hash) {
|
|
|
6710
7029
|
},
|
|
6711
7030
|
fileName: modulePath
|
|
6712
7031
|
});
|
|
6713
|
-
const outPath = join2(tmpdir(), `agentwheel-adapter-${
|
|
7032
|
+
const outPath = join2(tmpdir(), `agentwheel-adapter-${hash2}.mjs`);
|
|
6714
7033
|
await writeFile2(outPath, output.outputText, "utf8");
|
|
6715
7034
|
return outPath;
|
|
6716
7035
|
}
|
|
@@ -6739,11 +7058,11 @@ init_manifest2();
|
|
|
6739
7058
|
// src/install/plan.ts
|
|
6740
7059
|
init_adapter();
|
|
6741
7060
|
init_manifest();
|
|
6742
|
-
import { basename as basename8, join as join24, relative as
|
|
7061
|
+
import { basename as basename8, join as join24, relative as relative9 } from "path";
|
|
6743
7062
|
|
|
6744
7063
|
// src/staging/codex-subagents.ts
|
|
6745
7064
|
init_fs();
|
|
6746
|
-
import { mkdir as
|
|
7065
|
+
import { mkdir as mkdir12, readFile as readFile19, writeFile as writeFile13 } from "fs/promises";
|
|
6747
7066
|
import { basename as basename4, dirname as dirname15, join as join14 } from "path";
|
|
6748
7067
|
import { parse as parseYaml } from "yaml";
|
|
6749
7068
|
var requiredCodexAgentFields = ["name", "description", "developer_instructions"];
|
|
@@ -6790,7 +7109,7 @@ async function renderCodexSubagent(artifact, stageRoot) {
|
|
|
6790
7109
|
}
|
|
6791
7110
|
const markdown = await readFile19(markdownPath, "utf8");
|
|
6792
7111
|
const toml = markdownToCodexAgentToml(agentName, markdown);
|
|
6793
|
-
await
|
|
7112
|
+
await mkdir12(dirname15(renderedPath), { recursive: true });
|
|
6794
7113
|
await writeFile13(renderedPath, toml, "utf8");
|
|
6795
7114
|
return {
|
|
6796
7115
|
...artifact,
|
|
@@ -6872,7 +7191,7 @@ function escapeRegExp2(value) {
|
|
|
6872
7191
|
|
|
6873
7192
|
// src/staging/copilot-artifacts.ts
|
|
6874
7193
|
init_fs();
|
|
6875
|
-
import { mkdir as
|
|
7194
|
+
import { mkdir as mkdir13, readFile as readFile20, writeFile as writeFile14 } from "fs/promises";
|
|
6876
7195
|
import { basename as basename5, dirname as dirname16, join as join15 } from "path";
|
|
6877
7196
|
async function renderCopilotArtifacts(artifacts, stageRoot, adapter) {
|
|
6878
7197
|
if (adapter?.name !== "copilot") return artifacts;
|
|
@@ -6904,7 +7223,7 @@ async function renderCopilotSubagent(artifact, stageRoot) {
|
|
|
6904
7223
|
throw new Error(`Copilot subagent ${artifact.relativePath} must be a .md file or directory containing AGENTS.md.`);
|
|
6905
7224
|
}
|
|
6906
7225
|
const markdown = await readFile20(markdownPath, "utf8");
|
|
6907
|
-
await
|
|
7226
|
+
await mkdir13(dirname16(renderedPath), { recursive: true });
|
|
6908
7227
|
await writeFile14(renderedPath, ensureCopilotAgentDescription(agentName, markdown), "utf8");
|
|
6909
7228
|
return {
|
|
6910
7229
|
...artifact,
|
|
@@ -6959,7 +7278,7 @@ function yamlString(value) {
|
|
|
6959
7278
|
|
|
6960
7279
|
// src/staging/openclaw-subagents.ts
|
|
6961
7280
|
init_fs();
|
|
6962
|
-
import { mkdir as
|
|
7281
|
+
import { mkdir as mkdir14, readFile as readFile21, writeFile as writeFile15 } from "fs/promises";
|
|
6963
7282
|
import { basename as basename6, dirname as dirname17, join as join16 } from "path";
|
|
6964
7283
|
async function renderOpenClawSubagents(artifacts, stageRoot, adapter) {
|
|
6965
7284
|
if (adapter?.name !== "openclaw") return artifacts;
|
|
@@ -6994,7 +7313,7 @@ async function renderOpenClawSubagent(artifact, stageRoot) {
|
|
|
6994
7313
|
|
|
6995
7314
|
${parsed.description ?? `OpenClaw subagent ${agentId}.`}`;
|
|
6996
7315
|
const renderedPath = join16(stageRoot, ".agentwheel-rendered", "openclaw-subagents", agentId, "AGENTS.md");
|
|
6997
|
-
await
|
|
7316
|
+
await mkdir14(dirname17(renderedPath), { recursive: true });
|
|
6998
7317
|
await writeFile15(renderedPath, `${body}
|
|
6999
7318
|
`, "utf8");
|
|
7000
7319
|
const renderedDir = dirname17(renderedPath);
|
|
@@ -7280,59 +7599,7 @@ init_transport();
|
|
|
7280
7599
|
|
|
7281
7600
|
// src/validation/adapter-targets.ts
|
|
7282
7601
|
init_adapter();
|
|
7283
|
-
|
|
7284
|
-
// src/model/selection.ts
|
|
7285
|
-
init_artifact();
|
|
7286
|
-
function artifactSelectorKey(artifact) {
|
|
7287
|
-
return `${artifact.type}/${artifact.name}`;
|
|
7288
|
-
}
|
|
7289
|
-
function artifactSelectorAliases(artifact) {
|
|
7290
|
-
const primary = artifactSelectorKey(artifact);
|
|
7291
|
-
if (artifact.type !== "subagents") return [primary];
|
|
7292
|
-
const baseName = subagentBaseName(artifact.name);
|
|
7293
|
-
if (baseName === artifact.name) return [primary];
|
|
7294
|
-
return [primary, `subagents/${baseName}`];
|
|
7295
|
-
}
|
|
7296
|
-
function normalizeArtifactSelectors(select, legacySkills) {
|
|
7297
|
-
const selected = [
|
|
7298
|
-
...select ?? [],
|
|
7299
|
-
...(legacySkills ?? []).map((name) => `skills/${name}`)
|
|
7300
|
-
].flatMap(splitSelectorList);
|
|
7301
|
-
if (selected.length === 0) return void 0;
|
|
7302
|
-
return [...new Set(selected.map(parseArtifactSelector))];
|
|
7303
|
-
}
|
|
7304
|
-
function filterArtifactsBySelection(artifacts, selectors, legacySkills, options = {}) {
|
|
7305
|
-
const selected = normalizeArtifactSelectors(selectors, legacySkills);
|
|
7306
|
-
if (!selected?.length) return artifacts;
|
|
7307
|
-
const selectedSet = new Set(selected);
|
|
7308
|
-
const available = new Set((options.validationArtifacts ?? artifacts).flatMap(artifactSelectorAliases));
|
|
7309
|
-
const missing = selected.filter((selector) => !available.has(selector));
|
|
7310
|
-
if (missing.length > 0) {
|
|
7311
|
-
throw new Error(`Selected artifact not found in package: ${missing.join(", ")}`);
|
|
7312
|
-
}
|
|
7313
|
-
return artifacts.filter((artifact) => artifact.required || artifactSelectorAliases(artifact).some((selector) => selectedSet.has(selector)));
|
|
7314
|
-
}
|
|
7315
|
-
function splitSelectorList(value) {
|
|
7316
|
-
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
7317
|
-
}
|
|
7318
|
-
function parseArtifactSelector(value) {
|
|
7319
|
-
const slash = value.indexOf("/");
|
|
7320
|
-
if (slash <= 0 || slash === value.length - 1) {
|
|
7321
|
-
throw new Error(`Invalid artifact selector: ${value}. Expected <type>/<name>.`);
|
|
7322
|
-
}
|
|
7323
|
-
const type = value.slice(0, slash);
|
|
7324
|
-
const name = value.slice(slash + 1);
|
|
7325
|
-
const parsedType = artifactTypeSchema.safeParse(type);
|
|
7326
|
-
if (!parsedType.success) {
|
|
7327
|
-
throw new Error(`Invalid artifact selector type: ${type}`);
|
|
7328
|
-
}
|
|
7329
|
-
return `${parsedType.data}/${name}`;
|
|
7330
|
-
}
|
|
7331
|
-
function subagentBaseName(name) {
|
|
7332
|
-
return name.replace(/\.agent\.md$/i, "").replace(/\.toml$/i, "").replace(/\.md$/i, "");
|
|
7333
|
-
}
|
|
7334
|
-
|
|
7335
|
-
// src/validation/adapter-targets.ts
|
|
7602
|
+
init_selection();
|
|
7336
7603
|
function filterArtifactsByAdapterTargets(artifacts, adapter, installationType, options = {}) {
|
|
7337
7604
|
const skipped = [];
|
|
7338
7605
|
const installable = artifacts.filter((artifact) => {
|
|
@@ -7375,10 +7642,11 @@ function skipWarning(selector, adapter, installationType, artifactType, support)
|
|
|
7375
7642
|
|
|
7376
7643
|
// src/validation/artifacts.ts
|
|
7377
7644
|
init_adapter();
|
|
7645
|
+
init_selection();
|
|
7646
|
+
init_fs();
|
|
7378
7647
|
import { readFile as readFile24 } from "fs/promises";
|
|
7379
7648
|
import { basename as basename7, join as join23 } from "path";
|
|
7380
7649
|
import { parseDocument as parseDocument2 } from "yaml";
|
|
7381
|
-
init_fs();
|
|
7382
7650
|
var behavioralRuleFormats = ["markdown-rule", "claude-markdown-rule", "copilot-instruction-rule"];
|
|
7383
7651
|
var pluginFormats = ["claude-plugin", "codex-plugin", "hermes-plugin", "copilot-plugin", "openclaw-plugin", "openclaw-clawhub-plugin"];
|
|
7384
7652
|
async function filterArtifactsByInstallFormat(artifacts, adapter, installationType, options = {}) {
|
|
@@ -8497,7 +8765,7 @@ async function operationForArtifact(artifact, adapter, targetRoot, installationT
|
|
|
8497
8765
|
kind: "file",
|
|
8498
8766
|
sourcePath: artifact.stagedPath ?? artifact.sourcePath,
|
|
8499
8767
|
destPath: destPath2,
|
|
8500
|
-
relativeDestPath:
|
|
8768
|
+
relativeDestPath: relative9(targetRoot, destPath2).replaceAll("\\", "/"),
|
|
8501
8769
|
desiredHash: artifact.hash,
|
|
8502
8770
|
reason: "destination missing",
|
|
8503
8771
|
channel: artifact.channel ?? "managed",
|
|
@@ -8515,7 +8783,7 @@ async function operationForArtifact(artifact, adapter, targetRoot, installationT
|
|
|
8515
8783
|
kind: artifact.kind,
|
|
8516
8784
|
sourcePath: artifact.stagedPath ?? artifact.sourcePath,
|
|
8517
8785
|
destPath,
|
|
8518
|
-
relativeDestPath:
|
|
8786
|
+
relativeDestPath: relative9(targetRoot, destPath).replaceAll("\\", "/"),
|
|
8519
8787
|
desiredHash: artifact.hash,
|
|
8520
8788
|
reason: "destination missing",
|
|
8521
8789
|
channel: artifact.channel ?? "managed",
|
|
@@ -9512,14 +9780,14 @@ async function writeMermaidAsset(response) {
|
|
|
9512
9780
|
"content-type": "application/javascript; charset=utf-8",
|
|
9513
9781
|
"cache-control": "public, max-age=3600"
|
|
9514
9782
|
});
|
|
9515
|
-
await new Promise((
|
|
9783
|
+
await new Promise((resolve45) => {
|
|
9516
9784
|
const stream = createReadStream(assetPath);
|
|
9517
9785
|
stream.on("error", () => {
|
|
9518
9786
|
if (!response.headersSent) writeText(response, 404, "Mermaid asset unavailable.\n");
|
|
9519
9787
|
else response.destroy();
|
|
9520
|
-
|
|
9788
|
+
resolve45();
|
|
9521
9789
|
});
|
|
9522
|
-
stream.on("end",
|
|
9790
|
+
stream.on("end", resolve45);
|
|
9523
9791
|
stream.pipe(response);
|
|
9524
9792
|
});
|
|
9525
9793
|
}
|
|
@@ -9546,14 +9814,14 @@ function escapeHtml2(value) {
|
|
|
9546
9814
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
9547
9815
|
}
|
|
9548
9816
|
function listen(server, port, bind) {
|
|
9549
|
-
return new Promise((
|
|
9817
|
+
return new Promise((resolve45, reject) => {
|
|
9550
9818
|
const onError = (error) => {
|
|
9551
9819
|
server.off("listening", onListening);
|
|
9552
9820
|
reject(error);
|
|
9553
9821
|
};
|
|
9554
9822
|
const onListening = () => {
|
|
9555
9823
|
server.off("error", onError);
|
|
9556
|
-
|
|
9824
|
+
resolve45();
|
|
9557
9825
|
};
|
|
9558
9826
|
server.once("error", onError);
|
|
9559
9827
|
server.once("listening", onListening);
|
|
@@ -9561,7 +9829,7 @@ function listen(server, port, bind) {
|
|
|
9561
9829
|
});
|
|
9562
9830
|
}
|
|
9563
9831
|
function waitForShutdown(server, beforeClose) {
|
|
9564
|
-
return new Promise((
|
|
9832
|
+
return new Promise((resolve45) => {
|
|
9565
9833
|
let closing = false;
|
|
9566
9834
|
const shutdown = () => {
|
|
9567
9835
|
if (closing) return;
|
|
@@ -9569,7 +9837,7 @@ function waitForShutdown(server, beforeClose) {
|
|
|
9569
9837
|
process.off("SIGINT", shutdown);
|
|
9570
9838
|
process.off("SIGTERM", shutdown);
|
|
9571
9839
|
beforeClose();
|
|
9572
|
-
server.close(() =>
|
|
9840
|
+
server.close(() => resolve45());
|
|
9573
9841
|
};
|
|
9574
9842
|
process.once("SIGINT", shutdown);
|
|
9575
9843
|
process.once("SIGTERM", shutdown);
|
|
@@ -9579,7 +9847,7 @@ function waitForShutdown(server, beforeClose) {
|
|
|
9579
9847
|
process.off("SIGINT", shutdown);
|
|
9580
9848
|
process.off("SIGTERM", shutdown);
|
|
9581
9849
|
beforeClose();
|
|
9582
|
-
|
|
9850
|
+
resolve45();
|
|
9583
9851
|
});
|
|
9584
9852
|
});
|
|
9585
9853
|
}
|
|
@@ -9597,7 +9865,7 @@ function errorMessage2(error) {
|
|
|
9597
9865
|
}
|
|
9598
9866
|
|
|
9599
9867
|
// src/source/clawhub.ts
|
|
9600
|
-
import { mkdir as
|
|
9868
|
+
import { mkdir as mkdir15, rm as rm8, writeFile as writeFile16 } from "fs/promises";
|
|
9601
9869
|
import { basename as basename10, dirname as dirname18, join as join28, resolve as resolve18 } from "path";
|
|
9602
9870
|
|
|
9603
9871
|
// src/model/package.ts
|
|
@@ -9791,8 +10059,8 @@ init_fs();
|
|
|
9791
10059
|
|
|
9792
10060
|
// src/source/local.ts
|
|
9793
10061
|
import { createHash as createHash13 } from "crypto";
|
|
9794
|
-
import { readdir as
|
|
9795
|
-
import { basename as basename9, join as join27, relative as
|
|
10062
|
+
import { readdir as readdir6, stat as stat5 } from "fs/promises";
|
|
10063
|
+
import { basename as basename9, join as join27, relative as relative10, resolve as resolve17 } from "path";
|
|
9796
10064
|
init_fs();
|
|
9797
10065
|
var LocalSourceDriver = class {
|
|
9798
10066
|
name = "local";
|
|
@@ -9933,23 +10201,23 @@ var LocalSourceDriver = class {
|
|
|
9933
10201
|
};
|
|
9934
10202
|
async function hashLocalSource(root, manifest) {
|
|
9935
10203
|
if (!manifest) return hashPath(root);
|
|
9936
|
-
const
|
|
10204
|
+
const hash2 = createHash13("sha256").update("local-openpack\0");
|
|
9937
10205
|
const manifestPath = await findPackageManifestPath(root, { warnLegacy: false });
|
|
9938
10206
|
if (manifestPath) {
|
|
9939
|
-
|
|
9940
|
-
|
|
9941
|
-
|
|
10207
|
+
hash2.update("manifest\0");
|
|
10208
|
+
hash2.update(relative10(root, manifestPath).replaceAll("\\", "/")).update("\0");
|
|
10209
|
+
hash2.update(await hashPath(manifestPath)).update("\0");
|
|
9942
10210
|
}
|
|
9943
10211
|
const provides = [...manifest.provides].sort((a, b) => `${a.type}\0${a.path}`.localeCompare(`${b.type}\0${b.path}`));
|
|
9944
10212
|
for (const provide of provides) {
|
|
9945
10213
|
const full = join27(root, provide.path);
|
|
9946
10214
|
if (!await pathExists(full)) continue;
|
|
9947
|
-
|
|
9948
|
-
|
|
9949
|
-
|
|
9950
|
-
|
|
10215
|
+
hash2.update("provide\0");
|
|
10216
|
+
hash2.update(provide.type).update("\0");
|
|
10217
|
+
hash2.update(provide.path.replaceAll("\\", "/")).update("\0");
|
|
10218
|
+
hash2.update(await hashPath(full)).update("\0");
|
|
9951
10219
|
}
|
|
9952
|
-
return
|
|
10220
|
+
return hash2.digest("hex");
|
|
9953
10221
|
}
|
|
9954
10222
|
async function firstExisting(paths) {
|
|
9955
10223
|
for (const path of paths) {
|
|
@@ -9958,7 +10226,7 @@ async function firstExisting(paths) {
|
|
|
9958
10226
|
return void 0;
|
|
9959
10227
|
}
|
|
9960
10228
|
async function sortedDirEntries(path) {
|
|
9961
|
-
return (await
|
|
10229
|
+
return (await readdir6(path, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name));
|
|
9962
10230
|
}
|
|
9963
10231
|
async function listFromManifest(root, packageName) {
|
|
9964
10232
|
const manifest = await readPackageManifest(root);
|
|
@@ -10139,7 +10407,7 @@ async function fetchClawHubPackage(fetchImpl, packageName) {
|
|
|
10139
10407
|
throw new Error(`ClawHub lookup failed for ${packageName}`);
|
|
10140
10408
|
}
|
|
10141
10409
|
async function delay(ms) {
|
|
10142
|
-
await new Promise((
|
|
10410
|
+
await new Promise((resolve45) => setTimeout(resolve45, ms));
|
|
10143
10411
|
}
|
|
10144
10412
|
function parseClawHubSource(source) {
|
|
10145
10413
|
if (!source.startsWith(sourcePrefix)) {
|
|
@@ -10171,7 +10439,7 @@ async function writeGeneratedPackage(root, packageInfo) {
|
|
|
10171
10439
|
};
|
|
10172
10440
|
const pluginPath = join28(root, "plugins", pluginId, "clawhub.json");
|
|
10173
10441
|
await rm8(root, { recursive: true, force: true });
|
|
10174
|
-
await
|
|
10442
|
+
await mkdir15(dirname18(pluginPath), { recursive: true });
|
|
10175
10443
|
await writeFile16(join28(root, "openpack.json"), `${JSON.stringify({
|
|
10176
10444
|
schemaVersion: CURRENT_OPENPACK_SCHEMA_VERSION,
|
|
10177
10445
|
name: `clawhub/${name}`,
|
|
@@ -10202,9 +10470,9 @@ function cachePathFor(packageName, cacheRoot) {
|
|
|
10202
10470
|
|
|
10203
10471
|
// src/source/git.ts
|
|
10204
10472
|
import { execFile as execFile5 } from "child_process";
|
|
10205
|
-
import { mkdir as
|
|
10473
|
+
import { mkdir as mkdir17, rename as rename6, rm as rm10, stat as stat7 } from "fs/promises";
|
|
10206
10474
|
import { homedir as homedir7 } from "os";
|
|
10207
|
-
import { basename as basename11, dirname as dirname20, join as join31, resolve as resolve19, sep as
|
|
10475
|
+
import { basename as basename11, dirname as dirname20, join as join31, resolve as resolve19, sep as sep6 } from "path";
|
|
10208
10476
|
import { promisify as promisify5 } from "util";
|
|
10209
10477
|
init_fs();
|
|
10210
10478
|
|
|
@@ -10285,8 +10553,8 @@ function isMissingFile(error) {
|
|
|
10285
10553
|
|
|
10286
10554
|
// src/source/cache.ts
|
|
10287
10555
|
init_fs();
|
|
10288
|
-
import { randomUUID as
|
|
10289
|
-
import { mkdir as
|
|
10556
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
10557
|
+
import { mkdir as mkdir16, readdir as readdir7, readFile as readFile27, rm as rm9, stat as stat6, writeFile as writeFile17 } from "fs/promises";
|
|
10290
10558
|
import { dirname as dirname19, join as join30 } from "path";
|
|
10291
10559
|
var snapshotNamePattern = /^(.*)-([0-9a-f]{12})$/i;
|
|
10292
10560
|
var leaseMarker = ".agentwheel-lease-";
|
|
@@ -10301,11 +10569,11 @@ async function pruneGitCache(cacheRoot, options = {}) {
|
|
|
10301
10569
|
}
|
|
10302
10570
|
async function withGitCacheMaintenanceLock(cacheRoot, timeoutMs, fn) {
|
|
10303
10571
|
const lockPath = join30(cacheRoot, ".maintenance.lock");
|
|
10304
|
-
await
|
|
10572
|
+
await mkdir16(cacheRoot, { recursive: true });
|
|
10305
10573
|
const started = Date.now();
|
|
10306
10574
|
while (true) {
|
|
10307
10575
|
try {
|
|
10308
|
-
await
|
|
10576
|
+
await mkdir16(lockPath);
|
|
10309
10577
|
await writeFile17(
|
|
10310
10578
|
join30(lockPath, "owner.json"),
|
|
10311
10579
|
JSON.stringify({ pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString() }),
|
|
@@ -10318,7 +10586,7 @@ async function withGitCacheMaintenanceLock(cacheRoot, timeoutMs, fn) {
|
|
|
10318
10586
|
if (Date.now() - started > timeoutMs) {
|
|
10319
10587
|
throw new Error(`Timed out waiting for git cache maintenance lock at ${lockPath}`);
|
|
10320
10588
|
}
|
|
10321
|
-
await new Promise((
|
|
10589
|
+
await new Promise((resolve45) => setTimeout(resolve45, 50));
|
|
10322
10590
|
}
|
|
10323
10591
|
}
|
|
10324
10592
|
try {
|
|
@@ -10328,7 +10596,7 @@ async function withGitCacheMaintenanceLock(cacheRoot, timeoutMs, fn) {
|
|
|
10328
10596
|
}
|
|
10329
10597
|
}
|
|
10330
10598
|
async function createGitSnapshotLease(snapshotPath) {
|
|
10331
|
-
const leasePath = `${snapshotPath}${leaseMarker}${process.pid}-${
|
|
10599
|
+
const leasePath = `${snapshotPath}${leaseMarker}${process.pid}-${randomUUID4()}`;
|
|
10332
10600
|
await writeFile17(leasePath, JSON.stringify({ pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString() }), {
|
|
10333
10601
|
encoding: "utf8",
|
|
10334
10602
|
flag: "wx"
|
|
@@ -10341,7 +10609,7 @@ async function releaseGitSnapshotLease(leasePath) {
|
|
|
10341
10609
|
async function removeGeneratedEntries(root, preserveGit) {
|
|
10342
10610
|
let entries;
|
|
10343
10611
|
try {
|
|
10344
|
-
entries = await
|
|
10612
|
+
entries = await readdir7(root, { withFileTypes: true });
|
|
10345
10613
|
} catch {
|
|
10346
10614
|
return;
|
|
10347
10615
|
}
|
|
@@ -10356,7 +10624,7 @@ async function removeGeneratedEntries(root, preserveGit) {
|
|
|
10356
10624
|
}
|
|
10357
10625
|
async function pruneGitCacheUnlocked(cacheRoot, options) {
|
|
10358
10626
|
const referencedCommits = await referencedGraphLockCommits(join30(dirname19(cacheRoot), "locks"));
|
|
10359
|
-
const entries = await
|
|
10627
|
+
const entries = await readdir7(cacheRoot, { withFileTypes: true });
|
|
10360
10628
|
const groups = /* @__PURE__ */ new Map();
|
|
10361
10629
|
for (const entry of entries) {
|
|
10362
10630
|
if (!entry.isDirectory()) continue;
|
|
@@ -10403,7 +10671,7 @@ async function referencedGraphLockCommits(lockRoot) {
|
|
|
10403
10671
|
async function walkGraphLocks(root, visit) {
|
|
10404
10672
|
let entries;
|
|
10405
10673
|
try {
|
|
10406
|
-
entries = await
|
|
10674
|
+
entries = await readdir7(root, { withFileTypes: true });
|
|
10407
10675
|
} catch (error) {
|
|
10408
10676
|
if (isNotFound(error)) return;
|
|
10409
10677
|
throw new Error(`Cannot inspect graph-lock directory ${root}; cache prune aborted: ${errorMessage3(error)}`);
|
|
@@ -10424,7 +10692,7 @@ async function walkGraphLocks(root, visit) {
|
|
|
10424
10692
|
async function hasLiveSnapshotLease(snapshotPath, dryRun) {
|
|
10425
10693
|
const directory = dirname19(snapshotPath);
|
|
10426
10694
|
const prefix = `${snapshotPath.slice(directory.length + 1)}${leaseMarker}`;
|
|
10427
|
-
const entries = await
|
|
10695
|
+
const entries = await readdir7(directory, { withFileTypes: true });
|
|
10428
10696
|
let live = false;
|
|
10429
10697
|
for (const entry of entries) {
|
|
10430
10698
|
if (!entry.isFile() || !entry.name.startsWith(prefix)) continue;
|
|
@@ -10523,10 +10791,10 @@ function normalizeGitSubpath(subpath, source) {
|
|
|
10523
10791
|
}
|
|
10524
10792
|
return normalized;
|
|
10525
10793
|
}
|
|
10526
|
-
function resolveGitRelativeSubpath(declaringSubpath,
|
|
10527
|
-
const joined = posix4.normalize(posix4.join(declaringSubpath ?? ".",
|
|
10794
|
+
function resolveGitRelativeSubpath(declaringSubpath, relative20, source) {
|
|
10795
|
+
const joined = posix4.normalize(posix4.join(declaringSubpath ?? ".", relative20));
|
|
10528
10796
|
if (joined === ".." || joined.startsWith("../")) {
|
|
10529
|
-
throw new Error(`Relative dependency '${
|
|
10797
|
+
throw new Error(`Relative dependency '${relative20}' escapes the git repository it is declared in: ${source}`);
|
|
10530
10798
|
}
|
|
10531
10799
|
return normalizeGitSubpath(joined, source);
|
|
10532
10800
|
}
|
|
@@ -10562,7 +10830,7 @@ var GitSourceDriver = class {
|
|
|
10562
10830
|
return withFilesystemLock(`${resolved.resolvedPath}.lock`, resolved.cacheLockTimeoutMs ?? 3e4, async () => {
|
|
10563
10831
|
const parsed = parseGitSource(resolved.source);
|
|
10564
10832
|
const cacheRoot = dirname20(resolved.resolvedPath);
|
|
10565
|
-
await
|
|
10833
|
+
await mkdir17(cacheRoot, { recursive: true });
|
|
10566
10834
|
await assertOwnedByCurrentUser(cacheRoot);
|
|
10567
10835
|
if (await pathExists(resolved.resolvedPath)) await assertOwnedByCurrentUser(resolved.resolvedPath);
|
|
10568
10836
|
if (!await pathExists(join31(resolved.resolvedPath, ".git"))) {
|
|
@@ -10662,11 +10930,11 @@ async function snapshotCommit(checkoutPath, commit) {
|
|
|
10662
10930
|
const tempIndexPath = `${tempPath}.index`;
|
|
10663
10931
|
await rm10(tempPath, { recursive: true, force: true });
|
|
10664
10932
|
await rm10(tempIndexPath, { force: true });
|
|
10665
|
-
await
|
|
10933
|
+
await mkdir17(tempPath, { recursive: true });
|
|
10666
10934
|
const env2 = { ...process.env, GIT_INDEX_FILE: tempIndexPath };
|
|
10667
10935
|
try {
|
|
10668
10936
|
await git(["-C", checkoutPath, "read-tree", commit], env2);
|
|
10669
|
-
await git(["-C", checkoutPath, "checkout-index", "--all", `--prefix=${resolve19(tempPath)}${
|
|
10937
|
+
await git(["-C", checkoutPath, "checkout-index", "--all", `--prefix=${resolve19(tempPath)}${sep6}`], env2);
|
|
10670
10938
|
await removeGeneratedEntries(tempPath, false);
|
|
10671
10939
|
} catch (error) {
|
|
10672
10940
|
await rm10(tempPath, { recursive: true, force: true });
|
|
@@ -10695,7 +10963,7 @@ async function assertOwnedByCurrentUser(path) {
|
|
|
10695
10963
|
}
|
|
10696
10964
|
|
|
10697
10965
|
// src/source/mcp-registry.ts
|
|
10698
|
-
import { mkdir as
|
|
10966
|
+
import { mkdir as mkdir18, writeFile as writeFile18 } from "fs/promises";
|
|
10699
10967
|
import { basename as basename12, dirname as dirname21, join as join32, resolve as resolve20 } from "path";
|
|
10700
10968
|
init_fs();
|
|
10701
10969
|
var registryBaseUrl = "https://registry.modelcontextprotocol.io/v0.1";
|
|
@@ -10804,7 +11072,7 @@ function isSafeHttpUrl(value) {
|
|
|
10804
11072
|
async function writeGeneratedPackage2(root, server) {
|
|
10805
11073
|
const serverId = installNameFor3(server.serverName);
|
|
10806
11074
|
const mcpPath = join32(root, "mcp", `${serverId}.json`);
|
|
10807
|
-
await
|
|
11075
|
+
await mkdir18(dirname21(mcpPath), { recursive: true });
|
|
10808
11076
|
await writeFile18(join32(root, "openpack.json"), `${JSON.stringify({
|
|
10809
11077
|
schemaVersion: CURRENT_OPENPACK_SCHEMA_VERSION,
|
|
10810
11078
|
name: `mcp-registry/${server.serverName}`,
|
|
@@ -10834,9 +11102,9 @@ function cachePathFor3(serverName, cacheRoot) {
|
|
|
10834
11102
|
// src/source/skillkit.ts
|
|
10835
11103
|
init_cache_identity();
|
|
10836
11104
|
init_fs();
|
|
10837
|
-
import { createHash as createHash14, randomUUID as
|
|
11105
|
+
import { createHash as createHash14, randomUUID as randomUUID5 } from "crypto";
|
|
10838
11106
|
import { execFile as execFile6 } from "child_process";
|
|
10839
|
-
import { cp as cp3, mkdir as
|
|
11107
|
+
import { cp as cp3, mkdir as mkdir19, readFile as readFile28, rename as rename7, rm as rm11 } from "fs/promises";
|
|
10840
11108
|
import { homedir as homedir8 } from "os";
|
|
10841
11109
|
import { basename as basename14, dirname as dirname23, join as join34, resolve as resolve21 } from "path";
|
|
10842
11110
|
import { promisify as promisify6 } from "util";
|
|
@@ -10844,7 +11112,7 @@ import * as defaultSkillKit from "@skillkit/core";
|
|
|
10844
11112
|
|
|
10845
11113
|
// src/source/skill-artifacts.ts
|
|
10846
11114
|
init_fs();
|
|
10847
|
-
import { readdir as
|
|
11115
|
+
import { readdir as readdir8, stat as stat8 } from "fs/promises";
|
|
10848
11116
|
import { basename as basename13, dirname as dirname22, extname as extname2, join as join33 } from "path";
|
|
10849
11117
|
async function artifactsFromSkillPaths(paths, packageName) {
|
|
10850
11118
|
const artifacts = [];
|
|
@@ -10912,7 +11180,7 @@ async function artifactFromSkillPath(item, packageName) {
|
|
|
10912
11180
|
}
|
|
10913
11181
|
async function walk(dir, paths) {
|
|
10914
11182
|
if (!await pathExists(dir)) return;
|
|
10915
|
-
const entries = await
|
|
11183
|
+
const entries = await readdir8(dir, { withFileTypes: true });
|
|
10916
11184
|
if (entries.some((entry) => entry.isFile() && entry.name === "SKILL.md")) {
|
|
10917
11185
|
paths.push({ path: dir });
|
|
10918
11186
|
return;
|
|
@@ -11015,8 +11283,8 @@ var SkillKitSourceDriver = class {
|
|
|
11015
11283
|
if (immutablePath && await pathExists(immutablePath)) {
|
|
11016
11284
|
return { path: immutablePath, commit: requestedCommit, cacheIdentity: requestedCommit };
|
|
11017
11285
|
}
|
|
11018
|
-
await
|
|
11019
|
-
const candidatePath = `${specCachePath}.agentwheel-tmp-${process.pid}-${
|
|
11286
|
+
await mkdir19(dirname23(specCachePath), { recursive: true });
|
|
11287
|
+
const candidatePath = `${specCachePath}.agentwheel-tmp-${process.pid}-${randomUUID5()}`;
|
|
11020
11288
|
const publishPath = `${candidatePath}.publish`;
|
|
11021
11289
|
let result;
|
|
11022
11290
|
try {
|
|
@@ -11171,7 +11439,7 @@ async function checkoutCommit(root, commit) {
|
|
|
11171
11439
|
// src/source/vercel-skills.ts
|
|
11172
11440
|
init_fs();
|
|
11173
11441
|
import { stat as stat9 } from "fs/promises";
|
|
11174
|
-
import { basename as basename15, join as join35, relative as
|
|
11442
|
+
import { basename as basename15, join as join35, relative as relative11, resolve as resolve22 } from "path";
|
|
11175
11443
|
var VercelSkillsSourceDriver = class {
|
|
11176
11444
|
name = "vercel-skills";
|
|
11177
11445
|
git = new GitSourceDriver();
|
|
@@ -11238,7 +11506,7 @@ async function resolveVercelSkillSubpath(root, subpath) {
|
|
|
11238
11506
|
for (const candidate of candidates) {
|
|
11239
11507
|
if (await pathExists(candidate)) return candidate;
|
|
11240
11508
|
}
|
|
11241
|
-
const tried = candidates.map((candidate) =>
|
|
11509
|
+
const tried = candidates.map((candidate) => relative11(root, candidate)).join(", ");
|
|
11242
11510
|
throw new Error(`Vercel skills subpath not found: ${subpath} (tried ${tried})`);
|
|
11243
11511
|
}
|
|
11244
11512
|
function parseVercelSource(source) {
|
|
@@ -11307,8 +11575,8 @@ function getSourceDriver(name = "local") {
|
|
|
11307
11575
|
init_identify();
|
|
11308
11576
|
|
|
11309
11577
|
// 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
|
|
11578
|
+
import { chmod as chmod3, cp as cp5, mkdir as mkdir22, mkdtemp as mkdtemp4, readdir as readdir11, stat as stat12 } from "fs/promises";
|
|
11579
|
+
import { basename as basename19, dirname as dirname27, join as join39, relative as relative13, resolve as resolve25, sep as sep8 } from "path";
|
|
11312
11580
|
import { tmpdir as tmpdir5 } from "os";
|
|
11313
11581
|
init_fs();
|
|
11314
11582
|
|
|
@@ -11316,8 +11584,8 @@ init_fs();
|
|
|
11316
11584
|
init_artifact();
|
|
11317
11585
|
init_fs();
|
|
11318
11586
|
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
|
|
11587
|
+
import { readdir as readdir9, readFile as readFile29, stat as stat10, writeFile as writeFile19 } from "fs/promises";
|
|
11588
|
+
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
11589
|
var includePattern = /<!--\s*openpack:include(\?)?\s+([^>]+?)\s*-->/g;
|
|
11322
11590
|
var escapedIncludePattern = /<!--\s*openpack\\:include(\?)?\s+([^>]+?)\s*-->/g;
|
|
11323
11591
|
var generatedPattern = /<!--\s*(?:BEGIN|END)\s+openpack:include\b/;
|
|
@@ -11527,7 +11795,7 @@ function extractOpenPackIncludeSelectors(content) {
|
|
|
11527
11795
|
function resolvePackageSelector(packageRoot, selector) {
|
|
11528
11796
|
const root = resolve24(packageRoot);
|
|
11529
11797
|
const resolved = resolve24(root, selector);
|
|
11530
|
-
if (resolved !== root && !resolved.startsWith(`${root}${
|
|
11798
|
+
if (resolved !== root && !resolved.startsWith(`${root}${sep7}`)) {
|
|
11531
11799
|
throw new Error(`OpenPack include escapes package root: ${selector}`);
|
|
11532
11800
|
}
|
|
11533
11801
|
return resolved;
|
|
@@ -11542,7 +11810,7 @@ async function markdownFilesForArtifact(artifact) {
|
|
|
11542
11810
|
async function listMarkdownFiles(root) {
|
|
11543
11811
|
const out = [];
|
|
11544
11812
|
async function walk2(dir) {
|
|
11545
|
-
for (const entry of (await
|
|
11813
|
+
for (const entry of (await readdir9(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
11546
11814
|
const full = join36(dir, entry.name);
|
|
11547
11815
|
if (entry.isDirectory()) {
|
|
11548
11816
|
await walk2(full);
|
|
@@ -11578,7 +11846,7 @@ function applyReplacements(content, replacements) {
|
|
|
11578
11846
|
return out + content.slice(cursor);
|
|
11579
11847
|
}
|
|
11580
11848
|
function relativeSelector(root, file) {
|
|
11581
|
-
return
|
|
11849
|
+
return relative12(root, file).replaceAll("\\", "/");
|
|
11582
11850
|
}
|
|
11583
11851
|
function ownerSelector(root, file, nodeId) {
|
|
11584
11852
|
const selector = relativeSelector(root, file);
|
|
@@ -11608,7 +11876,7 @@ function artifactPathMap(artifacts) {
|
|
|
11608
11876
|
|
|
11609
11877
|
// src/staging/customize.ts
|
|
11610
11878
|
init_fs();
|
|
11611
|
-
import { cp as cp4, mkdir as
|
|
11879
|
+
import { cp as cp4, mkdir as mkdir20, readdir as readdir10, readFile as readFile30, writeFile as writeFile20 } from "fs/promises";
|
|
11612
11880
|
import { dirname as dirname25, join as join37 } from "path";
|
|
11613
11881
|
async function applyCustomizations(artifacts, options) {
|
|
11614
11882
|
let next = [...artifacts];
|
|
@@ -11633,7 +11901,7 @@ async function applyInstructionOverlay(artifacts, options) {
|
|
|
11633
11901
|
const managed = await readFile30(artifact.stagedPath ?? artifact.sourcePath, "utf8");
|
|
11634
11902
|
const local = await readFile30(overlayPath, "utf8");
|
|
11635
11903
|
const composedPath = join37(options.stageRoot, ".agentwheel-composed", "instructions", "AGENTS.md");
|
|
11636
|
-
await
|
|
11904
|
+
await mkdir20(dirname25(composedPath), { recursive: true });
|
|
11637
11905
|
await writeFile20(
|
|
11638
11906
|
composedPath,
|
|
11639
11907
|
[
|
|
@@ -11706,7 +11974,7 @@ async function applyReplacements2(artifacts, options, channel, artifactTypes) {
|
|
|
11706
11974
|
const artifactKind = entry.isDirectory() ? "dir" : "file";
|
|
11707
11975
|
const existing = byKey.get(artifactMapKey);
|
|
11708
11976
|
const stagedPath = join37(options.stageRoot, ".agentwheel-composed", channel, type, entry.name);
|
|
11709
|
-
await
|
|
11977
|
+
await mkdir20(dirname25(stagedPath), { recursive: true });
|
|
11710
11978
|
await cp4(full, stagedPath, { recursive: artifactKind === "dir", dereference: true });
|
|
11711
11979
|
byKey.set(artifactMapKey, {
|
|
11712
11980
|
...existing,
|
|
@@ -11746,12 +12014,12 @@ function installableArtifactTypes() {
|
|
|
11746
12014
|
return ["instructions", "rules", "skills", "commands", "subagents", "mcp", "hooks", "settings", "plugins"];
|
|
11747
12015
|
}
|
|
11748
12016
|
async function sortedDirEntries2(path) {
|
|
11749
|
-
return (await
|
|
12017
|
+
return (await readdir10(path, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name));
|
|
11750
12018
|
}
|
|
11751
12019
|
|
|
11752
12020
|
// src/staging/claude-subagents.ts
|
|
11753
12021
|
init_fs();
|
|
11754
|
-
import { mkdir as
|
|
12022
|
+
import { mkdir as mkdir21, readFile as readFile31, writeFile as writeFile21 } from "fs/promises";
|
|
11755
12023
|
import { basename as basename18, dirname as dirname26, join as join38 } from "path";
|
|
11756
12024
|
async function renderClaudeSubagents(artifacts, stageRoot, adapter) {
|
|
11757
12025
|
if (adapter?.name !== "claude") return artifacts;
|
|
@@ -11783,7 +12051,7 @@ async function renderClaudeSubagent(artifact, stageRoot) {
|
|
|
11783
12051
|
}
|
|
11784
12052
|
const content = await readFile31(markdownPath, "utf8");
|
|
11785
12053
|
const renderedPath = join38(stageRoot, ".agentwheel-rendered", "claude-subagents", `${agentName}.md`);
|
|
11786
|
-
await
|
|
12054
|
+
await mkdir21(dirname26(renderedPath), { recursive: true });
|
|
11787
12055
|
await writeFile21(renderedPath, content.endsWith("\n") ? content : `${content}
|
|
11788
12056
|
`, "utf8");
|
|
11789
12057
|
return {
|
|
@@ -11802,6 +12070,7 @@ function claudeAgentName(artifact) {
|
|
|
11802
12070
|
}
|
|
11803
12071
|
|
|
11804
12072
|
// src/staging/staging.ts
|
|
12073
|
+
init_selection();
|
|
11805
12074
|
async function stageSource(driver, source, options = {}) {
|
|
11806
12075
|
const raw = await stageSourceRaw(driver, source, options);
|
|
11807
12076
|
return removeOnFailure(raw.root, () => renderStagedBundle(raw, options));
|
|
@@ -11825,7 +12094,7 @@ async function stageResolvedArtifactsRaw(resolved, artifacts, parentDir = tmpdir
|
|
|
11825
12094
|
await removeOnFailure(root, async () => {
|
|
11826
12095
|
for (const artifact of artifacts) {
|
|
11827
12096
|
const stagedPath = join39(root, artifact.relativePath);
|
|
11828
|
-
await
|
|
12097
|
+
await mkdir22(dirname27(stagedPath), { recursive: true });
|
|
11829
12098
|
await cp5(artifact.sourcePath, stagedPath, {
|
|
11830
12099
|
recursive: artifact.kind === "dir",
|
|
11831
12100
|
dereference: true,
|
|
@@ -11922,7 +12191,7 @@ async function copyAsset(asset, source, dest) {
|
|
|
11922
12191
|
const sourceStats = await stat12(source);
|
|
11923
12192
|
if (sourceStats.isFile()) {
|
|
11924
12193
|
if (matchesAny(basename19(source), asset.include)) {
|
|
11925
|
-
await
|
|
12194
|
+
await mkdir22(dest, { recursive: true });
|
|
11926
12195
|
await copyAssetFile(source, join39(dest, basename19(source)), asset);
|
|
11927
12196
|
}
|
|
11928
12197
|
return;
|
|
@@ -11931,26 +12200,26 @@ async function copyAsset(asset, source, dest) {
|
|
|
11931
12200
|
throw new Error(`Asset include source is not a file or directory: ${source}`);
|
|
11932
12201
|
}
|
|
11933
12202
|
if (!asset.include?.length) {
|
|
11934
|
-
await
|
|
12203
|
+
await mkdir22(dirname27(dest), { recursive: true });
|
|
11935
12204
|
await cp5(source, dest, { recursive: true, dereference: true });
|
|
11936
12205
|
if (asset.mode === "copy") await normalizeCopiedModes(dest);
|
|
11937
12206
|
return;
|
|
11938
12207
|
}
|
|
11939
12208
|
for (const file of await listFiles2(source)) {
|
|
11940
|
-
const rel =
|
|
12209
|
+
const rel = relative13(source, file).replaceAll("\\", "/");
|
|
11941
12210
|
if (!matchesAny(rel, asset.include) && !matchesAny(basename19(file), asset.include)) continue;
|
|
11942
12211
|
await copyAssetFile(file, join39(dest, rel), asset);
|
|
11943
12212
|
}
|
|
11944
12213
|
}
|
|
11945
12214
|
async function copyAssetFile(source, dest, asset) {
|
|
11946
|
-
await
|
|
12215
|
+
await mkdir22(dirname27(dest), { recursive: true });
|
|
11947
12216
|
await cp5(source, dest, { dereference: true });
|
|
11948
12217
|
if (asset.mode === "copy") await chmod3(dest, 420);
|
|
11949
12218
|
}
|
|
11950
12219
|
function resolvePackagePath(packageRoot, path) {
|
|
11951
12220
|
const resolved = resolve25(packageRoot, path);
|
|
11952
12221
|
const root = resolve25(packageRoot);
|
|
11953
|
-
if (resolved !== root && !resolved.startsWith(`${root}${
|
|
12222
|
+
if (resolved !== root && !resolved.startsWith(`${root}${sep8}`)) {
|
|
11954
12223
|
throw new Error(`Asset include escapes package root: ${path}`);
|
|
11955
12224
|
}
|
|
11956
12225
|
return resolved;
|
|
@@ -11958,7 +12227,7 @@ function resolvePackagePath(packageRoot, path) {
|
|
|
11958
12227
|
async function listFiles2(root) {
|
|
11959
12228
|
const out = [];
|
|
11960
12229
|
async function walk2(dir) {
|
|
11961
|
-
for (const entry of (await
|
|
12230
|
+
for (const entry of (await readdir11(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
11962
12231
|
const full = join39(dir, entry.name);
|
|
11963
12232
|
if (entry.isDirectory()) {
|
|
11964
12233
|
await walk2(full);
|
|
@@ -11977,7 +12246,7 @@ async function normalizeCopiedModes(path) {
|
|
|
11977
12246
|
return;
|
|
11978
12247
|
}
|
|
11979
12248
|
if (!stats.isDirectory()) return;
|
|
11980
|
-
for (const entry of await
|
|
12249
|
+
for (const entry of await readdir11(path, { withFileTypes: true })) {
|
|
11981
12250
|
await normalizeCopiedModes(join39(path, entry.name));
|
|
11982
12251
|
}
|
|
11983
12252
|
}
|
|
@@ -11998,22 +12267,24 @@ init_adapters();
|
|
|
11998
12267
|
init_adapter();
|
|
11999
12268
|
init_artifact();
|
|
12000
12269
|
init_workspace();
|
|
12001
|
-
import { appendFile, cp as cp6, lstat as
|
|
12002
|
-
import { dirname as dirname29, join as join42, relative as
|
|
12270
|
+
import { appendFile, cp as cp6, lstat as lstat3, mkdir as mkdir23, readdir as readdir13, rm as rm13 } from "fs/promises";
|
|
12271
|
+
import { dirname as dirname29, join as join42, relative as relative14 } from "path";
|
|
12003
12272
|
|
|
12004
12273
|
// src/resolve/graph.ts
|
|
12005
12274
|
import { createHash as createHash17 } from "crypto";
|
|
12006
|
-
import { readdir as
|
|
12275
|
+
import { readdir as readdir12, readFile as readFile34, stat as stat14 } from "fs/promises";
|
|
12007
12276
|
import { basename as basename20, extname as extname4, join as join41 } from "path";
|
|
12008
12277
|
init_artifact();
|
|
12009
12278
|
init_fs();
|
|
12279
|
+
init_selection();
|
|
12010
12280
|
|
|
12011
12281
|
// src/model/workspace-composition.ts
|
|
12012
12282
|
init_workspace();
|
|
12283
|
+
init_selection();
|
|
12284
|
+
init_fs();
|
|
12013
12285
|
import { createHash as createHash16 } from "crypto";
|
|
12014
12286
|
import { readFile as readFile32 } from "fs/promises";
|
|
12015
12287
|
import { z as z12 } from "zod";
|
|
12016
|
-
init_fs();
|
|
12017
12288
|
var selectionSourceConfigSchema = z12.object({
|
|
12018
12289
|
schemaVersion: z12.literal(2),
|
|
12019
12290
|
exports: workspaceExportsSchema
|
|
@@ -12172,6 +12443,7 @@ var registryCacheSchema = z13.object({
|
|
|
12172
12443
|
});
|
|
12173
12444
|
|
|
12174
12445
|
// src/registry/client.ts
|
|
12446
|
+
init_selection();
|
|
12175
12447
|
init_workspace();
|
|
12176
12448
|
init_fs();
|
|
12177
12449
|
var DEFAULT_REGISTRY_SOURCE = "github:NestDevLab/agentwheel-registry";
|
|
@@ -13075,7 +13347,7 @@ async function markdownFilesForArtifact2(artifact) {
|
|
|
13075
13347
|
async function listMarkdownFiles2(root) {
|
|
13076
13348
|
const out = [];
|
|
13077
13349
|
async function walk2(dir) {
|
|
13078
|
-
for (const entry of (await
|
|
13350
|
+
for (const entry of (await readdir12(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
13079
13351
|
const full = join41(dir, entry.name);
|
|
13080
13352
|
if (entry.isDirectory()) {
|
|
13081
13353
|
await walk2(full);
|
|
@@ -13257,8 +13529,8 @@ function verifyIntegrity(integrity, sourceHash, label) {
|
|
|
13257
13529
|
async function withCachePathLock(path, fn) {
|
|
13258
13530
|
const previous = cacheLocks.get(path) ?? Promise.resolve();
|
|
13259
13531
|
let release = () => void 0;
|
|
13260
|
-
const current = previous.then(() => new Promise((
|
|
13261
|
-
release =
|
|
13532
|
+
const current = previous.then(() => new Promise((resolve45) => {
|
|
13533
|
+
release = resolve45;
|
|
13262
13534
|
}));
|
|
13263
13535
|
cacheLocks.set(path, current);
|
|
13264
13536
|
await previous;
|
|
@@ -13339,8 +13611,8 @@ function detectDirectCollisions(nodes) {
|
|
|
13339
13611
|
}
|
|
13340
13612
|
}
|
|
13341
13613
|
function graphNodeId(name, version, normalizedSource, resolvedCommit, sourceHash) {
|
|
13342
|
-
const
|
|
13343
|
-
return `${name}@${version}+${
|
|
13614
|
+
const digest3 = createHash17("sha256").update(normalizedSource).update("\0").update(resolvedCommit ?? sourceHash).digest("hex").slice(0, 12);
|
|
13615
|
+
return `${name}@${version}+${digest3}`;
|
|
13344
13616
|
}
|
|
13345
13617
|
function sortedUnique4(values) {
|
|
13346
13618
|
return [...new Set(values)].sort((a, b) => a.localeCompare(b));
|
|
@@ -13382,7 +13654,7 @@ init_declarations();
|
|
|
13382
13654
|
async function remember(workspaceRoot, runtime, text) {
|
|
13383
13655
|
const overlayPath = join42(workspaceRoot, ".agentwheel", "overlays", runtime, "instructions.local.md");
|
|
13384
13656
|
declareMutationPath(overlayPath);
|
|
13385
|
-
await
|
|
13657
|
+
await mkdir23(dirname29(overlayPath), { recursive: true });
|
|
13386
13658
|
await appendFile(overlayPath, `${text.trim()}
|
|
13387
13659
|
`, "utf8");
|
|
13388
13660
|
return { overlayPath };
|
|
@@ -13410,12 +13682,12 @@ async function ejectArtifact(workspaceRoot, item) {
|
|
|
13410
13682
|
for (const path of await listFilesIfPresent(ejectedPath)) declareMutationPath(path);
|
|
13411
13683
|
if (artifact.kind === "dir") {
|
|
13412
13684
|
for (const sourceFile of await listFiles3(sourcePath)) {
|
|
13413
|
-
declareMutationPath(join42(ejectedPath,
|
|
13685
|
+
declareMutationPath(join42(ejectedPath, relative14(sourcePath, sourceFile)));
|
|
13414
13686
|
}
|
|
13415
13687
|
} else {
|
|
13416
13688
|
declareMutationPath(ejectedPath);
|
|
13417
13689
|
}
|
|
13418
|
-
await
|
|
13690
|
+
await mkdir23(dirname29(ejectedPath), { recursive: true });
|
|
13419
13691
|
await rm13(ejectedPath, { recursive: true, force: true });
|
|
13420
13692
|
await cp6(sourcePath, ejectedPath, { recursive: artifact.kind === "dir", dereference: true });
|
|
13421
13693
|
return {
|
|
@@ -13431,11 +13703,11 @@ async function ejectArtifact(workspaceRoot, item) {
|
|
|
13431
13703
|
}
|
|
13432
13704
|
}
|
|
13433
13705
|
async function listFiles3(root) {
|
|
13434
|
-
const stats = await
|
|
13706
|
+
const stats = await lstat3(root);
|
|
13435
13707
|
if (stats.isFile()) return [root];
|
|
13436
13708
|
const files = [];
|
|
13437
13709
|
async function walk2(dir) {
|
|
13438
|
-
for (const entry of await
|
|
13710
|
+
for (const entry of await readdir13(dir, { withFileTypes: true })) {
|
|
13439
13711
|
const path = join42(dir, entry.name);
|
|
13440
13712
|
if (entry.isDirectory()) await walk2(path);
|
|
13441
13713
|
else files.push(path);
|
|
@@ -13533,7 +13805,7 @@ init_adapter();
|
|
|
13533
13805
|
init_adapter();
|
|
13534
13806
|
init_graph_lock();
|
|
13535
13807
|
import { createHash as createHash19 } from "crypto";
|
|
13536
|
-
import { mkdir as
|
|
13808
|
+
import { mkdir as mkdir25, readdir as readdir14 } from "fs/promises";
|
|
13537
13809
|
import { basename as basename21, dirname as dirname31, join as join45, posix as posix6, resolve as resolve31 } from "path";
|
|
13538
13810
|
init_paths();
|
|
13539
13811
|
init_merge_removal();
|
|
@@ -13674,8 +13946,8 @@ function formatNamespace(decision) {
|
|
|
13674
13946
|
function formatOverride(decision) {
|
|
13675
13947
|
return `${decision.graphNodeId}:${decision.type}/${decision.name} replaces ${decision.overriddenGraphNodeId}:${decision.type}/${decision.name} via ${decision.rootId} (${decision.selector})`;
|
|
13676
13948
|
}
|
|
13677
|
-
function short(
|
|
13678
|
-
return
|
|
13949
|
+
function short(hash2) {
|
|
13950
|
+
return hash2.slice(0, 12);
|
|
13679
13951
|
}
|
|
13680
13952
|
function selectionImportKey(root) {
|
|
13681
13953
|
const selection = root.selectionImport;
|
|
@@ -13699,6 +13971,7 @@ function formatSelectionImport(root) {
|
|
|
13699
13971
|
}
|
|
13700
13972
|
|
|
13701
13973
|
// src/resolve/render.ts
|
|
13974
|
+
init_selection();
|
|
13702
13975
|
import { createHash as createHash18 } from "crypto";
|
|
13703
13976
|
import { readFile as readFile35, mkdtemp as mkdtemp5 } from "fs/promises";
|
|
13704
13977
|
import { tmpdir as tmpdir6 } from "os";
|
|
@@ -14215,7 +14488,7 @@ init_fs();
|
|
|
14215
14488
|
|
|
14216
14489
|
// src/lifecycle/trust.ts
|
|
14217
14490
|
init_fs();
|
|
14218
|
-
import { mkdir as
|
|
14491
|
+
import { mkdir as mkdir24, readFile as readFile36 } from "fs/promises";
|
|
14219
14492
|
import { homedir as homedir12 } from "os";
|
|
14220
14493
|
import { dirname as dirname30, join as join44 } from "path";
|
|
14221
14494
|
import { z as z14 } from "zod";
|
|
@@ -14294,7 +14567,7 @@ async function readTrustStore(path) {
|
|
|
14294
14567
|
return trustStoreSchema.parse(JSON.parse(await readFile36(path, "utf8")));
|
|
14295
14568
|
}
|
|
14296
14569
|
async function writeTrustStore(path, store) {
|
|
14297
|
-
await
|
|
14570
|
+
await mkdir24(dirname30(path), { recursive: true });
|
|
14298
14571
|
await writeJsonAtomic(path, trustStoreSchema.parse(store));
|
|
14299
14572
|
}
|
|
14300
14573
|
function defaultTrustStorePath() {
|
|
@@ -14303,6 +14576,7 @@ function defaultTrustStorePath() {
|
|
|
14303
14576
|
|
|
14304
14577
|
// src/lifecycle/source-plan.ts
|
|
14305
14578
|
init_workspace();
|
|
14579
|
+
init_selection();
|
|
14306
14580
|
|
|
14307
14581
|
// src/model/workspace-owner.ts
|
|
14308
14582
|
import { Buffer as Buffer2 } from "buffer";
|
|
@@ -14422,9 +14696,9 @@ async function createExactMcpRetirementPlan(desiredArtifacts, adapter, targetRoo
|
|
|
14422
14696
|
init_fs();
|
|
14423
14697
|
init_declarations();
|
|
14424
14698
|
init_workspace();
|
|
14425
|
-
import { lstat as
|
|
14699
|
+
import { lstat as lstat4, readFile as readFile37, realpath as realpath2 } from "fs/promises";
|
|
14426
14700
|
import { homedir as homedir13 } from "os";
|
|
14427
|
-
import { isAbsolute as isAbsolute10, relative as
|
|
14701
|
+
import { isAbsolute as isAbsolute10, relative as relative15, resolve as resolve29 } from "path";
|
|
14428
14702
|
async function resolveWorkspaceScope(request = {}) {
|
|
14429
14703
|
const selected = [request.user === true, request.local === true, request.fleet !== void 0].filter(Boolean).length;
|
|
14430
14704
|
if (selected > 1) {
|
|
@@ -14562,7 +14836,7 @@ function assertFleetContract(id, registration, config) {
|
|
|
14562
14836
|
async function assertCanonicalDirectory(input, label) {
|
|
14563
14837
|
const canonical = await canonicalizeDirectory(input, label);
|
|
14564
14838
|
const normalized = resolve29(input);
|
|
14565
|
-
const stats = await
|
|
14839
|
+
const stats = await lstat4(input);
|
|
14566
14840
|
if (stats.isSymbolicLink() || canonical !== normalized || input !== normalized) {
|
|
14567
14841
|
throw new Error(`${label} must be canonical and symlink-unambiguous: ${input} resolves to ${canonical}.`);
|
|
14568
14842
|
}
|
|
@@ -14576,7 +14850,7 @@ async function canonicalizeDirectory(input, label) {
|
|
|
14576
14850
|
} catch {
|
|
14577
14851
|
throw new Error(`${label} does not exist: ${input}`);
|
|
14578
14852
|
}
|
|
14579
|
-
const stats = await
|
|
14853
|
+
const stats = await lstat4(canonical);
|
|
14580
14854
|
if (!stats.isDirectory()) throw new Error(`${label} is not a directory: ${input}`);
|
|
14581
14855
|
return canonical;
|
|
14582
14856
|
}
|
|
@@ -14596,7 +14870,7 @@ function sortedUnique6(values) {
|
|
|
14596
14870
|
return [...new Set(values.map((value) => value.trim()).filter(Boolean))].sort((a, b) => a.localeCompare(b));
|
|
14597
14871
|
}
|
|
14598
14872
|
function containsPath(parent, candidate) {
|
|
14599
|
-
const path =
|
|
14873
|
+
const path = relative15(parent, candidate);
|
|
14600
14874
|
return path === "" || !path.startsWith("..") && !isAbsolute10(path);
|
|
14601
14875
|
}
|
|
14602
14876
|
|
|
@@ -15117,7 +15391,7 @@ async function discoverLegacyStateCandidates(options) {
|
|
|
15117
15391
|
const directory = dirname31(options.graphLockPath);
|
|
15118
15392
|
let names;
|
|
15119
15393
|
try {
|
|
15120
|
-
names = await
|
|
15394
|
+
names = await readdir14(directory);
|
|
15121
15395
|
} catch (error) {
|
|
15122
15396
|
if (error.code === "ENOENT") return [];
|
|
15123
15397
|
throw error;
|
|
@@ -15553,7 +15827,7 @@ import { basename as basename22, dirname as dirname32, join as join47, resolve a
|
|
|
15553
15827
|
// src/transport/run-as-identity.ts
|
|
15554
15828
|
import { execFileSync } from "child_process";
|
|
15555
15829
|
import { lstatSync, realpathSync as realpathSync2 } from "fs";
|
|
15556
|
-
import { isAbsolute as isAbsolute11, join as join46, parse as parse5, resolve as resolve32, sep as
|
|
15830
|
+
import { isAbsolute as isAbsolute11, join as join46, parse as parse5, resolve as resolve32, sep as sep9 } from "path";
|
|
15557
15831
|
function resolveRunAsIdentity(user, root, getent = (name) => execFileSync("/usr/bin/getent", ["passwd", name], { encoding: "utf8" })) {
|
|
15558
15832
|
if (!/^[a-z_][a-z0-9_-]*[$]?$/i.test(user) || user === "root") {
|
|
15559
15833
|
throw new Error(`Invalid local run-as account '${user}'.`);
|
|
@@ -15575,19 +15849,19 @@ function resolveRunAsIdentity(user, root, getent = (name) => execFileSync("/usr/
|
|
|
15575
15849
|
if (!Number.isSafeInteger(uid) || uid <= 0 || !Number.isSafeInteger(gid) || gid < 0 || !isAbsolute11(home) || resolve32(home) !== home) {
|
|
15576
15850
|
throw new Error(`Invalid UID, GID or home for local run-as account '${user}'.`);
|
|
15577
15851
|
}
|
|
15578
|
-
|
|
15852
|
+
assertNoSymlinkPath2(home);
|
|
15579
15853
|
if (realpathSync2(home) !== home || !lstatSync(home).isDirectory()) {
|
|
15580
15854
|
throw new Error(`Local run-as home ${home} must be a real directory.`);
|
|
15581
15855
|
}
|
|
15582
|
-
|
|
15856
|
+
assertNoSymlinkPath2(root);
|
|
15583
15857
|
if (realpathSync2(root) !== root || !lstatSync(root).isDirectory() || lstatSync(root).uid !== uid) {
|
|
15584
15858
|
throw new Error(`Local run-as root ${root} must be a real directory owned by UID ${uid}.`);
|
|
15585
15859
|
}
|
|
15586
15860
|
return { user, uid, gid, home, root };
|
|
15587
15861
|
}
|
|
15588
|
-
function
|
|
15862
|
+
function assertNoSymlinkPath2(path) {
|
|
15589
15863
|
let current = parse5(path).root;
|
|
15590
|
-
for (const part of path.slice(current.length).split(
|
|
15864
|
+
for (const part of path.slice(current.length).split(sep9).filter(Boolean)) {
|
|
15591
15865
|
current = join46(current, part);
|
|
15592
15866
|
if (lstatSync(current).isSymbolicLink()) throw new Error(`Local run-as root contains a symlink: ${current}`);
|
|
15593
15867
|
}
|
|
@@ -15773,6 +16047,7 @@ function runtimeScanRoot(request) {
|
|
|
15773
16047
|
// src/lifecycle/profile.ts
|
|
15774
16048
|
init_identify();
|
|
15775
16049
|
init_transport();
|
|
16050
|
+
init_selection();
|
|
15776
16051
|
init_declarations();
|
|
15777
16052
|
async function syncProfile(options) {
|
|
15778
16053
|
const config = await readMergedWorkspaceConfig(options.workspaceRoot);
|
|
@@ -15930,6 +16205,7 @@ function combinedSuggestionAliases(packageAliases, optionAliases) {
|
|
|
15930
16205
|
}
|
|
15931
16206
|
|
|
15932
16207
|
// src/registry/publish.ts
|
|
16208
|
+
init_selection();
|
|
15933
16209
|
var DEFAULT_REGISTRY_SUBMISSION_URL = "https://github.com/NestDevLab/agentwheel-registry/issues/new";
|
|
15934
16210
|
var registryEntryTypes = ["package", "skill", "plugin", "mcp", "adapter"];
|
|
15935
16211
|
var explicitSourcePrefixes = ["github:", "git:", "skillkit:", "vercel:", "mcp-registry:", "clawhub:"];
|
|
@@ -16093,8 +16369,11 @@ function shellQuoteArg(value) {
|
|
|
16093
16369
|
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
16094
16370
|
}
|
|
16095
16371
|
|
|
16372
|
+
// src/cli/index.ts
|
|
16373
|
+
init_selection();
|
|
16374
|
+
|
|
16096
16375
|
// src/cli/update-check.ts
|
|
16097
|
-
import { mkdir as
|
|
16376
|
+
import { mkdir as mkdir26, readFile as readFile38, writeFile as writeFile22 } from "fs/promises";
|
|
16098
16377
|
import { homedir as homedir14 } from "os";
|
|
16099
16378
|
import { dirname as dirname33, join as join48 } from "path";
|
|
16100
16379
|
var DEFAULT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -16149,7 +16428,7 @@ async function readCache(path) {
|
|
|
16149
16428
|
}
|
|
16150
16429
|
}
|
|
16151
16430
|
async function writeCache(path, cache) {
|
|
16152
|
-
await
|
|
16431
|
+
await mkdir26(dirname33(path), { recursive: true });
|
|
16153
16432
|
await writeFile22(path, `${JSON.stringify(cache, null, 2)}
|
|
16154
16433
|
`, "utf8");
|
|
16155
16434
|
}
|
|
@@ -16593,6 +16872,7 @@ init_manifest2();
|
|
|
16593
16872
|
init_apply();
|
|
16594
16873
|
init_paths();
|
|
16595
16874
|
init_transaction();
|
|
16875
|
+
init_selection();
|
|
16596
16876
|
import { createHash as createHash20 } from "crypto";
|
|
16597
16877
|
import { resolve as resolve37 } from "path";
|
|
16598
16878
|
init_declarations();
|
|
@@ -16872,14 +17152,222 @@ function canonicalJson6(value) {
|
|
|
16872
17152
|
return `{${Object.keys(record).filter((key) => record[key] !== void 0).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson6(record[key])}`).join(",")}}`;
|
|
16873
17153
|
}
|
|
16874
17154
|
|
|
17155
|
+
// src/lifecycle/ownership-release.ts
|
|
17156
|
+
init_apply();
|
|
17157
|
+
init_manifest2();
|
|
17158
|
+
init_paths();
|
|
17159
|
+
init_transaction();
|
|
17160
|
+
import { createHash as createHash21, randomUUID as randomUUID6 } from "crypto";
|
|
17161
|
+
import { lstat as lstat5 } from "fs/promises";
|
|
17162
|
+
import { join as join52, resolve as resolve38 } from "path";
|
|
17163
|
+
init_coordinator();
|
|
17164
|
+
init_declarations();
|
|
17165
|
+
init_protocol();
|
|
17166
|
+
init_repository();
|
|
17167
|
+
init_process();
|
|
17168
|
+
init_transport();
|
|
17169
|
+
async function planReleaseOwnership(request) {
|
|
17170
|
+
return observe2(request);
|
|
17171
|
+
}
|
|
17172
|
+
async function applyReleaseOwnership(request) {
|
|
17173
|
+
for (const [name, value] of Object.entries({
|
|
17174
|
+
planDigest: request.planDigest,
|
|
17175
|
+
expectedSourceRevision: request.expectedSourceRevision,
|
|
17176
|
+
expectedInventoryRevision: request.expectedInventoryRevision
|
|
17177
|
+
})) {
|
|
17178
|
+
if (!value || !/^[a-f0-9]{64}$/.test(value)) throw new Error(`Reviewed SHA-256 ${name} is required to release a claim.`);
|
|
17179
|
+
}
|
|
17180
|
+
const workspaceRoot = resolve38(request.workspaceRoot);
|
|
17181
|
+
const existing = activeMutationMetadata();
|
|
17182
|
+
if (existing && (resolve38(existing.workspaceRoot) !== workspaceRoot || existing.reason !== request.reason.replace(/\r\n?/gu, "\n").trim() || request.operationId && request.operationId !== existing.operationId)) {
|
|
17183
|
+
throw new Error("Ownership release does not match the active governed workspace, reason or operation.");
|
|
17184
|
+
}
|
|
17185
|
+
const operationId = mutationOperationIdSchema.parse(existing?.operationId ?? request.operationId ?? randomUUID6());
|
|
17186
|
+
const auditPath = join52(workspaceRoot, ".agentwheel", "ownership-releases", `${operationId}.json`);
|
|
17187
|
+
const mutation = existing ? void 0 : await GovernedMutation.begin({
|
|
17188
|
+
workspaceRoot,
|
|
17189
|
+
commandName: "agentwheel ownership release-claim",
|
|
17190
|
+
reason: request.reason,
|
|
17191
|
+
operationId,
|
|
17192
|
+
noCommit: request.noCommit,
|
|
17193
|
+
globalRoot: request.globalRoot,
|
|
17194
|
+
anticipatedPaths: [auditPath]
|
|
17195
|
+
});
|
|
17196
|
+
try {
|
|
17197
|
+
if ((existing ?? mutation?.metadata())?.revisionMode === "commit-after-verify") {
|
|
17198
|
+
const repository = await discoverGitRepository(workspaceRoot);
|
|
17199
|
+
if (!repository) throw new Error("Ownership release requires a declarative Git audit path.");
|
|
17200
|
+
const ignored = await runProcess("git", ["-C", repository.root, "check-ignore", "--no-index", "--", auditPath], { allowExitCodes: [0, 1] });
|
|
17201
|
+
if (ignored.exitCode === 0) {
|
|
17202
|
+
throw new Error("Ownership release audit path is Git-ignored; explicitly declare it in repository policy before releasing a claim.");
|
|
17203
|
+
}
|
|
17204
|
+
}
|
|
17205
|
+
const result = await applyLocked(request, operationId, auditPath);
|
|
17206
|
+
await mutation?.complete();
|
|
17207
|
+
return result;
|
|
17208
|
+
} catch (error) {
|
|
17209
|
+
await mutation?.fail(error);
|
|
17210
|
+
throw error;
|
|
17211
|
+
}
|
|
17212
|
+
}
|
|
17213
|
+
async function applyLocked(request, operationId, auditPath) {
|
|
17214
|
+
const transport = request.transport ?? localTransport;
|
|
17215
|
+
if (transport.kind !== "local") throw new Error("Ownership release supports local targets only.");
|
|
17216
|
+
await assertNoSymlinkPath(join52(resolve38(request.targetRoot), ".agentwheel"));
|
|
17217
|
+
await assertNoSymlinkPath(auditPath);
|
|
17218
|
+
const lock = await acquireApplyLock(resolve38(request.targetRoot), request.adapter, transport, {}, {
|
|
17219
|
+
installationType: request.installationType,
|
|
17220
|
+
stateKey: request.stateKey
|
|
17221
|
+
});
|
|
17222
|
+
try {
|
|
17223
|
+
if ((await listAllApplyJournals(request.targetRoot, void 0, transport)).length) {
|
|
17224
|
+
throw new Error("Ownership release refuses pending runtime journals; recover them first.");
|
|
17225
|
+
}
|
|
17226
|
+
const plan = await observe2(request);
|
|
17227
|
+
if (plan.planDigest !== request.planDigest || plan.source.revision !== request.expectedSourceRevision || plan.manifestInventoryRevision !== request.expectedInventoryRevision) {
|
|
17228
|
+
throw new Error("Stale ownership release plan, manifest or inventory; replan required.");
|
|
17229
|
+
}
|
|
17230
|
+
if (await transport.pathExists(auditPath)) throw new Error(`Ownership release audit already exists: ${auditPath}`);
|
|
17231
|
+
declareMutationPath(auditPath);
|
|
17232
|
+
declareMutationPath(plan.source.manifestPath);
|
|
17233
|
+
const manifest = await readInstallManifest(plan.targetRoot, plan.adapter, transport, {
|
|
17234
|
+
installationType: plan.installationType,
|
|
17235
|
+
stateKey: plan.source.stateKey
|
|
17236
|
+
});
|
|
17237
|
+
if (!manifest || manifest.version !== 2 || manifest.revision !== plan.source.revision) {
|
|
17238
|
+
throw new Error("Source manifest changed after selection; replan required.");
|
|
17239
|
+
}
|
|
17240
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
17241
|
+
const content = `${JSON.stringify({
|
|
17242
|
+
schemaVersion: 1,
|
|
17243
|
+
event: "ownership-release-intent",
|
|
17244
|
+
operationId,
|
|
17245
|
+
reason: plan.reason,
|
|
17246
|
+
reviewedPlanDigest: plan.planDigest,
|
|
17247
|
+
plan,
|
|
17248
|
+
// This immutable dossier records intent. The linked journal verifies completion separately.
|
|
17249
|
+
completion: "Verify install state and the governed mutation receipt; intent alone does not prove completion."
|
|
17250
|
+
}, null, 2)}
|
|
17251
|
+
`;
|
|
17252
|
+
const metadata = mutationMetadataForApplyJournal();
|
|
17253
|
+
const journal = {
|
|
17254
|
+
version: metadata ? 2 : 1,
|
|
17255
|
+
...metadata ? { mutation: metadata } : {},
|
|
17256
|
+
mode: "uninstall",
|
|
17257
|
+
adapter: plan.adapter,
|
|
17258
|
+
installationType: plan.installationType,
|
|
17259
|
+
stateKey: plan.source.stateKey,
|
|
17260
|
+
targetRoot: plan.targetRoot,
|
|
17261
|
+
baseRevision: manifest.revision,
|
|
17262
|
+
createdAt: now,
|
|
17263
|
+
updatedAt: now,
|
|
17264
|
+
operations: [],
|
|
17265
|
+
completed: [],
|
|
17266
|
+
manifest: { ...manifest, entries: manifest.entries.filter((entry) => digest2(entry) !== plan.source.entryDigest) },
|
|
17267
|
+
metadataAudit: {
|
|
17268
|
+
kind: "ownership-release",
|
|
17269
|
+
workspaceRoot: plan.workspaceRoot,
|
|
17270
|
+
operationId,
|
|
17271
|
+
planDigest: plan.planDigest,
|
|
17272
|
+
inventory: plan.inventory,
|
|
17273
|
+
runtime: [{ path: plan.path, hash: plan.runtimeHash, snapshotDigest: plan.runtimeSnapshotDigest }],
|
|
17274
|
+
auditFiles: [{ path: `.agentwheel/ownership-releases/${operationId}.json`, content, sha256: hash(content) }]
|
|
17275
|
+
}
|
|
17276
|
+
};
|
|
17277
|
+
await writeApplyJournal(journal, transport);
|
|
17278
|
+
await commitManifestMetadataJournal(journal, transport);
|
|
17279
|
+
return { ...plan, applied: true, operationId, auditPath, retainedEntries: journal.manifest.entries.length };
|
|
17280
|
+
} finally {
|
|
17281
|
+
await lock.release();
|
|
17282
|
+
}
|
|
17283
|
+
}
|
|
17284
|
+
async function observe2(request) {
|
|
17285
|
+
const transport = request.transport ?? localTransport;
|
|
17286
|
+
if (transport.kind !== "local") throw new Error("Ownership release supports local targets only.");
|
|
17287
|
+
const targetRoot = resolve38(request.targetRoot);
|
|
17288
|
+
const workspaceRoot = resolve38(request.workspaceRoot);
|
|
17289
|
+
const reason = mutationReasonSchema.parse(request.reason.trim());
|
|
17290
|
+
if (!request.stateKey || stateKeyFor(request.adapter, { stateKey: request.stateKey }) !== request.stateKey || !request.installationType.trim() || !request.selector.includes("/")) {
|
|
17291
|
+
throw new Error("Ownership release requires an exact state key, installation type and type/name selector.");
|
|
17292
|
+
}
|
|
17293
|
+
const runtimePath = metadataRuntimePath(targetRoot, request.path);
|
|
17294
|
+
await assertNoSymlinkPath(workspaceRoot);
|
|
17295
|
+
await assertNoSymlinkPath(join52(targetRoot, ".agentwheel"));
|
|
17296
|
+
const manifests = await listInstallManifests(targetRoot, void 0, transport);
|
|
17297
|
+
for (const item of manifests) await assertNoSymlinkPath(item.path);
|
|
17298
|
+
const source = manifests.find((item) => item.stateKey === request.stateKey);
|
|
17299
|
+
const manifest = source?.manifest;
|
|
17300
|
+
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) {
|
|
17301
|
+
throw new Error("Source manifest does not prove the requested target, adapter, installation type and state key.");
|
|
17302
|
+
}
|
|
17303
|
+
const matches = manifest.entries.filter((entry2) => entry2.path === request.path && `${entry2.artifactType}/${entry2.artifactName}` === request.selector);
|
|
17304
|
+
if (matches.length !== 1) throw new Error(`Expected exactly one ownership claim at ${request.path} for ${request.selector}.`);
|
|
17305
|
+
const entry = matches[0];
|
|
17306
|
+
const owner = workspaceOwnerForRoot(request.fromWorkspaceRoot, request.fromFleetId);
|
|
17307
|
+
if (entry.workspaceOwner !== owner) throw new Error(`Workspace owner does not match the selected claim at ${entry.path}.`);
|
|
17308
|
+
if (entry.mergeStrategy || entry.mode || entry.semanticPlugin || entry.semanticCommand || entry.executed || entry.channel === "ejected") {
|
|
17309
|
+
throw new Error("Ownership release only supports plain file/directory claims; shared or semantic contributions are protected.");
|
|
17310
|
+
}
|
|
17311
|
+
for (const item of manifests) {
|
|
17312
|
+
for (const other of item.manifest.entries) {
|
|
17313
|
+
if (item === source && other === entry) continue;
|
|
17314
|
+
const otherPath = metadataRuntimePath(targetRoot, other.path);
|
|
17315
|
+
if (otherPath === runtimePath || otherPath.startsWith(`${runtimePath}/`) || runtimePath.startsWith(`${otherPath}/`)) {
|
|
17316
|
+
throw new Error(`Ambiguous or overlapping ownership claim at ${entry.path} in ${item.stateKey}.`);
|
|
17317
|
+
}
|
|
17318
|
+
}
|
|
17319
|
+
}
|
|
17320
|
+
const snapshotDigest = await ownershipRuntimeSnapshot(runtimePath);
|
|
17321
|
+
const runtimeKind = (await lstat5(runtimePath)).isDirectory() ? "dir" : "file";
|
|
17322
|
+
if (runtimeKind !== entry.kind) throw new Error("Selected runtime kind does not match the manifest claim.");
|
|
17323
|
+
const runtimeHash = await transport.hashPath(runtimePath);
|
|
17324
|
+
const inventory = manifests.map((item) => ({ stateKey: item.stateKey, revision: item.manifest.revision }));
|
|
17325
|
+
const plan = {
|
|
17326
|
+
adapter: request.adapter,
|
|
17327
|
+
installationType: request.installationType,
|
|
17328
|
+
targetRoot,
|
|
17329
|
+
workspaceRoot,
|
|
17330
|
+
owner,
|
|
17331
|
+
selector: request.selector,
|
|
17332
|
+
path: entry.path,
|
|
17333
|
+
source: {
|
|
17334
|
+
stateKey: request.stateKey,
|
|
17335
|
+
manifestPath: installManifestPath(targetRoot, request.adapter, { installationType: request.installationType, stateKey: request.stateKey }),
|
|
17336
|
+
revision: manifest.revision,
|
|
17337
|
+
entryDigest: digest2(entry),
|
|
17338
|
+
entry
|
|
17339
|
+
},
|
|
17340
|
+
recordedHash: entry.hash,
|
|
17341
|
+
runtimeHash,
|
|
17342
|
+
runtimeSnapshotDigest: snapshotDigest,
|
|
17343
|
+
manifestInventoryRevision: await computeInstallManifestInventoryRevision(targetRoot, void 0, transport),
|
|
17344
|
+
inventory,
|
|
17345
|
+
reason
|
|
17346
|
+
};
|
|
17347
|
+
return { ...plan, planDigest: digest2(plan) };
|
|
17348
|
+
}
|
|
17349
|
+
function hash(content) {
|
|
17350
|
+
return createHash21("sha256").update(content).digest("hex");
|
|
17351
|
+
}
|
|
17352
|
+
function digest2(value) {
|
|
17353
|
+
return hash(canonicalJson7(value));
|
|
17354
|
+
}
|
|
17355
|
+
function canonicalJson7(value) {
|
|
17356
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson7).join(",")}]`;
|
|
17357
|
+
if (!value || typeof value !== "object") return JSON.stringify(value);
|
|
17358
|
+
const record = value;
|
|
17359
|
+
return `{${Object.keys(record).filter((key) => record[key] !== void 0).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson7(record[key])}`).join(",")}}`;
|
|
17360
|
+
}
|
|
17361
|
+
|
|
16875
17362
|
// src/lifecycle/ownership-adopt-legacy.ts
|
|
16876
17363
|
init_apply();
|
|
16877
17364
|
init_manifest2();
|
|
16878
17365
|
init_paths();
|
|
16879
17366
|
init_transaction();
|
|
16880
|
-
|
|
17367
|
+
init_instructions_block();
|
|
17368
|
+
import { createHash as createHash23 } from "crypto";
|
|
16881
17369
|
import { realpath as realpath3 } from "fs/promises";
|
|
16882
|
-
import { isAbsolute as isAbsolute12, relative as
|
|
17370
|
+
import { isAbsolute as isAbsolute12, join as join54, relative as relative17, resolve as resolve40 } from "path";
|
|
16883
17371
|
init_manifest();
|
|
16884
17372
|
init_declarations();
|
|
16885
17373
|
init_transport();
|
|
@@ -16892,18 +17380,18 @@ init_instructions_block();
|
|
|
16892
17380
|
init_merge_removal();
|
|
16893
17381
|
init_graph_lock();
|
|
16894
17382
|
init_manifest();
|
|
16895
|
-
import { createHash as
|
|
16896
|
-
import { lstat as
|
|
16897
|
-
import { basename as basename23, dirname as dirname35, join as
|
|
17383
|
+
import { createHash as createHash22 } from "crypto";
|
|
17384
|
+
import { lstat as lstat6, readdir as readdir15 } from "fs/promises";
|
|
17385
|
+
import { basename as basename23, dirname as dirname35, join as join53, posix as posix7, relative as relative16, resolve as resolve39 } from "path";
|
|
16898
17386
|
async function inventoryHistoricalLocks(fleetRoot) {
|
|
16899
|
-
const root =
|
|
17387
|
+
const root = join53(fleetRoot, ".agentwheel", "locks");
|
|
16900
17388
|
const found = [];
|
|
16901
17389
|
async function visit(dir) {
|
|
16902
|
-
for (const entry of await
|
|
17390
|
+
for (const entry of await readdir15(dir, { withFileTypes: true }).catch((error) => {
|
|
16903
17391
|
if (error.code === "ENOENT") return [];
|
|
16904
17392
|
throw error;
|
|
16905
17393
|
})) {
|
|
16906
|
-
const path =
|
|
17394
|
+
const path = join53(dir, entry.name);
|
|
16907
17395
|
if (entry.isDirectory()) await visit(path);
|
|
16908
17396
|
else if (entry.isFile() && entry.name.endsWith(".graph-lock.json")) found.push(await historicalLock(path));
|
|
16909
17397
|
}
|
|
@@ -16912,15 +17400,15 @@ async function inventoryHistoricalLocks(fleetRoot) {
|
|
|
16912
17400
|
return found.sort((a, b) => a.path.localeCompare(b.path));
|
|
16913
17401
|
}
|
|
16914
17402
|
async function findLegacyNamedLocks(workspaceRoot, adapter, fingerprint) {
|
|
16915
|
-
const root =
|
|
17403
|
+
const root = join53(workspaceRoot, ".agentwheel", "locks");
|
|
16916
17404
|
const found = [];
|
|
16917
|
-
for (const target of await
|
|
17405
|
+
for (const target of await readdir15(root, { withFileTypes: true }).catch((error) => {
|
|
16918
17406
|
if (error.code === "ENOENT") return [];
|
|
16919
17407
|
throw error;
|
|
16920
17408
|
})) {
|
|
16921
17409
|
if (!target.isDirectory()) continue;
|
|
16922
|
-
const path =
|
|
16923
|
-
const stats = await
|
|
17410
|
+
const path = join53(root, target.name, adapter, `${fingerprint}.graph-lock.json`);
|
|
17411
|
+
const stats = await lstat6(path).catch((error) => {
|
|
16924
17412
|
if (error.code === "ENOENT") return void 0;
|
|
16925
17413
|
throw error;
|
|
16926
17414
|
});
|
|
@@ -16959,7 +17447,7 @@ async function planLegacyRecovery(input) {
|
|
|
16959
17447
|
const nodeHashes = new Map(graphPlan.bundle.graphLock.canonical.nodes.map((node) => [node.id, node.sourceHash]));
|
|
16960
17448
|
for (const op of graphPlan.plan.operations) {
|
|
16961
17449
|
if (!op.desiredHash || op.action === "remove" || op.action === "keep") continue;
|
|
16962
|
-
const path =
|
|
17450
|
+
const path = relative16(installRoot, op.destPath);
|
|
16963
17451
|
if (path.startsWith("..") || path === "") throw new Error(`Planner destination escapes target: ${op.destPath}`);
|
|
16964
17452
|
paths.add(path);
|
|
16965
17453
|
const items = desired.get(path) ?? [];
|
|
@@ -16980,8 +17468,8 @@ async function planLegacyRecovery(input) {
|
|
|
16980
17468
|
const currentNodeIds = [...new Set(current.map((item) => item.graphNodeId).filter((value) => Boolean(value)))].sort();
|
|
16981
17469
|
const currentSelectors = [...new Set(current.map((item) => item.logicalSelector).filter((value) => Boolean(value)))].sort();
|
|
16982
17470
|
const currentOwners = [...new Set(current.flatMap((item) => item.owners ?? []))].sort();
|
|
16983
|
-
const livePath =
|
|
16984
|
-
if (
|
|
17471
|
+
const livePath = resolve39(installRoot, path);
|
|
17472
|
+
if (relative16(installRoot, livePath).startsWith("..")) throw new Error(`Manifest path escapes target: ${path}`);
|
|
16985
17473
|
const liveHash = await transport.pathExists(livePath) ? await transport.hashPath(livePath) : null;
|
|
16986
17474
|
const reasons = [];
|
|
16987
17475
|
const currentResults = await Promise.all(current.map((op) => verifyCurrentOperation(op, liveHash, transport)));
|
|
@@ -17046,14 +17534,14 @@ async function planLegacyRecovery(input) {
|
|
|
17046
17534
|
};
|
|
17047
17535
|
}
|
|
17048
17536
|
function claimFor(item, entry, locks, binding) {
|
|
17049
|
-
const
|
|
17537
|
+
const digest3 = "graphLockDigest" in entry ? entry.graphLockDigest : void 0;
|
|
17050
17538
|
const nodeId = "graphNodeId" in entry ? entry.graphNodeId : void 0;
|
|
17051
17539
|
const selector = "logicalSelector" in entry ? entry.logicalSelector : void 0;
|
|
17052
|
-
const matches = locks.filter((candidate) => candidate.digest ===
|
|
17540
|
+
const matches = locks.filter((candidate) => candidate.digest === digest3);
|
|
17053
17541
|
const evidence = [];
|
|
17054
17542
|
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) =>
|
|
17543
|
+
const sameRoot = binding.transportKind === "ssh" ? posix7.normalize(item.manifest.targetRoot) === posix7.normalize(binding.installRoot) : resolve39(item.manifest.targetRoot) === resolve39(binding.installRoot);
|
|
17544
|
+
const inTargetDir = matches.filter((candidate) => resolve39(dirname35(candidate.path)) === resolve39(binding.targetLockDir));
|
|
17057
17545
|
const boundLocks = inTargetDir.filter((candidate) => lockBindsState(candidate, item, binding));
|
|
17058
17546
|
if (owner !== binding.expectedOwner) evidence.push("workspace owner differs from selected Fleet target");
|
|
17059
17547
|
if (item.manifest.version !== 2 || item.manifest.installationType !== binding.installationType) evidence.push("installation type differs or manifest is legacy v1");
|
|
@@ -17061,7 +17549,7 @@ function claimFor(item, entry, locks, binding) {
|
|
|
17061
17549
|
if (item.manifest.version === 2 && (item.manifest.stateKey !== item.stateKey || item.manifest.adapter !== binding.adapter)) {
|
|
17062
17550
|
evidence.push("manifest state key or adapter differs from filename");
|
|
17063
17551
|
}
|
|
17064
|
-
if (!
|
|
17552
|
+
if (!digest3) evidence.push("historical graph-lock digest missing");
|
|
17065
17553
|
else if (matches.length === 0) evidence.push("historical graph-lock digest has no matching lock");
|
|
17066
17554
|
if (matches.length && !inTargetDir.length) evidence.push("claim belongs to another Fleet target graph");
|
|
17067
17555
|
if (inTargetDir.length && !boundLocks.length) evidence.push("graph-lock fingerprint, filename, or state key does not bind claim");
|
|
@@ -17082,7 +17570,7 @@ function claimFor(item, entry, locks, binding) {
|
|
|
17082
17570
|
owner,
|
|
17083
17571
|
hash: entry.hash,
|
|
17084
17572
|
sourceHash: entry.sourceHash,
|
|
17085
|
-
graphLockDigest:
|
|
17573
|
+
graphLockDigest: digest3,
|
|
17086
17574
|
graphNodeId: nodeId,
|
|
17087
17575
|
logicalSelector: selector,
|
|
17088
17576
|
owners: "owners" in entry ? entry.owners : [entry.packageName ?? "legacy"],
|
|
@@ -17095,7 +17583,7 @@ function lockBindsState(candidate, manifest, binding) {
|
|
|
17095
17583
|
if (!name.endsWith(".graph-lock.json")) return false;
|
|
17096
17584
|
const fingerprint = name.slice(0, -".graph-lock.json".length);
|
|
17097
17585
|
if (!/^[a-f0-9]{64}$/u.test(fingerprint)) return false;
|
|
17098
|
-
const stable =
|
|
17586
|
+
const stable = resolve39(candidate.path) === resolve39(binding.stableLockPath);
|
|
17099
17587
|
if (stable) {
|
|
17100
17588
|
return manifest.stateKey === binding.stableStateKey && candidate.lock.canonical.targetFingerprint === binding.currentFingerprint;
|
|
17101
17589
|
}
|
|
@@ -17163,7 +17651,7 @@ function assertCompleteGraphPlan(plan) {
|
|
|
17163
17651
|
}
|
|
17164
17652
|
}
|
|
17165
17653
|
function sha2566(value) {
|
|
17166
|
-
return
|
|
17654
|
+
return createHash22("sha256").update(value).digest("hex");
|
|
17167
17655
|
}
|
|
17168
17656
|
|
|
17169
17657
|
// src/lifecycle/ownership-adopt-legacy.ts
|
|
@@ -17174,24 +17662,31 @@ function desiredCoverageFromPlan(plan) {
|
|
|
17174
17662
|
artifactType: operation.artifactType,
|
|
17175
17663
|
artifactName: operation.artifactName,
|
|
17176
17664
|
kind: operation.kind,
|
|
17665
|
+
...operation.mode ? { mode: operation.mode } : {},
|
|
17666
|
+
...operation.mode && operation.logicalSelector ? { logicalSelector: operation.logicalSelector } : {},
|
|
17177
17667
|
...operation.packageName ? { packageName: operation.packageName } : {}
|
|
17178
17668
|
})).sort((left, right) => left.path.localeCompare(right.path));
|
|
17179
17669
|
}
|
|
17180
17670
|
async function planAdoptLegacyOwnership(request) {
|
|
17181
|
-
return
|
|
17671
|
+
return observe3(request, request.transport ?? localTransport);
|
|
17182
17672
|
}
|
|
17183
17673
|
async function applyAdoptLegacyOwnership(request) {
|
|
17184
17674
|
assertApplyPreconditions2(request);
|
|
17185
17675
|
const transport = request.transport ?? localTransport;
|
|
17186
|
-
const targetRoot =
|
|
17676
|
+
const targetRoot = resolve40(request.targetRoot);
|
|
17187
17677
|
const installationType = request.installationType.trim();
|
|
17678
|
+
if (transport.kind !== "local") throw new Error("Legacy ownership adoption requires a local filesystem transport.");
|
|
17679
|
+
const beforeLock = await readInstallManifest(targetRoot, request.adapter, transport, { installationType, stateKey: request.sourceStateKey });
|
|
17680
|
+
if (beforeLock?.entries.some((entry) => entry.mode === "managed-block")) {
|
|
17681
|
+
await assertNoSymlinkPath(join54(targetRoot, ".agentwheel"));
|
|
17682
|
+
}
|
|
17188
17683
|
const lock = await acquireApplyLock(targetRoot, request.adapter, transport, {}, {
|
|
17189
17684
|
installationType,
|
|
17190
17685
|
stateKey: request.destinationStateKey
|
|
17191
17686
|
});
|
|
17192
17687
|
try {
|
|
17193
17688
|
await assertNoPendingJournals(targetRoot, request.adapter, transport);
|
|
17194
|
-
const current = await
|
|
17689
|
+
const current = await observe3(request, transport);
|
|
17195
17690
|
assertExpected(request.planDigest, current.planDigest, "plan digest");
|
|
17196
17691
|
assertExpected(request.expectedSourceRevision, current.source.revision, "source manifest revision");
|
|
17197
17692
|
assertExpected(request.expectedDestinationRevision, current.destination.revision ?? absentRevision, "destination manifest revision");
|
|
@@ -17211,6 +17706,7 @@ async function applyAdoptLegacyOwnership(request) {
|
|
|
17211
17706
|
throw new Error("Source manifest selection changed while locked; replan required.");
|
|
17212
17707
|
}
|
|
17213
17708
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
17709
|
+
let guards = current.metadataGuards;
|
|
17214
17710
|
if (adopted.length > 0) {
|
|
17215
17711
|
declareMutationPath(current.destination.manifestPath);
|
|
17216
17712
|
const base = destination ?? {
|
|
@@ -17224,13 +17720,22 @@ async function applyAdoptLegacyOwnership(request) {
|
|
|
17224
17720
|
legacy: false,
|
|
17225
17721
|
entries: []
|
|
17226
17722
|
};
|
|
17227
|
-
|
|
17723
|
+
const next = withManifestRevision({
|
|
17228
17724
|
...base,
|
|
17725
|
+
generatedAt: now,
|
|
17229
17726
|
entries: [...base.entries, ...adopted].sort((left, right) => left.path.localeCompare(right.path))
|
|
17230
|
-
}
|
|
17727
|
+
});
|
|
17728
|
+
await commitMetadata(next, destination?.revision ?? null, now, transport, guards);
|
|
17729
|
+
if (guards) guards = {
|
|
17730
|
+
...guards,
|
|
17731
|
+
inventory: [
|
|
17732
|
+
...guards.inventory.filter((item) => item.stateKey !== current.destination.stateKey),
|
|
17733
|
+
{ stateKey: current.destination.stateKey, revision: next.revision }
|
|
17734
|
+
].sort((left, right) => left.stateKey.localeCompare(right.stateKey))
|
|
17735
|
+
};
|
|
17231
17736
|
}
|
|
17232
17737
|
declareMutationPath(current.source.manifestPath);
|
|
17233
|
-
await commitMetadata({ ...source, entries: retained }, source.revision, now, transport);
|
|
17738
|
+
await commitMetadata({ ...source, entries: retained }, source.revision, now, transport, guards);
|
|
17234
17739
|
return {
|
|
17235
17740
|
...current,
|
|
17236
17741
|
applied: true,
|
|
@@ -17243,7 +17748,7 @@ async function applyAdoptLegacyOwnership(request) {
|
|
|
17243
17748
|
await lock.release();
|
|
17244
17749
|
}
|
|
17245
17750
|
}
|
|
17246
|
-
async function commitMetadata(manifest, baseRevision, now, transport) {
|
|
17751
|
+
async function commitMetadata(manifest, baseRevision, now, transport, metadataGuards) {
|
|
17247
17752
|
const mutation = mutationMetadataForApplyJournal();
|
|
17248
17753
|
const journal = {
|
|
17249
17754
|
version: mutation ? 2 : 1,
|
|
@@ -17258,12 +17763,13 @@ async function commitMetadata(manifest, baseRevision, now, transport) {
|
|
|
17258
17763
|
updatedAt: now,
|
|
17259
17764
|
operations: [],
|
|
17260
17765
|
completed: [],
|
|
17261
|
-
manifest
|
|
17766
|
+
manifest,
|
|
17767
|
+
...metadataGuards ? { metadataGuards } : {}
|
|
17262
17768
|
};
|
|
17263
17769
|
await writeApplyJournal(journal, transport);
|
|
17264
17770
|
await commitManifestMetadataJournal(journal, transport);
|
|
17265
17771
|
}
|
|
17266
|
-
async function
|
|
17772
|
+
async function observe3(request, transport) {
|
|
17267
17773
|
if (transport.kind === "ssh") {
|
|
17268
17774
|
throw new Error("Legacy ownership adoption does not support SSH targets; legacy SSH state does not prove its endpoint.");
|
|
17269
17775
|
}
|
|
@@ -17294,11 +17800,21 @@ async function observe2(request, transport) {
|
|
|
17294
17800
|
desiredByPath
|
|
17295
17801
|
);
|
|
17296
17802
|
const selectedPaths = new Set(selectedEntries.map((entry) => entry.path));
|
|
17803
|
+
const hasManagedBlock = selectedEntries.some((entry) => entry.mode === "managed-block");
|
|
17804
|
+
if (hasManagedBlock && request.carryDrift) throw new Error("--carry-drift is forbidden for managed instruction block adoption.");
|
|
17297
17805
|
const selected = [];
|
|
17298
17806
|
for (const entry of selectedEntries) {
|
|
17299
17807
|
const runtimePath = containedArtifactPath(targetRoot, entry.path);
|
|
17808
|
+
const managedBlock = entry.mode === "managed-block" ? exactManagedInstructionBlockSnapshot(
|
|
17809
|
+
await transport.readFile(runtimePath),
|
|
17810
|
+
managedInstructionSelector(entry.logicalSelector, entry.artifactType, entry.artifactName)
|
|
17811
|
+
) : void 0;
|
|
17812
|
+
if (managedBlock && managedBlock.hash !== entry.hash) {
|
|
17813
|
+
throw new Error(`Managed instruction block hash differs from its recorded manifest hash at ${entry.path}; adoption is refused.`);
|
|
17814
|
+
}
|
|
17815
|
+
const runtimeSnapshotDigest = hasManagedBlock ? await ownershipRuntimeSnapshot(runtimePath) : void 0;
|
|
17300
17816
|
const runtimeHash = await transport.pathExists(runtimePath) ? await transport.hashPath(runtimePath) : null;
|
|
17301
|
-
const drift = runtimeHash !== entry.hash;
|
|
17817
|
+
const drift = managedBlock ? false : runtimeHash !== entry.hash;
|
|
17302
17818
|
if (drift && request.carryDrift !== true) {
|
|
17303
17819
|
throw new Error(
|
|
17304
17820
|
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 +17837,28 @@ async function observe2(request, transport) {
|
|
|
17321
17837
|
...destinationEntryDigest ? { destinationEntryDigest } : {},
|
|
17322
17838
|
recordedHash: entry.hash,
|
|
17323
17839
|
runtimeHash,
|
|
17324
|
-
drift
|
|
17840
|
+
drift,
|
|
17841
|
+
...runtimeSnapshotDigest ? { runtimeSnapshotDigest } : {},
|
|
17842
|
+
...managedBlock ? { managedBlock } : {}
|
|
17325
17843
|
});
|
|
17326
17844
|
}
|
|
17845
|
+
const guardedInventory = hasManagedBlock ? await listInstallManifests(targetRoot, void 0, transport) : void 0;
|
|
17846
|
+
if (guardedInventory) {
|
|
17847
|
+
for (const item of guardedInventory) {
|
|
17848
|
+
await assertNoSymlinkPath(item.path);
|
|
17849
|
+
for (const other of item.manifest.entries) {
|
|
17850
|
+
const otherPath = containedArtifactPath(targetRoot, other.path);
|
|
17851
|
+
for (const entry of selectedEntries) {
|
|
17852
|
+
const path = containedArtifactPath(targetRoot, entry.path);
|
|
17853
|
+
const overlap = otherPath === path || otherPath.startsWith(`${path}/`) || path.startsWith(`${otherPath}/`);
|
|
17854
|
+
if (!overlap) continue;
|
|
17855
|
+
if (item.stateKey === input.sourceStateKey && otherPath === path && "workspaceOwner" in other && entryDigest(other) === entryDigest(entry)) continue;
|
|
17856
|
+
if (item.stateKey === input.destinationStateKey && otherPath === path && "workspaceOwner" in other && coversSourceEntry(other, entry, input.destinationOwner)) continue;
|
|
17857
|
+
throw new Error(`Overlapping or duplicate ownership claim at ${entry.path} in ${item.stateKey}; managed-block adoption is refused.`);
|
|
17858
|
+
}
|
|
17859
|
+
}
|
|
17860
|
+
}
|
|
17861
|
+
}
|
|
17327
17862
|
const remainingDuplicates = [];
|
|
17328
17863
|
const selectedByPath = new Map(selectedEntries.map((entry) => [entry.path, entry]));
|
|
17329
17864
|
for (const item of await listInstallManifests(targetRoot, adapter, transport)) {
|
|
@@ -17360,20 +17895,35 @@ async function observe2(request, transport) {
|
|
|
17360
17895
|
owner: input.destinationOwner,
|
|
17361
17896
|
workspaceRoot: input.workspaceRoot
|
|
17362
17897
|
},
|
|
17363
|
-
manifestInventoryRevision: await computeInstallManifestInventoryRevision(targetRoot, adapter, transport),
|
|
17898
|
+
manifestInventoryRevision: await computeInstallManifestInventoryRevision(targetRoot, hasManagedBlock ? void 0 : adapter, transport),
|
|
17364
17899
|
selected: selected.sort((left, right) => left.path.localeCompare(right.path)),
|
|
17365
17900
|
retained: retained.sort((left, right) => left.path.localeCompare(right.path) || left.owner.localeCompare(right.owner)),
|
|
17366
17901
|
remainingDuplicates: remainingDuplicates.sort((left, right) => left.stateKey.localeCompare(right.stateKey) || left.path.localeCompare(right.path)),
|
|
17367
17902
|
// 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
|
|
17903
|
+
desiredCoverage: request.desiredCoverage.filter((item) => selectedPaths.has(item.path)).map(({ path, artifactType, artifactName, kind, mode, logicalSelector }) => ({
|
|
17904
|
+
path,
|
|
17905
|
+
artifactType,
|
|
17906
|
+
artifactName,
|
|
17907
|
+
kind,
|
|
17908
|
+
...mode ? { mode, logicalSelector: managedInstructionSelector(logicalSelector, artifactType, artifactName) } : {}
|
|
17909
|
+
})).sort((left, right) => left.path.localeCompare(right.path) || left.artifactType.localeCompare(right.artifactType) || left.artifactName.localeCompare(right.artifactName)),
|
|
17910
|
+
...guardedInventory ? { metadataGuards: {
|
|
17911
|
+
inventory: guardedInventory.map((item) => ({ stateKey: item.stateKey, revision: item.manifest.revision })),
|
|
17912
|
+
runtime: selected.map((entry) => ({
|
|
17913
|
+
path: entry.path,
|
|
17914
|
+
hash: entry.runtimeHash,
|
|
17915
|
+
snapshotDigest: entry.runtimeSnapshotDigest,
|
|
17916
|
+
...entry.managedBlock ? { managedBlock: entry.managedBlock } : {}
|
|
17917
|
+
}))
|
|
17918
|
+
} } : {}
|
|
17369
17919
|
};
|
|
17370
|
-
return { ...withoutDigest, planDigest:
|
|
17920
|
+
return { ...withoutDigest, planDigest: createHash23("sha256").update(canonicalJson6(withoutDigest)).digest("hex") };
|
|
17371
17921
|
}
|
|
17372
17922
|
async function normalizeRequest2(request) {
|
|
17373
17923
|
if (request.destinationFleetId !== void 0) {
|
|
17374
17924
|
throw new Error("Legacy ownership adoption targets a nested workspace; a Fleet target uses fleet normalize or ownership retire-stale.");
|
|
17375
17925
|
}
|
|
17376
|
-
const targetRoot =
|
|
17926
|
+
const targetRoot = resolve40(request.targetRoot);
|
|
17377
17927
|
const installationType = request.installationType.trim();
|
|
17378
17928
|
const sourceStateKey = exactStateKey(request.adapter, request.sourceStateKey, installationType, "source");
|
|
17379
17929
|
const destinationStateKey = exactStateKey(request.adapter, request.destinationStateKey, installationType, "destination");
|
|
@@ -17385,22 +17935,22 @@ async function normalizeRequest2(request) {
|
|
|
17385
17935
|
const scope = await resolveWorkspaceOwnershipScope(request.workspaceRoot, { globalRoot: request.globalRoot });
|
|
17386
17936
|
const destinationOwner = workspaceOwnerForRoot(scope.root, scope.fleetId);
|
|
17387
17937
|
const workspaceRoot = await realpath3(request.workspaceRoot);
|
|
17388
|
-
const sourceRoot =
|
|
17938
|
+
const sourceRoot = resolve40(request.fromWorkspaceRoot);
|
|
17389
17939
|
const sourceOwner = workspaceOwnerForRoot(sourceRoot);
|
|
17390
17940
|
let sourceClass;
|
|
17391
|
-
if (sourceRoot === workspaceRoot || sourceRoot ===
|
|
17941
|
+
if (sourceRoot === workspaceRoot || sourceRoot === resolve40(request.workspaceRoot)) {
|
|
17392
17942
|
if (sourceOwner === destinationOwner) {
|
|
17393
17943
|
throw new Error(`${sourceOwner} is already the destination owner; ordinary install migration handles its legacy state.`);
|
|
17394
17944
|
}
|
|
17395
17945
|
sourceClass = "own-legacy-owner";
|
|
17396
17946
|
} else {
|
|
17397
17947
|
const sourceForms = [sourceRoot, ...await realpath3(sourceRoot).then((path) => [path], () => [])];
|
|
17398
|
-
const workspaceForms = [workspaceRoot,
|
|
17948
|
+
const workspaceForms = [workspaceRoot, resolve40(request.workspaceRoot), scope.root];
|
|
17399
17949
|
if (sourceForms.some((source) => workspaceForms.some((workspace) => containsPath2(source, workspace) || containsPath2(workspace, source)))) {
|
|
17400
17950
|
throw new Error(`Source root ${sourceRoot} contains or is contained by the destination workspace; it cannot be adopted as a foreign root.`);
|
|
17401
17951
|
}
|
|
17402
17952
|
for (const fleet of await listRegisteredFleets({ globalRoot: request.globalRoot })) {
|
|
17403
|
-
const fleetForms = [
|
|
17953
|
+
const fleetForms = [resolve40(fleet.root), ...await realpath3(fleet.root).then((path) => [path], () => [])];
|
|
17404
17954
|
if (sourceForms.some((source) => fleetForms.some((root) => containsPath2(root, source)))) {
|
|
17405
17955
|
throw new Error(`Source root ${sourceRoot} is inside registered Fleet '${fleet.id}'; use fleet normalize or ownership retire-stale.`);
|
|
17406
17956
|
}
|
|
@@ -17436,7 +17986,7 @@ async function selectLegacySource(input, stateKey, fingerprint, source, desiredB
|
|
|
17436
17986
|
);
|
|
17437
17987
|
}
|
|
17438
17988
|
for (const entry of source.entries) {
|
|
17439
|
-
if (entry.workspaceOwner === input.sourceOwner)
|
|
17989
|
+
if (entry.workspaceOwner === input.sourceOwner) assertSupportedLegacyEntry(entry, input.targetRoot);
|
|
17440
17990
|
}
|
|
17441
17991
|
const entries = [];
|
|
17442
17992
|
const retained = [];
|
|
@@ -17514,20 +18064,21 @@ async function readDestination(identity, stateKey, transport) {
|
|
|
17514
18064
|
}
|
|
17515
18065
|
return requireManifest(identity, stateKey, transport, "destination");
|
|
17516
18066
|
}
|
|
17517
|
-
function
|
|
17518
|
-
|
|
18067
|
+
function assertSupportedLegacyEntry(entry, targetRoot) {
|
|
18068
|
+
const supportedBlock = entry.mode === "managed-block" && entry.kind === "file" && entry.artifactType === "instructions";
|
|
18069
|
+
if (entry.mode && !supportedBlock || entry.mergeStrategy || entry.semanticPlugin || entry.semanticCommand || entry.executed) {
|
|
17519
18070
|
throw new Error(`Legacy ownership adoption supports only plain file or directory entries; ${entry.path} is not one.`);
|
|
17520
18071
|
}
|
|
17521
18072
|
containedArtifactPath(targetRoot, entry.path);
|
|
17522
18073
|
}
|
|
17523
18074
|
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);
|
|
18075
|
+
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
18076
|
}
|
|
17526
18077
|
function coversSourceEntry(destination, source, owner) {
|
|
17527
|
-
return destination.workspaceOwner === owner &&
|
|
18078
|
+
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
18079
|
}
|
|
17529
18080
|
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;
|
|
18081
|
+
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
18082
|
}
|
|
17532
18083
|
async function assertNoPendingJournals(targetRoot, adapter, transport) {
|
|
17533
18084
|
const pending = await listAllApplyJournals(targetRoot, adapter, transport);
|
|
@@ -17549,14 +18100,14 @@ function assertApplyPreconditions2(request) {
|
|
|
17549
18100
|
}
|
|
17550
18101
|
}
|
|
17551
18102
|
function containsPath2(parent, candidate) {
|
|
17552
|
-
const path =
|
|
18103
|
+
const path = relative17(parent, candidate);
|
|
17553
18104
|
return path === "" || !path.startsWith("..") && !isAbsolute12(path);
|
|
17554
18105
|
}
|
|
17555
18106
|
|
|
17556
18107
|
// src/version/policy.ts
|
|
17557
18108
|
import { execFile as execFile7 } from "child_process";
|
|
17558
18109
|
import { readFile as readFile40 } from "fs/promises";
|
|
17559
|
-
import { join as
|
|
18110
|
+
import { join as join55, resolve as resolve41 } from "path";
|
|
17560
18111
|
import { promisify as promisify7 } from "util";
|
|
17561
18112
|
import { parse as parseJsonc } from "jsonc-parser";
|
|
17562
18113
|
import { z as z15 } from "zod";
|
|
@@ -17653,7 +18204,7 @@ async function discoverVersionsFromSource(pkg, workspaceRoot) {
|
|
|
17653
18204
|
if (tagged.length > 0) return tagged;
|
|
17654
18205
|
}
|
|
17655
18206
|
if (driverName === "local") {
|
|
17656
|
-
const root =
|
|
18207
|
+
const root = resolve41(workspaceRoot, pkg.source);
|
|
17657
18208
|
const manifest2 = await readPackageManifest(root);
|
|
17658
18209
|
const current = manifest2 ? [{ version: manifest2.version, ref: pkg.requestedRef ?? root }] : [];
|
|
17659
18210
|
try {
|
|
@@ -17668,7 +18219,7 @@ async function discoverVersionsFromSource(pkg, workspaceRoot) {
|
|
|
17668
18219
|
}
|
|
17669
18220
|
const driver = getSourceDriver(driverName);
|
|
17670
18221
|
const resolved = await driver.resolve(pkg.source, {
|
|
17671
|
-
cacheRoot:
|
|
18222
|
+
cacheRoot: join55(workspaceRoot, ".agentwheel", "cache"),
|
|
17672
18223
|
mode: "tracking",
|
|
17673
18224
|
ref: pkg.requestedRef
|
|
17674
18225
|
});
|
|
@@ -17762,7 +18313,7 @@ function gitUrlFromSource(source) {
|
|
|
17762
18313
|
throw new Error(`Version discovery does not support Git source: ${source}`);
|
|
17763
18314
|
}
|
|
17764
18315
|
function versionCachePath(workspaceRoot) {
|
|
17765
|
-
return
|
|
18316
|
+
return join55(workspaceRoot, ".agentwheel", "cache", "version-index.json");
|
|
17766
18317
|
}
|
|
17767
18318
|
async function readVersionCache(path) {
|
|
17768
18319
|
if (!await pathExists(path)) return { schemaVersion: 1, sources: {} };
|
|
@@ -17779,7 +18330,7 @@ init_semver();
|
|
|
17779
18330
|
// src/profile/members.ts
|
|
17780
18331
|
import { execFile as execFile8 } from "child_process";
|
|
17781
18332
|
import { readFile as readFile41 } from "fs/promises";
|
|
17782
|
-
import { join as
|
|
18333
|
+
import { join as join56, resolve as resolve42 } from "path";
|
|
17783
18334
|
import { promisify as promisify8 } from "util";
|
|
17784
18335
|
import { z as z17 } from "zod";
|
|
17785
18336
|
|
|
@@ -17955,7 +18506,7 @@ async function invokeMemberStatus(member, parentWorkspace, chain, options, cliEn
|
|
|
17955
18506
|
let stderr = "";
|
|
17956
18507
|
try {
|
|
17957
18508
|
if (member.transport === "local") {
|
|
17958
|
-
const workspace =
|
|
18509
|
+
const workspace = resolve42(parentWorkspace, member.workspace);
|
|
17959
18510
|
const result = await execFileAsync8(process.execPath, [cliEntry, ...args], {
|
|
17960
18511
|
cwd: workspace,
|
|
17961
18512
|
env: env2,
|
|
@@ -18001,7 +18552,7 @@ async function runMemberAgentwheel(member, parentWorkspace, args, chain) {
|
|
|
18001
18552
|
process.execPath,
|
|
18002
18553
|
[process.argv[1], ...memberCommandArgs(member, args)],
|
|
18003
18554
|
{
|
|
18004
|
-
cwd:
|
|
18555
|
+
cwd: resolve42(parentWorkspace, member.workspace),
|
|
18005
18556
|
env: env2,
|
|
18006
18557
|
maxBuffer: 20 * 1024 * 1024
|
|
18007
18558
|
}
|
|
@@ -18081,7 +18632,7 @@ function memberFailure(member, health, error) {
|
|
|
18081
18632
|
};
|
|
18082
18633
|
}
|
|
18083
18634
|
function memberCachePath(workspaceRoot, profileName, memberId) {
|
|
18084
|
-
return
|
|
18635
|
+
return join56(workspaceRoot, ".agentwheel", "cache", "member-status", profileName, `${memberId}.json`);
|
|
18085
18636
|
}
|
|
18086
18637
|
async function readMemberCache(path) {
|
|
18087
18638
|
if (!await pathExists(path)) return void 0;
|
|
@@ -18107,7 +18658,7 @@ function assertNoCompositeCycle(workspaceRoot, profileName, chain) {
|
|
|
18107
18658
|
}
|
|
18108
18659
|
}
|
|
18109
18660
|
function compositeKey(workspaceRoot, profileName) {
|
|
18110
|
-
return `${
|
|
18661
|
+
return `${resolve42(workspaceRoot)}#${profileName}`;
|
|
18111
18662
|
}
|
|
18112
18663
|
|
|
18113
18664
|
// src/status/repository.ts
|
|
@@ -18154,10 +18705,10 @@ function valueAfter(lines, prefix) {
|
|
|
18154
18705
|
}
|
|
18155
18706
|
|
|
18156
18707
|
// src/catalogue/client.ts
|
|
18157
|
-
import { createHash as
|
|
18708
|
+
import { createHash as createHash24 } from "crypto";
|
|
18158
18709
|
import { readFile as readFile42, rm as rm15 } from "fs/promises";
|
|
18159
18710
|
import { homedir as homedir15 } from "os";
|
|
18160
|
-
import { join as
|
|
18711
|
+
import { join as join57 } from "path";
|
|
18161
18712
|
|
|
18162
18713
|
// src/model/catalogue.ts
|
|
18163
18714
|
import { z as z18 } from "zod";
|
|
@@ -18430,18 +18981,18 @@ var CatalogueClient = class {
|
|
|
18430
18981
|
}
|
|
18431
18982
|
return {
|
|
18432
18983
|
value: schema.parse(value),
|
|
18433
|
-
digest:
|
|
18984
|
+
digest: createHash24("sha256").update(bytes).digest("hex")
|
|
18434
18985
|
};
|
|
18435
18986
|
}
|
|
18436
18987
|
};
|
|
18437
18988
|
function defaultCatalogueCachePath() {
|
|
18438
|
-
return
|
|
18989
|
+
return join57(homedir15(), ".agentwheel", "catalogue-cache.json");
|
|
18439
18990
|
}
|
|
18440
18991
|
function sameSources2(a, b) {
|
|
18441
18992
|
return a.length === b.length && a.every((source, index) => source === b[index]);
|
|
18442
18993
|
}
|
|
18443
18994
|
function catalogueContentHash(enriched, vercel) {
|
|
18444
|
-
return
|
|
18995
|
+
return createHash24("sha256").update(JSON.stringify({ enriched, vercel })).digest("hex");
|
|
18445
18996
|
}
|
|
18446
18997
|
|
|
18447
18998
|
// src/search/index.ts
|
|
@@ -18803,9 +19354,9 @@ function includesToken(normalizedText, token) {
|
|
|
18803
19354
|
}
|
|
18804
19355
|
|
|
18805
19356
|
// src/semantic/index.ts
|
|
18806
|
-
import { createHash as
|
|
19357
|
+
import { createHash as createHash25 } from "crypto";
|
|
18807
19358
|
import { homedir as homedir16 } from "os";
|
|
18808
|
-
import { join as
|
|
19359
|
+
import { join as join58 } from "path";
|
|
18809
19360
|
import { env, pipeline } from "@huggingface/transformers";
|
|
18810
19361
|
var DEFAULT_SEMANTIC_INDEX_URL = "https://raw.githubusercontent.com/NestDevLab/agentwheel-registry/main/catalogue-semantic-index/gte-v1/";
|
|
18811
19362
|
var CONTRACT = {
|
|
@@ -18893,13 +19444,13 @@ var SemanticSearchClient = class {
|
|
|
18893
19444
|
if (!response.ok) throw new Error(`Semantic index file failed (${response.status}): ${descriptor.path}`);
|
|
18894
19445
|
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
18895
19446
|
if (bytes.byteLength !== descriptor.bytes) throw new Error(`Semantic index file size does not match: ${descriptor.path}`);
|
|
18896
|
-
const
|
|
18897
|
-
if (
|
|
19447
|
+
const digest3 = createHash25("sha256").update(bytes).digest("hex");
|
|
19448
|
+
if (digest3 !== descriptor.sha256) throw new Error(`Semantic index checksum does not match: ${descriptor.path}`);
|
|
18898
19449
|
return bytes;
|
|
18899
19450
|
}
|
|
18900
19451
|
};
|
|
18901
19452
|
async function embedQuery(query) {
|
|
18902
|
-
env.cacheDir =
|
|
19453
|
+
env.cacheDir = join58(homedir16(), ".agentwheel", "semantic-models");
|
|
18903
19454
|
const extractor = await pipeline("feature-extraction", CONTRACT.model.id, {
|
|
18904
19455
|
revision: CONTRACT.model.revision,
|
|
18905
19456
|
dtype: CONTRACT.model.dtype,
|
|
@@ -18983,9 +19534,10 @@ function ensureTrailingSlash(value) {
|
|
|
18983
19534
|
}
|
|
18984
19535
|
|
|
18985
19536
|
// src/trial/skill.ts
|
|
18986
|
-
|
|
19537
|
+
init_selection();
|
|
19538
|
+
import { createHash as createHash26 } from "crypto";
|
|
18987
19539
|
import { readFile as readFile43, stat as stat16 } from "fs/promises";
|
|
18988
|
-
import { join as
|
|
19540
|
+
import { join as join59 } from "path";
|
|
18989
19541
|
import { parse as parseYaml2 } from "yaml";
|
|
18990
19542
|
var MAX_TRIAL_SKILL_BYTES = 512 * 1024;
|
|
18991
19543
|
async function createSkillTrial(driver, resolved, selectors) {
|
|
@@ -18997,7 +19549,7 @@ async function createSkillTrial(driver, resolved, selectors) {
|
|
|
18997
19549
|
throw new Error("Skill trial requires exactly one selected skill. Use --skill <name> or --select skills/<name>.");
|
|
18998
19550
|
}
|
|
18999
19551
|
const artifact = skills[0];
|
|
19000
|
-
const path = artifact.kind === "dir" ?
|
|
19552
|
+
const path = artifact.kind === "dir" ? join59(artifact.sourcePath, "SKILL.md") : artifact.sourcePath;
|
|
19001
19553
|
const info = await stat16(path);
|
|
19002
19554
|
if (info.size > MAX_TRIAL_SKILL_BYTES) {
|
|
19003
19555
|
throw new Error(`Skill trial exceeds the ${MAX_TRIAL_SKILL_BYTES / 1024} KiB content limit.`);
|
|
@@ -19013,7 +19565,7 @@ async function createSkillTrial(driver, resolved, selectors) {
|
|
|
19013
19565
|
skill: {
|
|
19014
19566
|
name: artifact.name,
|
|
19015
19567
|
relativePath: artifact.relativePath,
|
|
19016
|
-
sha256:
|
|
19568
|
+
sha256: createHash26("sha256").update(content).digest("hex"),
|
|
19017
19569
|
frontmatter,
|
|
19018
19570
|
content
|
|
19019
19571
|
}
|
|
@@ -19032,10 +19584,10 @@ function readSkillFrontmatter(content, artifact) {
|
|
|
19032
19584
|
}
|
|
19033
19585
|
|
|
19034
19586
|
// src/lifecycle/fleet-normalize.ts
|
|
19035
|
-
import { createHash as
|
|
19036
|
-
import { readFile as readFile44, readdir as
|
|
19587
|
+
import { createHash as createHash27 } from "crypto";
|
|
19588
|
+
import { readFile as readFile44, readdir as readdir16, rm as rm16 } from "fs/promises";
|
|
19037
19589
|
import { homedir as homedir17 } from "os";
|
|
19038
|
-
import { dirname as dirname36, isAbsolute as isAbsolute13, join as
|
|
19590
|
+
import { dirname as dirname36, isAbsolute as isAbsolute13, join as join60, relative as relative18, resolve as resolve43 } from "path";
|
|
19039
19591
|
init_manifest2();
|
|
19040
19592
|
init_paths();
|
|
19041
19593
|
init_merge_removal();
|
|
@@ -19071,14 +19623,14 @@ async function planFleetNormalization(request) {
|
|
|
19071
19623
|
if (legacySelfNormalization && normalizedRequest.orphanedOwnerRoots?.length && normalizedRequest.packages?.includes(name)) {
|
|
19072
19624
|
packages.push({
|
|
19073
19625
|
name,
|
|
19074
|
-
declarationDigest: sha2567(
|
|
19626
|
+
declarationDigest: sha2567(canonicalJson8({ name, evidence: "orphaned-installed-state" }))
|
|
19075
19627
|
});
|
|
19076
19628
|
continue;
|
|
19077
19629
|
}
|
|
19078
19630
|
throw new Error(`Package '${name}' must be an existing duplicate in both source and destination fleets.`);
|
|
19079
19631
|
}
|
|
19080
|
-
const sourceDeclaration =
|
|
19081
|
-
const destinationDeclaration =
|
|
19632
|
+
const sourceDeclaration = canonicalJson8(sourcePackage);
|
|
19633
|
+
const destinationDeclaration = canonicalJson8(destinationPackage);
|
|
19082
19634
|
if (sourceDeclaration !== destinationDeclaration) {
|
|
19083
19635
|
throw new Error(
|
|
19084
19636
|
`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 +19667,7 @@ async function planFleetNormalization(request) {
|
|
|
19115
19667
|
packages,
|
|
19116
19668
|
installedState
|
|
19117
19669
|
};
|
|
19118
|
-
return { ...planWithoutDigest, planDigest: sha2567(
|
|
19670
|
+
return { ...planWithoutDigest, planDigest: sha2567(canonicalJson8(planWithoutDigest)) };
|
|
19119
19671
|
}
|
|
19120
19672
|
async function applyFleetNormalization(request, hooks = {}) {
|
|
19121
19673
|
if (!request.apply) throw new Error("Fleet normalization is dry-run by default; pass --apply with --plan-digest <digest>.");
|
|
@@ -19253,8 +19805,8 @@ async function assertNormalizationPostconditions(journal) {
|
|
|
19253
19805
|
}
|
|
19254
19806
|
for (const graphLock of journal.graphLocks) {
|
|
19255
19807
|
const raw = await readOptionalRecord(graphLock.path);
|
|
19256
|
-
const
|
|
19257
|
-
if (
|
|
19808
|
+
const digest3 = raw ? sha2567(canonicalJson8(await readGraphLock(graphLock.path))) : null;
|
|
19809
|
+
if (digest3 !== graphLock.afterDigest) {
|
|
19258
19810
|
throw new Error(`Fleet normalization graph-lock postcheck failed: ${graphLock.path}`);
|
|
19259
19811
|
}
|
|
19260
19812
|
}
|
|
@@ -19325,17 +19877,17 @@ function normalizeRequest3(request) {
|
|
|
19325
19877
|
...profile ? { profile } : {},
|
|
19326
19878
|
...agent ? { agent } : {},
|
|
19327
19879
|
...orphanedOwnerRoots ? { orphanedOwnerRoots } : {},
|
|
19328
|
-
...request.globalRoot ? { globalRoot:
|
|
19880
|
+
...request.globalRoot ? { globalRoot: resolve43(request.globalRoot) } : {}
|
|
19329
19881
|
};
|
|
19330
19882
|
}
|
|
19331
19883
|
function normalizeOrphanedOwnerRoot(value) {
|
|
19332
19884
|
const root = value.trim();
|
|
19333
19885
|
if (!root || !isAbsolute13(root)) throw new Error(`Invalid orphaned owner root '${value}'; expected an absolute workspace path.`);
|
|
19334
|
-
return
|
|
19886
|
+
return resolve43(root);
|
|
19335
19887
|
}
|
|
19336
19888
|
async function normalizationRoots(request) {
|
|
19337
19889
|
const destination = await showRegisteredFleet(request.destinationFleet, { globalRoot: request.globalRoot });
|
|
19338
|
-
const source = request.from === "user" ?
|
|
19890
|
+
const source = request.from === "user" ? resolve43(request.globalRoot ?? homedir17()) : (await showRegisteredFleet(request.from.slice("fleet:".length), { globalRoot: request.globalRoot })).root;
|
|
19339
19891
|
return { source, destination: destination.root };
|
|
19340
19892
|
}
|
|
19341
19893
|
async function withOrderedLocks(roots, fn) {
|
|
@@ -19343,7 +19895,7 @@ async function withOrderedLocks(roots, fn) {
|
|
|
19343
19895
|
if (index === roots.length) return fn();
|
|
19344
19896
|
const root = roots[index];
|
|
19345
19897
|
return withFilesystemLock(
|
|
19346
|
-
|
|
19898
|
+
join60(root, ".agentwheel", "fleet-normalize.lock"),
|
|
19347
19899
|
5e3,
|
|
19348
19900
|
() => run(index + 1),
|
|
19349
19901
|
"fleet normalization"
|
|
@@ -19442,10 +19994,10 @@ async function inspectInstalledState(source, destination, packageNames) {
|
|
|
19442
19994
|
const destinationGraphAfter = destinationGraph?.lock ?? destinationGraphFromSource(sourceGraph.lock, destinationState.targetFingerprint);
|
|
19443
19995
|
graphTransfers.push({
|
|
19444
19996
|
sourceGraphLockPath: sourceGraph.path,
|
|
19445
|
-
sourceGraphLockDigest: sha2567(
|
|
19997
|
+
sourceGraphLockDigest: sha2567(canonicalJson8(sourceGraph.lock)),
|
|
19446
19998
|
destinationGraphLockPath: destinationState.graphLockPath,
|
|
19447
|
-
destinationGraphLockDigest: destinationGraph ? sha2567(
|
|
19448
|
-
destinationGraphLockAfterDigest: sha2567(
|
|
19999
|
+
destinationGraphLockDigest: destinationGraph ? sha2567(canonicalJson8(destinationGraph.lock)) : null,
|
|
20000
|
+
destinationGraphLockAfterDigest: sha2567(canonicalJson8(destinationGraphAfter)),
|
|
19449
20001
|
targetKey: sourceGraph.targetKey,
|
|
19450
20002
|
adapter: destinationState.adapter,
|
|
19451
20003
|
targetFingerprint: destinationState.targetFingerprint
|
|
@@ -19550,7 +20102,7 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
19550
20102
|
let normalizedGraphCount = 0;
|
|
19551
20103
|
for (const graph of graphCandidates) {
|
|
19552
20104
|
const destinationState = await targetStateForGraph(fleet, graph, fleet.fleetId, allowOrphanedRetiredPackages);
|
|
19553
|
-
if (
|
|
20105
|
+
if (resolve43(destinationState.graphLockPath) === resolve43(graph.path)) {
|
|
19554
20106
|
const destinationManifest = (await collectManifestPaths([destinationState.manifestPath]))[0];
|
|
19555
20107
|
if (destinationManifest?.manifest.entries.some((entry) => entry.workspaceOwner === fleetOwner && entryMatchesPackages(entry, selected))) {
|
|
19556
20108
|
assertCorrelatedGraphManifest(
|
|
@@ -19565,7 +20117,7 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
19565
20117
|
}
|
|
19566
20118
|
}
|
|
19567
20119
|
const legacyState = await legacyTargetStateForGraph(fleet, graph, allowOrphanedRetiredPackages);
|
|
19568
|
-
if (
|
|
20120
|
+
if (resolve43(legacyState.graphLockPath) !== resolve43(graph.path)) {
|
|
19569
20121
|
throw new Error(`Legacy graph lock target identity is stale or noncanonical: ${graph.path}`);
|
|
19570
20122
|
}
|
|
19571
20123
|
const legacyManifest = (await collectManifestPaths([legacyState.manifestPath]))[0];
|
|
@@ -19673,7 +20225,7 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
19673
20225
|
)
|
|
19674
20226
|
});
|
|
19675
20227
|
if (!selectedArtifacts) {
|
|
19676
|
-
const fullDigest = sha2567(
|
|
20228
|
+
const fullDigest = sha2567(canonicalJson8(graph.lock));
|
|
19677
20229
|
const existingDestinationGraph = graphCandidates.find((candidate) => candidate.path === destinationState.graphLockPath);
|
|
19678
20230
|
if (existingDestinationGraph) assertGraphIsSubset(graph, existingDestinationGraph);
|
|
19679
20231
|
const destinationGraph = destinationGraphFromSource(graph.lock, destinationState.targetFingerprint);
|
|
@@ -19681,8 +20233,8 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
19681
20233
|
sourceGraphLockPath: graph.path,
|
|
19682
20234
|
sourceGraphLockDigest: fullDigest,
|
|
19683
20235
|
destinationGraphLockPath: destinationState.graphLockPath,
|
|
19684
|
-
destinationGraphLockDigest: existingDestinationGraph ? sha2567(
|
|
19685
|
-
destinationGraphLockAfterDigest: sha2567(
|
|
20236
|
+
destinationGraphLockDigest: existingDestinationGraph ? sha2567(canonicalJson8(existingDestinationGraph.lock)) : null,
|
|
20237
|
+
destinationGraphLockAfterDigest: sha2567(canonicalJson8(destinationGraph)),
|
|
19686
20238
|
targetKey: graph.targetKey,
|
|
19687
20239
|
adapter: graph.adapter,
|
|
19688
20240
|
targetFingerprint: destinationState.targetFingerprint
|
|
@@ -19740,7 +20292,7 @@ function targetKeysForAgent(scope, name) {
|
|
|
19740
20292
|
async function runtimeRoots(scope) {
|
|
19741
20293
|
const roots = /* @__PURE__ */ new Set([scope.root]);
|
|
19742
20294
|
if (scope.config.packages.some((pkg) => pkg.installationType === "user") || Object.values(scope.config.agents).some((agent) => agent.installationType === "user")) {
|
|
19743
|
-
roots.add(
|
|
20295
|
+
roots.add(resolve43(process.env.AGENTWHEEL_TEST_HOME || process.env.HOME || homedir17()));
|
|
19744
20296
|
}
|
|
19745
20297
|
for (const [name, agent] of Object.entries(scope.config.agents)) {
|
|
19746
20298
|
if (agent.transport === "ssh" || agent.transport === "local-run-as") throw new Error(`Installed-state normalization cannot inspect ${agent.transport} agent '${name}' locally.`);
|
|
@@ -19758,17 +20310,17 @@ async function runtimeRoots(scope) {
|
|
|
19758
20310
|
async function collectManifests(roots) {
|
|
19759
20311
|
const found = /* @__PURE__ */ new Map();
|
|
19760
20312
|
for (const root of roots) {
|
|
19761
|
-
const dir =
|
|
20313
|
+
const dir = join60(root, ".agentwheel");
|
|
19762
20314
|
let names;
|
|
19763
20315
|
try {
|
|
19764
|
-
names = await
|
|
20316
|
+
names = await readdir16(dir);
|
|
19765
20317
|
} catch (error) {
|
|
19766
20318
|
if (isMissing5(error)) continue;
|
|
19767
20319
|
throw error;
|
|
19768
20320
|
}
|
|
19769
20321
|
for (const name of names.sort((a, b) => a.localeCompare(b))) {
|
|
19770
20322
|
if (!name.endsWith(".install-manifest.json")) continue;
|
|
19771
|
-
const path =
|
|
20323
|
+
const path = join60(dir, name);
|
|
19772
20324
|
const raw = JSON.parse(await readFile44(path, "utf8"));
|
|
19773
20325
|
const parsed = installManifestSchema.parse(raw);
|
|
19774
20326
|
if (parsed.version !== 2) {
|
|
@@ -19777,14 +20329,14 @@ async function collectManifests(roots) {
|
|
|
19777
20329
|
}
|
|
19778
20330
|
continue;
|
|
19779
20331
|
}
|
|
19780
|
-
if (
|
|
20332
|
+
if (resolve43(parsed.targetRoot) !== resolve43(root)) {
|
|
19781
20333
|
throw new Error(`Install manifest target root does not match its enumerated runtime root: ${path}`);
|
|
19782
20334
|
}
|
|
19783
20335
|
const expectedPath = installManifestPath(parsed.targetRoot, parsed.adapter, {
|
|
19784
20336
|
installationType: parsed.installationType,
|
|
19785
20337
|
stateKey: parsed.stateKey
|
|
19786
20338
|
});
|
|
19787
|
-
if (
|
|
20339
|
+
if (resolve43(expectedPath) !== resolve43(path)) {
|
|
19788
20340
|
throw new Error(`Install manifest state identity does not match its canonical path: ${path}`);
|
|
19789
20341
|
}
|
|
19790
20342
|
found.set(path, { path, raw, manifest: { ...parsed, revision: computeManifestRevision(raw), legacy: false } });
|
|
@@ -19805,7 +20357,7 @@ async function collectManifestPaths(paths) {
|
|
|
19805
20357
|
installationType: parsed.installationType,
|
|
19806
20358
|
stateKey: parsed.stateKey
|
|
19807
20359
|
});
|
|
19808
|
-
if (
|
|
20360
|
+
if (resolve43(expectedPath) !== resolve43(path)) {
|
|
19809
20361
|
throw new Error(`Install manifest state identity does not match its canonical path: ${path}`);
|
|
19810
20362
|
}
|
|
19811
20363
|
found.push({ path, raw, manifest: { ...parsed, revision: computeManifestRevision(raw), legacy: false } });
|
|
@@ -19824,14 +20376,14 @@ function assertNoForeignSamePathOwnership(manifests, renderedPaths, allowedOwner
|
|
|
19824
20376
|
}
|
|
19825
20377
|
}
|
|
19826
20378
|
async function relevantGraphLocks(workspaceRoot, selected, targetKeys, include) {
|
|
19827
|
-
const root =
|
|
20379
|
+
const root = join60(workspaceRoot, ".agentwheel", "locks");
|
|
19828
20380
|
if (!await pathExists(root)) return [];
|
|
19829
20381
|
const results = [];
|
|
19830
20382
|
for (const path of await listFiles(root)) {
|
|
19831
20383
|
if (!path.endsWith(".graph-lock.json")) continue;
|
|
19832
20384
|
const lock = await readGraphLock(path);
|
|
19833
20385
|
if (!lock.canonical.roots.some((candidate2) => selected.has(candidate2.rootId))) continue;
|
|
19834
|
-
const parts =
|
|
20386
|
+
const parts = relative18(root, path).split(/[\\/]/);
|
|
19835
20387
|
if (parts.length !== 3) throw new Error(`Graph lock path is not canonical: ${path}`);
|
|
19836
20388
|
const [targetKey2, adapter] = parts;
|
|
19837
20389
|
if (targetKeys && !targetKeys.has(targetKey2)) continue;
|
|
@@ -19848,7 +20400,7 @@ async function relevantGraphLocks(workspaceRoot, selected, targetKeys, include)
|
|
|
19848
20400
|
const projection = relevantGraphProjection(lock, selected);
|
|
19849
20401
|
results.push({
|
|
19850
20402
|
...candidate,
|
|
19851
|
-
digest: sha2567(
|
|
20403
|
+
digest: sha2567(canonicalJson8(projection)),
|
|
19852
20404
|
artifactIdentities: lock.canonical.artifacts.filter((artifact) => artifact.owners.some((owner) => ownerMatchesGraphPackage(lock, owner, selected))).map(graphArtifactIdentity).sort()
|
|
19853
20405
|
});
|
|
19854
20406
|
}
|
|
@@ -19873,7 +20425,7 @@ async function hasRelevantLegacyManifestCandidate(scope, graph, selected) {
|
|
|
19873
20425
|
const legacyManifest = (await collectManifestPaths([legacyState.manifestPath]))[0];
|
|
19874
20426
|
if (legacyManifest?.manifest.entries.some((entry) => entryMatchesGraphPackages(entry, graph.lock, selected))) return true;
|
|
19875
20427
|
const stableState = await targetStateForGraph(scope, graph, scope.fleetId ?? null);
|
|
19876
|
-
if (
|
|
20428
|
+
if (resolve43(stableState.graphLockPath) !== resolve43(graph.path)) return false;
|
|
19877
20429
|
const stableManifest = (await collectManifestPaths([stableState.manifestPath]))[0];
|
|
19878
20430
|
return stableManifest?.manifest.entries.some((entry) => entryMatchesGraphPackages(entry, graph.lock, selected)) ?? false;
|
|
19879
20431
|
}
|
|
@@ -19894,7 +20446,7 @@ async function legacyManifestPathForConfiguredTarget(scope, graph) {
|
|
|
19894
20446
|
baseDir: scope.root
|
|
19895
20447
|
});
|
|
19896
20448
|
const targetRoot = resolveConfigPath(agent.root, scope.root);
|
|
19897
|
-
const installRoot =
|
|
20449
|
+
const installRoot = resolve43(installRootForAdapterInstallationType(adapter, targetRoot, agent.installationType, { kind: "local" }));
|
|
19898
20450
|
const stateKey = stateKeyFor(adapter.name, {
|
|
19899
20451
|
installationType: agent.installationType,
|
|
19900
20452
|
stateKey: agent.stateKey,
|
|
@@ -19962,7 +20514,7 @@ async function targetStateForGraph(scope, graph, identityFleetId = scope.fleetId
|
|
|
19962
20514
|
ssh: void 0,
|
|
19963
20515
|
...agent?.stateKey ? { stateKey: agent.stateKey } : {}
|
|
19964
20516
|
};
|
|
19965
|
-
const installRoot =
|
|
20517
|
+
const installRoot = resolve43(installRootForAdapterInstallationType(adapter, targetRoot, installationType, { kind: "local" }));
|
|
19966
20518
|
const targetIdentity = resolveTargetStateIdentity({
|
|
19967
20519
|
targetFingerprintParts: fingerprintParts,
|
|
19968
20520
|
workspaceRoot: scope.root,
|
|
@@ -19994,16 +20546,16 @@ async function targetStateForGraph(scope, graph, identityFleetId = scope.fleetId
|
|
|
19994
20546
|
async function installedTargetStateForGraph(scope, graph, manifests, owners, selected, identityFleetId = scope.fleetId ?? null) {
|
|
19995
20547
|
const stable = await targetStateForGraph(scope, graph, identityFleetId);
|
|
19996
20548
|
let state;
|
|
19997
|
-
if (
|
|
20549
|
+
if (resolve43(graph.path) === resolve43(stable.graphLockPath)) {
|
|
19998
20550
|
state = stable;
|
|
19999
20551
|
} else {
|
|
20000
20552
|
const released = await releasedTargetStateForGraph(scope, graph, identityFleetId);
|
|
20001
|
-
if (
|
|
20553
|
+
if (resolve43(graph.path) !== resolve43(released.graphLockPath)) {
|
|
20002
20554
|
throw new Error(`Graph lock target identity is stale or noncanonical: ${graph.path}`);
|
|
20003
20555
|
}
|
|
20004
20556
|
state = released;
|
|
20005
20557
|
}
|
|
20006
|
-
const manifest = manifests.find((candidate) =>
|
|
20558
|
+
const manifest = manifests.find((candidate) => resolve43(candidate.path) === resolve43(state.manifestPath));
|
|
20007
20559
|
if (!manifest) {
|
|
20008
20560
|
throw new Error(`Graph lock is not covered by its canonical install manifest: ${state.manifestPath}`);
|
|
20009
20561
|
}
|
|
@@ -20027,7 +20579,7 @@ async function releasedTargetStateForGraph(scope, graph, identityFleetId = scope
|
|
|
20027
20579
|
...current,
|
|
20028
20580
|
stateKey,
|
|
20029
20581
|
targetFingerprint,
|
|
20030
|
-
graphLockPath:
|
|
20582
|
+
graphLockPath: join60(dirname36(graph.path), `${targetFingerprint}.graph-lock.json`),
|
|
20031
20583
|
manifestPath: installManifestPath(current.installRoot, current.adapter, {
|
|
20032
20584
|
installationType: current.installationType,
|
|
20033
20585
|
stateKey
|
|
@@ -20035,7 +20587,7 @@ async function releasedTargetStateForGraph(scope, graph, identityFleetId = scope
|
|
|
20035
20587
|
};
|
|
20036
20588
|
}
|
|
20037
20589
|
function assertCorrelatedGraphManifest(graph, state, manifest, owners, selected) {
|
|
20038
|
-
if (manifest.manifest.adapter !== state.adapter || manifest.manifest.installationType !== state.installationType || manifest.manifest.stateKey !== state.stateKey ||
|
|
20590
|
+
if (manifest.manifest.adapter !== state.adapter || manifest.manifest.installationType !== state.installationType || manifest.manifest.stateKey !== state.stateKey || resolve43(manifest.manifest.targetRoot) !== resolve43(state.installRoot)) {
|
|
20039
20591
|
throw new Error(`Install manifest identity does not match graph target state: ${manifest.path}`);
|
|
20040
20592
|
}
|
|
20041
20593
|
const entries = manifest.manifest.entries.filter((entry) => entryMatchesGraphPackages(entry, graph.lock, selected));
|
|
@@ -20043,7 +20595,7 @@ function assertCorrelatedGraphManifest(graph, state, manifest, owners, selected)
|
|
|
20043
20595
|
throw new Error(`Graph lock is not covered by selected install-manifest contributions: ${graph.path}`);
|
|
20044
20596
|
}
|
|
20045
20597
|
const graphArtifacts = new Set(graph.artifactIdentities);
|
|
20046
|
-
const graphLockDigest = sha2567(
|
|
20598
|
+
const graphLockDigest = sha2567(canonicalJson8(graph.lock));
|
|
20047
20599
|
for (const entry of entries) {
|
|
20048
20600
|
if (!owners.has(entry.workspaceOwner)) {
|
|
20049
20601
|
throw new Error(`Graph lock manifest owner mismatch at ${manifest.path}: ${entry.workspaceOwner}.`);
|
|
@@ -20060,9 +20612,9 @@ async function legacyTargetStateForGraph(scope, graph, allowMissingNamedAgentPac
|
|
|
20060
20612
|
return releasedTargetStateForGraph(scope, graph, null, allowMissingNamedAgentPackages);
|
|
20061
20613
|
}
|
|
20062
20614
|
function assertGraphIsSubset(source, destination) {
|
|
20063
|
-
const sourceRoots = new Map(source.lock.canonical.roots.map((root) => [root.rootId,
|
|
20615
|
+
const sourceRoots = new Map(source.lock.canonical.roots.map((root) => [root.rootId, canonicalJson8(root)]));
|
|
20064
20616
|
for (const root of destination.lock.canonical.roots) {
|
|
20065
|
-
if (sourceRoots.get(root.rootId) !==
|
|
20617
|
+
if (sourceRoots.get(root.rootId) !== canonicalJson8(root)) {
|
|
20066
20618
|
throw new Error(
|
|
20067
20619
|
`Partially normalized destination graph diverges at root '${root.rootId}': ${destination.path}`
|
|
20068
20620
|
);
|
|
@@ -20092,7 +20644,7 @@ function destinationGraphFromSource(source, targetFingerprint) {
|
|
|
20092
20644
|
function mergeDestinationManifest(existing, state, generatedAt, desiredEntries, replaceOwners = /* @__PURE__ */ new Set()) {
|
|
20093
20645
|
if (existing) {
|
|
20094
20646
|
const manifest2 = existing.manifest;
|
|
20095
|
-
if (manifest2.adapter !== state.adapter || manifest2.installationType !== state.installationType || manifest2.stateKey !== state.stateKey ||
|
|
20647
|
+
if (manifest2.adapter !== state.adapter || manifest2.installationType !== state.installationType || manifest2.stateKey !== state.stateKey || resolve43(manifest2.targetRoot) !== state.installRoot) {
|
|
20096
20648
|
throw new Error(`Destination manifest identity diverges from the derived target: ${existing.path}`);
|
|
20097
20649
|
}
|
|
20098
20650
|
}
|
|
@@ -20152,7 +20704,7 @@ async function assertEquivalentRuntimeState(entry, sourcePath, destinationPath)
|
|
|
20152
20704
|
const content = await readFile44(path, "utf8");
|
|
20153
20705
|
if (hasMergeRemovalContent(entry.mergeRemoval)) {
|
|
20154
20706
|
assertExactMergeContribution(entry.mergeRemoval, entry.mergeStrategy, content);
|
|
20155
|
-
} else if (
|
|
20707
|
+
} else if (resolve43(sourcePath) === resolve43(destinationPath) && entry.mergeStrategy !== "codex-toml-mcp") {
|
|
20156
20708
|
assertExactMergeContribution({}, entry.mergeStrategy, content);
|
|
20157
20709
|
} else {
|
|
20158
20710
|
throw new Error(`Installed-state normalization cannot prove incomplete merge ownership at ${path}.`);
|
|
@@ -20163,8 +20715,8 @@ async function assertEquivalentRuntimeState(entry, sourcePath, destinationPath)
|
|
|
20163
20715
|
await assertEquivalentRuntimeBytes(sourcePath, destinationPath, entry.hash);
|
|
20164
20716
|
}
|
|
20165
20717
|
function renderedEntryPathForRoot(root, entryPath) {
|
|
20166
|
-
const normalizedRoot =
|
|
20167
|
-
const candidate =
|
|
20718
|
+
const normalizedRoot = resolve43(root);
|
|
20719
|
+
const candidate = resolve43(normalizedRoot, entryPath);
|
|
20168
20720
|
if (candidate !== normalizedRoot && !candidate.startsWith(`${normalizedRoot}/`)) {
|
|
20169
20721
|
throw new Error(`Install manifest entry escapes its target root: ${entryPath}`);
|
|
20170
20722
|
}
|
|
@@ -20255,8 +20807,8 @@ function ownerMatchesGraphPackage(lock, owner, selected) {
|
|
|
20255
20807
|
return ownerMatchesPackage(owner, selected) || lock.canonical.roots.some((root) => selected.has(root.rootId) && root.graphNodeId === owner);
|
|
20256
20808
|
}
|
|
20257
20809
|
function renderedEntryPath(manifest, entry) {
|
|
20258
|
-
const root =
|
|
20259
|
-
const candidate =
|
|
20810
|
+
const root = resolve43(manifest.targetRoot);
|
|
20811
|
+
const candidate = resolve43(root, entry.path);
|
|
20260
20812
|
if (candidate !== root && !candidate.startsWith(`${root}/`)) {
|
|
20261
20813
|
throw new Error(`Install manifest entry escapes its target root: ${entry.path}`);
|
|
20262
20814
|
}
|
|
@@ -20275,7 +20827,7 @@ function assertSimpleVerifiableEntry(entry, path) {
|
|
|
20275
20827
|
if (entry.kind !== "file" && entry.kind !== "dir") throw new Error(`Unsupported installed entry kind at ${path}.`);
|
|
20276
20828
|
}
|
|
20277
20829
|
function entryIdentity(entry) {
|
|
20278
|
-
return
|
|
20830
|
+
return canonicalJson8({
|
|
20279
20831
|
path: entry.path,
|
|
20280
20832
|
artifactType: entry.artifactType,
|
|
20281
20833
|
artifactName: entry.artifactName,
|
|
@@ -20293,7 +20845,7 @@ function entryIdentity(entry) {
|
|
|
20293
20845
|
});
|
|
20294
20846
|
}
|
|
20295
20847
|
function graphArtifactIdentity(artifact) {
|
|
20296
|
-
return
|
|
20848
|
+
return canonicalJson8({
|
|
20297
20849
|
graphNodeId: artifact.graphNodeId,
|
|
20298
20850
|
type: artifact.type,
|
|
20299
20851
|
name: artifact.name,
|
|
@@ -20307,7 +20859,7 @@ function graphArtifactIdentity(artifact) {
|
|
|
20307
20859
|
});
|
|
20308
20860
|
}
|
|
20309
20861
|
function graphEntryIdentity(entry) {
|
|
20310
|
-
return
|
|
20862
|
+
return canonicalJson8({
|
|
20311
20863
|
graphNodeId: entry.graphNodeId,
|
|
20312
20864
|
type: entry.artifactType,
|
|
20313
20865
|
name: entry.artifactName,
|
|
@@ -20411,7 +20963,7 @@ async function buildJournalGraphLockStates(plan) {
|
|
|
20411
20963
|
for (const transfer of plan.installedState.graphTransfers) {
|
|
20412
20964
|
const sourceBefore = JSON.parse(await readFile44(transfer.sourceGraphLockPath, "utf8"));
|
|
20413
20965
|
const sourceLock = await readGraphLock(transfer.sourceGraphLockPath);
|
|
20414
|
-
const sourceDigest = sha2567(
|
|
20966
|
+
const sourceDigest = sha2567(canonicalJson8(sourceLock));
|
|
20415
20967
|
if (sourceDigest !== transfer.sourceGraphLockDigest) {
|
|
20416
20968
|
throw new Error(`Source graph lock changed after planning: ${transfer.sourceGraphLockPath}`);
|
|
20417
20969
|
}
|
|
@@ -20429,9 +20981,9 @@ async function buildJournalGraphLockStates(plan) {
|
|
|
20429
20981
|
continue;
|
|
20430
20982
|
}
|
|
20431
20983
|
const destinationBefore = await readOptionalRecord(transfer.destinationGraphLockPath);
|
|
20432
|
-
const destinationBeforeDigest = destinationBefore ? sha2567(
|
|
20984
|
+
const destinationBeforeDigest = destinationBefore ? sha2567(canonicalJson8(await readGraphLock(transfer.destinationGraphLockPath))) : null;
|
|
20433
20985
|
const destinationAfter = destinationBefore ? await readGraphLock(transfer.destinationGraphLockPath) : destinationGraphFromSource(sourceLock, transfer.targetFingerprint);
|
|
20434
|
-
const destinationAfterDigest = sha2567(
|
|
20986
|
+
const destinationAfterDigest = sha2567(canonicalJson8(destinationAfter));
|
|
20435
20987
|
if (destinationBeforeDigest !== transfer.destinationGraphLockDigest || destinationAfterDigest !== transfer.destinationGraphLockAfterDigest) {
|
|
20436
20988
|
throw new Error(`Destination graph lock changed after planning: ${transfer.destinationGraphLockPath}`);
|
|
20437
20989
|
}
|
|
@@ -20541,8 +21093,8 @@ async function assertJournalRecoverable(journal) {
|
|
|
20541
21093
|
}
|
|
20542
21094
|
for (const graphLock of journal.graphLocks) {
|
|
20543
21095
|
const raw = await readOptionalRecord(graphLock.path);
|
|
20544
|
-
const
|
|
20545
|
-
if (
|
|
21096
|
+
const digest3 = raw ? sha2567(canonicalJson8(await readGraphLock(graphLock.path))) : null;
|
|
21097
|
+
if (digest3 !== graphLock.beforeDigest && digest3 !== graphLock.afterDigest) {
|
|
20546
21098
|
throw new Error(`Cannot recover fleet normalization because graph lock changed outside the transaction: ${graphLock.path}`);
|
|
20547
21099
|
}
|
|
20548
21100
|
}
|
|
@@ -20594,12 +21146,12 @@ function packageMap(config) {
|
|
|
20594
21146
|
return result;
|
|
20595
21147
|
}
|
|
20596
21148
|
function configRevision(config) {
|
|
20597
|
-
return sha2567(
|
|
21149
|
+
return sha2567(canonicalJson8(config));
|
|
20598
21150
|
}
|
|
20599
21151
|
function normalizationJournalPath(sourceRoot, destinationFleet) {
|
|
20600
|
-
return
|
|
21152
|
+
return join60(sourceRoot, ".agentwheel", `fleet-normalize-${destinationFleet}.journal.json`);
|
|
20601
21153
|
}
|
|
20602
|
-
function
|
|
21154
|
+
function canonicalJson8(value) {
|
|
20603
21155
|
return JSON.stringify(sortValue(value));
|
|
20604
21156
|
}
|
|
20605
21157
|
function sortValue(value) {
|
|
@@ -20610,7 +21162,7 @@ function sortValue(value) {
|
|
|
20610
21162
|
return value;
|
|
20611
21163
|
}
|
|
20612
21164
|
function sha2567(value) {
|
|
20613
|
-
return
|
|
21165
|
+
return createHash27("sha256").update(value).digest("hex");
|
|
20614
21166
|
}
|
|
20615
21167
|
function assertDigest(value) {
|
|
20616
21168
|
if (!/^[a-f0-9]{64}$/.test(value)) throw new Error(`Invalid plan digest: ${value}`);
|
|
@@ -20633,6 +21185,7 @@ var commandGovernance = {
|
|
|
20633
21185
|
"fleet normalize": "governed-apply-or-recover",
|
|
20634
21186
|
"cache prune": "governed-apply",
|
|
20635
21187
|
add: "governed-always",
|
|
21188
|
+
pin: "governed-always",
|
|
20636
21189
|
list: "incidental-cache",
|
|
20637
21190
|
search: "incidental-cache",
|
|
20638
21191
|
try: "incidental-cache",
|
|
@@ -20655,6 +21208,7 @@ var commandGovernance = {
|
|
|
20655
21208
|
"ownership handoff": "governed-unless-dry-run",
|
|
20656
21209
|
"ownership recovery-plan": "incidental-cache",
|
|
20657
21210
|
"ownership retire-stale": "governed-apply",
|
|
21211
|
+
"ownership release-claim": "governed-apply",
|
|
20658
21212
|
"ownership adopt-legacy": "governed-apply",
|
|
20659
21213
|
"mcp retire": "governed-apply",
|
|
20660
21214
|
eject: "governed-always",
|
|
@@ -20688,6 +21242,7 @@ function requiresCleanMutationPreflight(path) {
|
|
|
20688
21242
|
"init",
|
|
20689
21243
|
"fleet register",
|
|
20690
21244
|
"add",
|
|
21245
|
+
"pin",
|
|
20691
21246
|
"package migrate",
|
|
20692
21247
|
"remember",
|
|
20693
21248
|
"journal abort"
|
|
@@ -20761,25 +21316,34 @@ fleetCommand.command("normalize").description("plan or apply duplicate desired-s
|
|
|
20761
21316
|
});
|
|
20762
21317
|
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
21318
|
const targetRoot = await workspaceContextRootFromOptions(normalizeRuntimeScopeOptions(options));
|
|
20764
|
-
const cacheRoot =
|
|
21319
|
+
const cacheRoot = join61(targetRoot, ".agentwheel", "cache");
|
|
20765
21320
|
const result = await pruneGitCache(cacheRoot, { keepSnapshots: options.keep, dryRun: !options.apply });
|
|
20766
21321
|
const verb = options.apply ? "Removed" : "Would remove";
|
|
20767
21322
|
for (const path of result.removedPaths) console.log(`${verb} ${path}`);
|
|
20768
21323
|
console.log(`${options.apply ? "Pruned" : "Preview"}: ${result.removedPaths.length} snapshots; retained ${result.retainedPaths.length}.`);
|
|
20769
21324
|
});
|
|
20770
|
-
program.command("add").description("add a package to .agentwheel/config.json without touching runtimes").argument("<source>", "package source").option("--driver <driver>", "source driver (local, git, skillkit, vercel-skills, mcp-registry, or clawhub)").option("--adapter <adapter>", "built-in adapter", "openclaw").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>", "workspace root").option("--mode <mode>", "pinned or tracking", "pinned").option("--version <range>", "root package version policy (exact, ~, ^, or *)").option("--name <name>", "package alias").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 on future installs", false).option("--suggestion <alias>", "include one suggested companion alias on future installs (repeatable or comma-separated)", collectSuggestionOption, []).option("--override <source-or-package::type/name>", "allow this package to replace a colliding artifact (repeatable)", collectOverrideOption, []).action(async (source, options) => {
|
|
21325
|
+
program.command("add").description("add a package to .agentwheel/config.json without touching runtimes").argument("<source>", "package source").option("--driver <driver>", "source driver (local, git, skillkit, vercel-skills, mcp-registry, or clawhub)").option("--adapter <adapter>", "built-in adapter", "openclaw").option("--runtimes <adapters>", "restrict package runtimes (repeatable or comma-separated)", collectSelectOption, []).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>", "workspace root").option("--mode <mode>", "pinned or tracking", "pinned").option("--version <range>", "root package version policy (exact, ~, ^, or *)").option("--name <name>", "package alias").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 on future installs", false).option("--suggestion <alias>", "include one suggested companion alias on future installs (repeatable or comma-separated)", collectSuggestionOption, []).option("--override <source-or-package::type/name>", "allow this package to replace a colliding artifact (repeatable)", collectOverrideOption, []).action(async (source, options) => {
|
|
20771
21326
|
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
20772
21327
|
const targetRoot = await workspaceRootFromOptions(normalizedOptions);
|
|
20773
21328
|
const entry = await packageEntryFromSource(source, targetRoot, normalizedOptions);
|
|
20774
21329
|
await writeWorkspaceConfig(targetRoot, upsertPackage(await readWorkspaceConfig(targetRoot), entry));
|
|
20775
21330
|
console.log(`Added ${entry.name}. Preview: agentwheel plan - Apply: agentwheel install`);
|
|
20776
21331
|
});
|
|
21332
|
+
program.command("pin").description("pin configured package aliases without resolving sources or touching runtimes").argument("[alias]", "exact configured package alias").argument("[commit]", "exact 40-hex commit SHA").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("--pins-file <path>", "JSON array of {alias, commit, select?} pins in one mutation").option("--select <type/name>", "replace the complete selection (repeatable or comma-separated)", collectSelectOption).action(async (alias, commit, options) => {
|
|
21333
|
+
const workspaceRoot = await workspaceRootFromOptions(normalizeRuntimeScopeOptions(options));
|
|
21334
|
+
if (options.pinsFile ? alias !== void 0 || commit !== void 0 || options.select !== void 0 : !alias || !commit) {
|
|
21335
|
+
throw new Error("Choose pin <alias> <commit> [--select ...] or pin --pins-file <path>, exclusively.");
|
|
21336
|
+
}
|
|
21337
|
+
const pins = options.pinsFile ? JSON.parse(await readFile45(resolve44(options.pinsFile), "utf8")) : [{ alias, commit, ...options.select === void 0 ? {} : { select: options.select } }];
|
|
21338
|
+
await pinConfiguredPackages(workspaceRoot, pins);
|
|
21339
|
+
console.log("Pinned configured package revisions. Preview: agentwheel plan - Apply: agentwheel install");
|
|
21340
|
+
});
|
|
20777
21341
|
program.command("list").description("list artifacts exposed by a package source").argument("<source>", "package source").option("--driver <driver>", "source driver").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("--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, []).action(async (source, options) => {
|
|
20778
21342
|
const targetRoot = await workspaceContextRootFromOptions(normalizeRuntimeScopeOptions(options));
|
|
20779
21343
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
20780
21344
|
const selectedArtifacts = selectedArtifactsFromOptionsOrRegistry(options, resolvedInput.registryEntry);
|
|
20781
21345
|
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:
|
|
21346
|
+
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join61(targetRoot, ".agentwheel", "cache") }))));
|
|
20783
21347
|
try {
|
|
20784
21348
|
const artifacts = filterArtifactsBySelection(await driver.list(resolved), selectedArtifacts);
|
|
20785
21349
|
for (const artifact of artifacts) {
|
|
@@ -20848,7 +21412,7 @@ program.command("try").description("read and validate one skill for the current
|
|
|
20848
21412
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
20849
21413
|
const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
|
|
20850
21414
|
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, {
|
|
20851
|
-
cacheRoot:
|
|
21415
|
+
cacheRoot: join61(targetRoot, ".agentwheel", "cache")
|
|
20852
21416
|
}))));
|
|
20853
21417
|
try {
|
|
20854
21418
|
const trial = await createSkillTrial(driver, resolved, selectedArtifactsFromOptionsOrRegistry(options, resolvedInput.registryEntry));
|
|
@@ -20870,7 +21434,7 @@ program.command("scan").description("scan a package source for validation findin
|
|
|
20870
21434
|
const targetRoot = await workspaceContextRootFromOptions(normalizeRuntimeScopeOptions(options));
|
|
20871
21435
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
20872
21436
|
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:
|
|
21437
|
+
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join61(targetRoot, ".agentwheel", "cache") }))));
|
|
20874
21438
|
try {
|
|
20875
21439
|
const result = await driver.scan(resolved);
|
|
20876
21440
|
if (result.findings.length === 0) {
|
|
@@ -20885,10 +21449,10 @@ program.command("scan").description("scan a package source for validation findin
|
|
|
20885
21449
|
await releaseGitSnapshotLease(resolved.cacheLeasePath);
|
|
20886
21450
|
}
|
|
20887
21451
|
});
|
|
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) => {
|
|
21452
|
+
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
21453
|
await runInstallCommand(source, { ...options, dryRun: true }, { apply: false });
|
|
20890
21454
|
});
|
|
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) => {
|
|
21455
|
+
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
21456
|
await runInstallCommand(source, options, { apply: !options.dryRun });
|
|
20893
21457
|
});
|
|
20894
21458
|
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 +21615,48 @@ program.command("remember").description("append text to the local instructions o
|
|
|
21051
21615
|
console.log(nextInstallNudge());
|
|
21052
21616
|
});
|
|
21053
21617
|
var ownershipCommand = program.command("ownership").description("inspect and transfer manifest ownership without rewriting runtime artifacts");
|
|
21618
|
+
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) => {
|
|
21619
|
+
const options = command.optsWithGlobals();
|
|
21620
|
+
if (!options.reason) throw new Error("Ownership release requires --reason <why> for planning and applying.");
|
|
21621
|
+
const normalized = normalizeRuntimeScopeOptions(options);
|
|
21622
|
+
const targets = await resolveCliTargets(normalized);
|
|
21623
|
+
if (targets.length !== 1) throw new Error("Ownership release requires exactly one target.");
|
|
21624
|
+
const target = targets[0];
|
|
21625
|
+
const adapterOptions = adapterOptionsForTarget(target, normalized);
|
|
21626
|
+
const adapter = await resolveAdapterForTarget(target, adapterOptions);
|
|
21627
|
+
const installationType = normalized.installationType ?? target.installationType ?? resolveInstallationTypeForAdapter(adapter);
|
|
21628
|
+
const state = installStateForTarget(target, adapter, adapterOptions, installationType);
|
|
21629
|
+
const request = {
|
|
21630
|
+
workspaceRoot: await workspaceRootFromOptions(normalized),
|
|
21631
|
+
fromWorkspaceRoot: normalizeCliPath(options.fromWorkspaceRoot),
|
|
21632
|
+
fromFleetId: options.fromFleetId,
|
|
21633
|
+
targetRoot: state.installRoot,
|
|
21634
|
+
adapter: adapter.name,
|
|
21635
|
+
installationType,
|
|
21636
|
+
stateKey: options.stateKey,
|
|
21637
|
+
selector,
|
|
21638
|
+
path: options.path,
|
|
21639
|
+
reason: options.reason,
|
|
21640
|
+
planDigest: options.planDigest,
|
|
21641
|
+
expectedSourceRevision: options.expectedSourceRevision,
|
|
21642
|
+
expectedInventoryRevision: options.expectedInventoryRevision,
|
|
21643
|
+
operationId: options.operationId,
|
|
21644
|
+
noCommit: options.commit === false,
|
|
21645
|
+
transport: transportForTarget(target)
|
|
21646
|
+
};
|
|
21647
|
+
const result = options.apply ? await applyReleaseOwnership(request) : await planReleaseOwnership(request);
|
|
21648
|
+
if (options.json) {
|
|
21649
|
+
console.log(JSON.stringify(result, null, 2));
|
|
21650
|
+
return;
|
|
21651
|
+
}
|
|
21652
|
+
console.log(`${options.apply ? "Released ownership claim" : "Ownership release plan"}: ${result.selector} at ${result.path}`);
|
|
21653
|
+
console.log(`Owner: ${result.owner}`);
|
|
21654
|
+
console.log(`Source: ${result.source.stateKey} revision ${result.source.revision}`);
|
|
21655
|
+
console.log(`Recorded hash: ${result.recordedHash}; actual runtime hash: ${result.runtimeHash}`);
|
|
21656
|
+
console.log(`Inventory revision: ${result.manifestInventoryRevision}`);
|
|
21657
|
+
console.log(`Plan digest: ${result.planDigest}`);
|
|
21658
|
+
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.");
|
|
21659
|
+
});
|
|
21054
21660
|
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
21661
|
if (!options.dryRun && (!options.expectedHash || !options.expectedRevision)) {
|
|
21056
21662
|
throw new Error("Applying an ownership handoff requires --expected-hash and --expected-revision from a reviewed --dry-run.");
|
|
@@ -21078,7 +21684,7 @@ ownershipCommand.command("handoff").description("transfer one managed artifact b
|
|
|
21078
21684
|
const toWorkspaceRoot = normalizeCliPath(options.toWorkspaceRoot);
|
|
21079
21685
|
if (options.toFleet) {
|
|
21080
21686
|
const fleet = await showRegisteredFleet(options.toFleet);
|
|
21081
|
-
if (
|
|
21687
|
+
if (resolve44(fleet.root) !== resolve44(toWorkspaceRoot)) {
|
|
21082
21688
|
throw new Error(`Destination fleet '${options.toFleet}' is registered at ${fleet.root}, not ${toWorkspaceRoot}.`);
|
|
21083
21689
|
}
|
|
21084
21690
|
}
|
|
@@ -21119,8 +21725,8 @@ ownershipCommand.command("recovery-plan").description("write a read-only per-pat
|
|
|
21119
21725
|
const adapter = await resolveAdapterForTarget(target, {});
|
|
21120
21726
|
const transport = transportForTarget(target);
|
|
21121
21727
|
const requestedReport = normalizeCliPath(options.report);
|
|
21122
|
-
const reportPath =
|
|
21123
|
-
const reportInsideTarget =
|
|
21728
|
+
const reportPath = join61(await realpath4(dirname37(requestedReport)), basename24(requestedReport));
|
|
21729
|
+
const reportInsideTarget = relative19(resolve44(target.targetRoot), reportPath);
|
|
21124
21730
|
if (reportInsideTarget === "" || !reportInsideTarget.startsWith("..") && !isAbsolute14(reportInsideTarget)) {
|
|
21125
21731
|
throw new Error("Recovery report must be outside the runtime target root.");
|
|
21126
21732
|
}
|
|
@@ -21151,7 +21757,7 @@ ownershipCommand.command("retire-stale").description("retire stale source-manife
|
|
|
21151
21757
|
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
21152
21758
|
const fleet = await showRegisteredFleet(options.fleet);
|
|
21153
21759
|
const toWorkspaceRoot = normalizeCliPath(options.toWorkspaceRoot);
|
|
21154
|
-
if (
|
|
21760
|
+
if (resolve44(fleet.root) !== resolve44(toWorkspaceRoot)) {
|
|
21155
21761
|
throw new Error(`Destination Fleet '${options.fleet}' is registered at ${fleet.root}, not ${toWorkspaceRoot}.`);
|
|
21156
21762
|
}
|
|
21157
21763
|
const targets = await resolveCliTargets(normalizedOptions);
|
|
@@ -21159,7 +21765,7 @@ ownershipCommand.command("retire-stale").description("retire stale source-manife
|
|
|
21159
21765
|
throw new Error(`Stale ownership retirement requires exactly one destination Fleet target, found ${targets.length}.`);
|
|
21160
21766
|
}
|
|
21161
21767
|
const target = targets[0];
|
|
21162
|
-
if (target.fleetId !== options.fleet ||
|
|
21768
|
+
if (target.fleetId !== options.fleet || resolve44(target.workspaceRoot) !== resolve44(fleet.root)) {
|
|
21163
21769
|
throw new Error(`Resolved target does not belong to destination Fleet '${options.fleet}'.`);
|
|
21164
21770
|
}
|
|
21165
21771
|
const adapterOptions = adapterOptionsForTarget(target, normalizedOptions);
|
|
@@ -21231,7 +21837,7 @@ ownershipCommand.command("retire-stale").description("retire stale source-manife
|
|
|
21231
21837
|
console.log(`Plan digest: ${result.planDigest}`);
|
|
21232
21838
|
if (!options.apply) console.log(`Apply: ${applyCommand}`);
|
|
21233
21839
|
});
|
|
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
|
|
21840
|
+
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
21841
|
if (Boolean(options.agent) === Boolean(options.profile)) {
|
|
21236
21842
|
throw new Error("Legacy ownership adoption requires exactly one of --agent or --profile.");
|
|
21237
21843
|
}
|
|
@@ -21281,7 +21887,7 @@ ownershipCommand.command("adopt-legacy").description("move proven legacy-keyed o
|
|
|
21281
21887
|
];
|
|
21282
21888
|
for (const { flag, requested, resolved, none } of requestedAdapterSettings) {
|
|
21283
21889
|
if (requested === void 0) continue;
|
|
21284
|
-
if (resolved !== void 0 &&
|
|
21890
|
+
if (resolved !== void 0 && resolve44(target.workspaceRoot, requested) === resolve44(target.workspaceRoot, resolved)) continue;
|
|
21285
21891
|
throw new Error(`${flag} ${requested} does not match what install resolves for the configured packages: ${resolved ?? none}.`);
|
|
21286
21892
|
}
|
|
21287
21893
|
const adapter = await resolveAdapterForTarget(group.target, adapterOptions);
|
|
@@ -21451,7 +22057,7 @@ journalCommand.command("list").description("show pending apply journals for reso
|
|
|
21451
22057
|
if (!journal) continue;
|
|
21452
22058
|
pending += 1;
|
|
21453
22059
|
console.log(`PENDING ${state.adapter.name}/${state.installationType} at ${state.installRoot}`);
|
|
21454
|
-
console.log(` journal: ${
|
|
22060
|
+
console.log(` journal: ${join61(state.installRoot, ".agentwheel", `${state.state.stateKey}.apply-journal.json`)}`);
|
|
21455
22061
|
console.log(` stateKey: ${state.state.stateKey}`);
|
|
21456
22062
|
console.log(` createdAt: ${journal.createdAt}`);
|
|
21457
22063
|
console.log(` updatedAt: ${journal.updatedAt}`);
|
|
@@ -21515,7 +22121,11 @@ program.command("doctor").description("check agentwheel runtime setup and compan
|
|
|
21515
22121
|
await printDoctor(targets[0], normalizedOptions);
|
|
21516
22122
|
});
|
|
21517
22123
|
async function runInstallCommand(nameOrSource, options, behavior) {
|
|
21518
|
-
const normalizedOptions = normalizeRuntimeScopeOptions(
|
|
22124
|
+
const normalizedOptions = normalizeRuntimeScopeOptions({
|
|
22125
|
+
...options,
|
|
22126
|
+
forceConflict: options.forceConflict || options.adopt,
|
|
22127
|
+
replaceConflict: options.replaceConflict || options.adopt
|
|
22128
|
+
}, { defaultUser: shouldDefaultUserInstall(nameOrSource, options) });
|
|
21519
22129
|
const outputFormat = effectivePlanOutputFormat(normalizedOptions);
|
|
21520
22130
|
const composite = await resolveSelectedCompositeProfile(normalizedOptions);
|
|
21521
22131
|
if (composite) {
|
|
@@ -21797,14 +22407,14 @@ async function packageEntryFromSource(source, targetRoot, options) {
|
|
|
21797
22407
|
);
|
|
21798
22408
|
}
|
|
21799
22409
|
const bundle = options.deferGraphRendering ? await stageSourceRaw(driver, resolvedSource, {
|
|
21800
|
-
cacheRoot:
|
|
22410
|
+
cacheRoot: join61(targetRoot, ".agentwheel", "cache"),
|
|
21801
22411
|
mode: options.mode,
|
|
21802
22412
|
ref: initialVersion?.ref,
|
|
21803
22413
|
frozenLock: lockMode
|
|
21804
22414
|
}) : await stageSource(driver, resolvedSource, {
|
|
21805
22415
|
workspaceRoot: targetRoot,
|
|
21806
22416
|
adapter,
|
|
21807
|
-
cacheRoot:
|
|
22417
|
+
cacheRoot: join61(targetRoot, ".agentwheel", "cache"),
|
|
21808
22418
|
mode: options.mode,
|
|
21809
22419
|
ref: initialVersion?.ref,
|
|
21810
22420
|
frozenLock: lockMode,
|
|
@@ -21822,6 +22432,7 @@ async function packageEntryFromSource(source, targetRoot, options) {
|
|
|
21822
22432
|
source: resolvedSource,
|
|
21823
22433
|
driver: driverName,
|
|
21824
22434
|
adapter: adapter.name,
|
|
22435
|
+
runtimes: options.runtimes?.length ? options.runtimes : void 0,
|
|
21825
22436
|
installationType,
|
|
21826
22437
|
adapterConfig: options.adapterConfig,
|
|
21827
22438
|
adapterModule: options.adapterModule,
|
|
@@ -22412,7 +23023,7 @@ function scopeUpdatePlanToDependencies(result, selectors, previousLock, manifest
|
|
|
22412
23023
|
selectedPreviousNodeIds,
|
|
22413
23024
|
selectedRootIds
|
|
22414
23025
|
);
|
|
22415
|
-
const graphLockDigest =
|
|
23026
|
+
const graphLockDigest = createHash28("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
|
|
22416
23027
|
return {
|
|
22417
23028
|
...result,
|
|
22418
23029
|
bundle: { ...result.bundle, graphLock },
|
|
@@ -22538,7 +23149,7 @@ function operationBelongsToDependencyUpdate(operation, allowedNodeIds, allowedNa
|
|
|
22538
23149
|
function scopeInstallPlanToArtifact(result, rootId, focused, manifest) {
|
|
22539
23150
|
const scoped = scopePlanOperationsToArtifact(result, rootId, focused, manifest);
|
|
22540
23151
|
const graphLock = focusedGraphLockForInstall(scoped.bundle.graphLock, rootId, focused);
|
|
22541
|
-
const graphLockDigest =
|
|
23152
|
+
const graphLockDigest = createHash28("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
|
|
22542
23153
|
return {
|
|
22543
23154
|
...scoped,
|
|
22544
23155
|
bundle: { ...scoped.bundle, graphLock },
|
|
@@ -22549,7 +23160,7 @@ function scopeInstallPlanToArtifact(result, rootId, focused, manifest) {
|
|
|
22549
23160
|
function scopeUpdatePlanToArtifact(result, rootId, focused, previousLock, manifest) {
|
|
22550
23161
|
const scoped = scopePlanOperationsToArtifact(result, rootId, focused, manifest, previousLock);
|
|
22551
23162
|
const graphLock = mergeFocusedArtifactGraphLock(scoped.bundle.graphLock, previousLock, rootId, focused);
|
|
22552
|
-
const graphLockDigest =
|
|
23163
|
+
const graphLockDigest = createHash28("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
|
|
22553
23164
|
return {
|
|
22554
23165
|
...scoped,
|
|
22555
23166
|
bundle: { ...scoped.bundle, graphLock },
|
|
@@ -22844,7 +23455,7 @@ function scopeUpdatePlanToRoot(result, rootId, previousLock, manifest) {
|
|
|
22844
23455
|
selectedPreviousNodeIds,
|
|
22845
23456
|
/* @__PURE__ */ new Set([rootId])
|
|
22846
23457
|
);
|
|
22847
|
-
const graphLockDigest =
|
|
23458
|
+
const graphLockDigest = createHash28("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
|
|
22848
23459
|
return {
|
|
22849
23460
|
...scoped,
|
|
22850
23461
|
bundle: { ...scoped.bundle, graphLock },
|
|
@@ -22916,7 +23527,7 @@ function keepManifestEntryOperation(entry, targetRoot, scopeDescription, operati
|
|
|
22916
23527
|
artifactType: entry.artifactType,
|
|
22917
23528
|
artifactName: entry.artifactName,
|
|
22918
23529
|
kind: entry.kind,
|
|
22919
|
-
destPath: operation?.destPath ??
|
|
23530
|
+
destPath: operation?.destPath ?? join61(targetRoot, entry.path),
|
|
22920
23531
|
relativeDestPath: entry.path,
|
|
22921
23532
|
desiredHash: entry.sourceHash,
|
|
22922
23533
|
currentHash: operation?.currentHash ?? entry.hash,
|
|
@@ -23117,7 +23728,7 @@ function installRootContextForTarget(target) {
|
|
|
23117
23728
|
return { kind: target.transport, homeRoot: target.runAs?.home };
|
|
23118
23729
|
}
|
|
23119
23730
|
async function withFreshReadOnlyGraphPlan(target, adapter, adapterOptions, installationType, packages, use) {
|
|
23120
|
-
const stage = await mkdtemp6(
|
|
23731
|
+
const stage = await mkdtemp6(join61(tmpdir7(), "agentwheel-recovery-"));
|
|
23121
23732
|
const priorTmpdir = process.env.TMPDIR;
|
|
23122
23733
|
try {
|
|
23123
23734
|
process.env.TMPDIR = stage;
|
|
@@ -23149,8 +23760,8 @@ async function withFreshReadOnlyGraphPlan(target, adapter, adapterOptions, insta
|
|
|
23149
23760
|
yes: true,
|
|
23150
23761
|
freshGraphOnly: true,
|
|
23151
23762
|
deferForeignStateCheck: true,
|
|
23152
|
-
cacheRoot:
|
|
23153
|
-
registryCachePath:
|
|
23763
|
+
cacheRoot: join61(stage, "source-cache"),
|
|
23764
|
+
registryCachePath: join61(stage, "registry.json")
|
|
23154
23765
|
});
|
|
23155
23766
|
return await use(graphPlan);
|
|
23156
23767
|
} finally {
|
|
@@ -23237,7 +23848,7 @@ async function resolveCliTargetState(target, options) {
|
|
|
23237
23848
|
migration: prior.migration,
|
|
23238
23849
|
targetStateFilePreconditions: {
|
|
23239
23850
|
graphLockPath: stableGraphLockPath,
|
|
23240
|
-
graphLockRevision: stableLock ?
|
|
23851
|
+
graphLockRevision: stableLock ? createHash28("sha256").update(canonicalGraphLockJson(stableLock)).digest("hex") : null,
|
|
23241
23852
|
sourceLockRevision: computeSourceLockRevision(stableSourceLock)
|
|
23242
23853
|
}
|
|
23243
23854
|
};
|
|
@@ -23260,7 +23871,7 @@ async function collectTargetStatus(target, options) {
|
|
|
23260
23871
|
const pending = await collectPendingInstallWork(target, options);
|
|
23261
23872
|
const foreignObservations = pending.foreignStateObservations;
|
|
23262
23873
|
const effectiveGraphLock = graphLock ?? pending.observedGraphLock;
|
|
23263
|
-
const observedManifestRevision = foreignObservations.length > 0 ?
|
|
23874
|
+
const observedManifestRevision = foreignObservations.length > 0 ? createHash28("sha256").update(JSON.stringify(foreignObservations.map((observation) => [
|
|
23264
23875
|
observation.fileName,
|
|
23265
23876
|
observation.manifestRevision
|
|
23266
23877
|
]).sort())).digest("hex") : null;
|
|
@@ -23507,7 +24118,7 @@ async function assertCompositeMutationSupported(workspaceRoot, profile, readOnly
|
|
|
23507
24118
|
`Composite-profile mutation is refused before member execution because ${member.id} uses ${member.transport}; durable remote policy and recovery preflight is not implemented.`
|
|
23508
24119
|
);
|
|
23509
24120
|
}
|
|
23510
|
-
const memberRoot =
|
|
24121
|
+
const memberRoot = resolve44(workspaceRoot, member.workspace);
|
|
23511
24122
|
const memberPolicy = await mutationPolicyForWorkspace(memberRoot);
|
|
23512
24123
|
if (memberPolicy?.revisioning.mode === "commit-after-verify") {
|
|
23513
24124
|
throw new Error(
|
|
@@ -23744,12 +24355,12 @@ async function printDoctor(target, options) {
|
|
|
23744
24355
|
const requestedSkills = doctorSkillRequests(target, options);
|
|
23745
24356
|
const skills = [];
|
|
23746
24357
|
for (const request of requestedSkills) {
|
|
23747
|
-
const skillPath =
|
|
24358
|
+
const skillPath = join61(state.installRoot, targetMapping.dest, request.name);
|
|
23748
24359
|
const exists2 = await pathExists(skillPath);
|
|
23749
24360
|
const manifestEntry = manifest?.entries.find((entry) => {
|
|
23750
24361
|
if (entry.artifactType !== "skills") return false;
|
|
23751
24362
|
const legacyInstallName = "installName" in entry && typeof entry.installName === "string" ? entry.installName : void 0;
|
|
23752
|
-
return entry.artifactName === request.name || legacyInstallName === request.name || entry.path ===
|
|
24363
|
+
return entry.artifactName === request.name || legacyInstallName === request.name || entry.path === join61(targetMapping.dest, request.name);
|
|
23753
24364
|
});
|
|
23754
24365
|
const status = manifestEntry ? "managed" : exists2 ? "present-unmanaged" : "missing";
|
|
23755
24366
|
skills.push({
|
|
@@ -23829,7 +24440,7 @@ function doctorSkillLabel(name) {
|
|
|
23829
24440
|
return `${name} skill`;
|
|
23830
24441
|
}
|
|
23831
24442
|
function isSyncwheelWorkspace(targetRoot) {
|
|
23832
|
-
return existsSync(
|
|
24443
|
+
return existsSync(join61(targetRoot, ".syncwheel", "manifest.json"));
|
|
23833
24444
|
}
|
|
23834
24445
|
function skillInstallCommand(adapter, installationType, options, skill, behavior = {}) {
|
|
23835
24446
|
const args = [
|
|
@@ -23858,7 +24469,7 @@ function shellQuoteArg2(value) {
|
|
|
23858
24469
|
if (/^[A-Za-z0-9_./:@=-]+$/.test(value)) return value;
|
|
23859
24470
|
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
23860
24471
|
}
|
|
23861
|
-
function collectSelectOption(value, previous) {
|
|
24472
|
+
function collectSelectOption(value, previous = []) {
|
|
23862
24473
|
return [...previous, ...splitSelectorList(value)];
|
|
23863
24474
|
}
|
|
23864
24475
|
function parsePositiveInteger(value) {
|
|
@@ -23992,14 +24603,14 @@ function looksLikeSourceSpecifier(value) {
|
|
|
23992
24603
|
}
|
|
23993
24604
|
function normalizeCliPath(value) {
|
|
23994
24605
|
if (value === "~") return homedir18();
|
|
23995
|
-
if (value.startsWith("~/")) return
|
|
23996
|
-
return
|
|
24606
|
+
if (value.startsWith("~/")) return resolve44(homedir18(), value.slice(2));
|
|
24607
|
+
return resolve44(value);
|
|
23997
24608
|
}
|
|
23998
24609
|
function shellQuoteArgument(value) {
|
|
23999
24610
|
return /^[a-z0-9_./:@%+=,-]+$/i.test(value) ? value : `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
24000
24611
|
}
|
|
24001
24612
|
function isHomePath(path) {
|
|
24002
|
-
return
|
|
24613
|
+
return resolve44(path) === resolve44(homedir18());
|
|
24003
24614
|
}
|
|
24004
24615
|
function adapterListFromOption(adapter) {
|
|
24005
24616
|
if (!adapter) return [];
|
|
@@ -24054,11 +24665,11 @@ function filterUninstallPlanBySelection(plan, selected) {
|
|
|
24054
24665
|
};
|
|
24055
24666
|
}
|
|
24056
24667
|
async function initPackage(root) {
|
|
24057
|
-
await
|
|
24058
|
-
await
|
|
24059
|
-
await
|
|
24060
|
-
const manifestPath =
|
|
24061
|
-
const instructionsPath =
|
|
24668
|
+
await mkdir27(join61(root, "instructions"), { recursive: true });
|
|
24669
|
+
await mkdir27(join61(root, "rules"), { recursive: true });
|
|
24670
|
+
await mkdir27(join61(root, "skills"), { recursive: true });
|
|
24671
|
+
const manifestPath = join61(root, "openpack.json");
|
|
24672
|
+
const instructionsPath = join61(root, "instructions", "AGENTS.md");
|
|
24062
24673
|
const manifest = {
|
|
24063
24674
|
schemaVersion: CURRENT_OPENPACK_SCHEMA_VERSION,
|
|
24064
24675
|
name: "example/agentwheel-package",
|
|
@@ -24120,7 +24731,7 @@ function withFleetExample(config) {
|
|
|
24120
24731
|
};
|
|
24121
24732
|
}
|
|
24122
24733
|
async function findAgentwheelPackageRoot(start) {
|
|
24123
|
-
let current =
|
|
24734
|
+
let current = resolve44(start);
|
|
24124
24735
|
while (true) {
|
|
24125
24736
|
if (await findPackageManifestPath(current, { warnLegacy: false })) return current;
|
|
24126
24737
|
const parent = dirname37(current);
|
|
@@ -24248,14 +24859,14 @@ async function governedCommandContext(command, options) {
|
|
|
24248
24859
|
const kind = String(command.args[0] ?? "workspace");
|
|
24249
24860
|
return {
|
|
24250
24861
|
workspaceRoot: workspaceRoot2,
|
|
24251
|
-
anticipatedPaths: kind === "package" ? [
|
|
24862
|
+
anticipatedPaths: kind === "package" ? [join61(targetRoot, "openpack.json"), join61(targetRoot, "instructions", "AGENTS.md")] : [workspaceConfigPath(targetRoot)]
|
|
24252
24863
|
};
|
|
24253
24864
|
}
|
|
24254
24865
|
if (path === "package migrate") {
|
|
24255
24866
|
const packageRoot = normalizeTargetRoot(String(command.args[0] ?? "."));
|
|
24256
24867
|
return {
|
|
24257
24868
|
workspaceRoot: await findExistingWorkspaceRoot(packageRoot) ?? packageRoot,
|
|
24258
|
-
anticipatedPaths: ["agentwheel.json", "agentwheel.jsonc", "openpack.json", "openpack.jsonc"].map((name) =>
|
|
24869
|
+
anticipatedPaths: ["agentwheel.json", "agentwheel.jsonc", "openpack.json", "openpack.jsonc"].map((name) => join61(packageRoot, name))
|
|
24259
24870
|
};
|
|
24260
24871
|
}
|
|
24261
24872
|
const source = typeof command.args[0] === "string" ? command.args[0] : void 0;
|
|
@@ -24265,9 +24876,9 @@ async function governedCommandContext(command, options) {
|
|
|
24265
24876
|
const contextOnly = path === "cache prune";
|
|
24266
24877
|
const workspaceRoot = contextOnly ? await workspaceContextRootFromOptions(normalized) : await workspaceRootFromOptions(normalized);
|
|
24267
24878
|
const anticipatedPaths = [];
|
|
24268
|
-
if (path === "add") anticipatedPaths.push(workspaceConfigPath(workspaceRoot));
|
|
24879
|
+
if (path === "add" || path === "pin") anticipatedPaths.push(workspaceConfigPath(workspaceRoot));
|
|
24269
24880
|
if (path === "remember" && options.runtime) {
|
|
24270
|
-
anticipatedPaths.push(
|
|
24881
|
+
anticipatedPaths.push(join61(workspaceRoot, ".agentwheel", "overlays", options.runtime, "instructions.local.md"));
|
|
24271
24882
|
}
|
|
24272
24883
|
if (path === "uninstall" && source) anticipatedPaths.push(workspaceConfigPath(workspaceRoot));
|
|
24273
24884
|
return {
|