agentwheel 0.18.5 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +281 -40
- 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
|
@@ -56,6 +56,18 @@ var packageComposeEntrySchema = z.object({
|
|
|
56
56
|
markers: z.boolean().optional(),
|
|
57
57
|
optional: z.boolean().optional()
|
|
58
58
|
});
|
|
59
|
+
var packageCompositionRuleSchema = z.object({
|
|
60
|
+
target: z.string().min(1),
|
|
61
|
+
include: z.string().min(1),
|
|
62
|
+
exclude: z.array(z.string().min(1)).optional(),
|
|
63
|
+
runtimes: z.array(z.string().min(1)).optional(),
|
|
64
|
+
markers: z.boolean().optional()
|
|
65
|
+
});
|
|
66
|
+
var packageSupersedesEntrySchema = z.object({
|
|
67
|
+
package: z.string().min(1),
|
|
68
|
+
selector: z.string().min(1),
|
|
69
|
+
reason: z.string().min(1)
|
|
70
|
+
});
|
|
59
71
|
var packageItemRequireObjectSchema = z.object({
|
|
60
72
|
selector: z.string().min(1),
|
|
61
73
|
optional: z.boolean().optional(),
|
|
@@ -97,6 +109,7 @@ var artifactSchema = z.object({
|
|
|
97
109
|
requires: z.array(packageItemRequireSchema).optional(),
|
|
98
110
|
suggests: z.array(packageItemSuggestSchema).optional(),
|
|
99
111
|
compose: z.array(packageComposeEntrySchema).optional(),
|
|
112
|
+
supersedes: z.array(packageSupersedesEntrySchema).optional(),
|
|
100
113
|
runtimes: z.array(z.string().min(1)).optional(),
|
|
101
114
|
composedFrom: z.array(composedFromEntrySchema).optional()
|
|
102
115
|
});
|
|
@@ -580,7 +593,8 @@ var graphLockArtifactSchema = z4.object({
|
|
|
580
593
|
kind: fileKindSchema,
|
|
581
594
|
hash: z4.string().min(16),
|
|
582
595
|
channel: z4.enum(["managed", "overlay", "addition", "override", "ejected"]).default("managed"),
|
|
583
|
-
composedFrom: z4.array(composedFromEntrySchema).optional()
|
|
596
|
+
composedFrom: z4.array(composedFromEntrySchema).optional(),
|
|
597
|
+
supersedes: z4.array(packageSupersedesEntrySchema).optional()
|
|
584
598
|
});
|
|
585
599
|
var graphLockPlainNameIncumbentSchema = z4.object({
|
|
586
600
|
adapter: z4.string().min(1),
|
|
@@ -1660,6 +1674,16 @@ import { dirname as dirname9 } from "path";
|
|
|
1660
1674
|
import { parse as parse3, stringify as stringify2 } from "yaml";
|
|
1661
1675
|
var MergeAdoptionMismatchError = class extends Error {
|
|
1662
1676
|
};
|
|
1677
|
+
function assertExactMergeContribution(contribution, strategy, currentContent) {
|
|
1678
|
+
if (strategy === "codex-toml-mcp") {
|
|
1679
|
+
assertExactMcpMergeContribution(contribution, strategy, currentContent);
|
|
1680
|
+
return;
|
|
1681
|
+
}
|
|
1682
|
+
const mismatch = firstMergeContributionMismatch(parseMergeDestination(currentContent, strategy), contribution);
|
|
1683
|
+
if (mismatch) {
|
|
1684
|
+
throw new MergeAdoptionMismatchError(`exact merge contribution differs or is missing at ${mismatch}`);
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1663
1687
|
function assertExactMcpMergeContribution(removal, strategy, currentContent) {
|
|
1664
1688
|
if (strategy === "codex-toml-mcp") {
|
|
1665
1689
|
const mismatched = mismatchedCodexTomlMcpServers(removal, currentContent);
|
|
@@ -1766,6 +1790,25 @@ function firstMcpContributionMismatch(current, incoming) {
|
|
|
1766
1790
|
}
|
|
1767
1791
|
return void 0;
|
|
1768
1792
|
}
|
|
1793
|
+
function firstMergeContributionMismatch(current, contribution, path = "$") {
|
|
1794
|
+
if (isRecord4(contribution)) {
|
|
1795
|
+
if (!isRecord4(current)) return path;
|
|
1796
|
+
for (const [key, value] of Object.entries(contribution)) {
|
|
1797
|
+
if (!(key in current)) return `${path}.${key}`;
|
|
1798
|
+
const mismatch = firstMergeContributionMismatch(current[key], value, `${path}.${key}`);
|
|
1799
|
+
if (mismatch) return mismatch;
|
|
1800
|
+
}
|
|
1801
|
+
return void 0;
|
|
1802
|
+
}
|
|
1803
|
+
if (Array.isArray(contribution)) {
|
|
1804
|
+
if (!Array.isArray(current)) return path;
|
|
1805
|
+
for (const value of contribution) {
|
|
1806
|
+
if (!current.some((candidate) => sameMcpValue(candidate, value))) return path;
|
|
1807
|
+
}
|
|
1808
|
+
return void 0;
|
|
1809
|
+
}
|
|
1810
|
+
return current === contribution ? void 0 : path;
|
|
1811
|
+
}
|
|
1769
1812
|
function combineMergeValues(existing, incoming) {
|
|
1770
1813
|
if (isRecord4(existing) && isRecord4(incoming)) {
|
|
1771
1814
|
const combined = { ...existing };
|
|
@@ -5638,7 +5681,7 @@ var legacyArtifactTypeSchema = z6.enum([
|
|
|
5638
5681
|
"plugins"
|
|
5639
5682
|
]);
|
|
5640
5683
|
var runtimeListSchema = z6.array(z6.string().min(1));
|
|
5641
|
-
var CURRENT_OPENPACK_SCHEMA_VERSION =
|
|
5684
|
+
var CURRENT_OPENPACK_SCHEMA_VERSION = 3;
|
|
5642
5685
|
var packageProvideBaseSchema = z6.object({
|
|
5643
5686
|
path: z6.string().min(1),
|
|
5644
5687
|
format: artifactFormatSchema.optional(),
|
|
@@ -5650,6 +5693,7 @@ var packageItemSchema = z6.object({
|
|
|
5650
5693
|
requires: z6.array(packageItemRequireSchema).optional(),
|
|
5651
5694
|
suggests: z6.array(packageItemSuggestSchema).optional(),
|
|
5652
5695
|
compose: z6.array(packageComposeEntrySchema).optional(),
|
|
5696
|
+
supersedes: z6.array(packageSupersedesEntrySchema).optional(),
|
|
5653
5697
|
runtimes: runtimeListSchema.optional()
|
|
5654
5698
|
});
|
|
5655
5699
|
var packageDependencySchema = z6.object({
|
|
@@ -5680,8 +5724,7 @@ var packageManifestV1Schema = z6.object({
|
|
|
5680
5724
|
version: z6.string().min(1),
|
|
5681
5725
|
provides: z6.array(packageProvideV1Schema).min(1)
|
|
5682
5726
|
});
|
|
5683
|
-
var
|
|
5684
|
-
schemaVersion: z6.literal(CURRENT_OPENPACK_SCHEMA_VERSION),
|
|
5727
|
+
var packageManifestModernShape = {
|
|
5685
5728
|
name: z6.string().min(1),
|
|
5686
5729
|
version: z6.string().min(1),
|
|
5687
5730
|
runtimes: runtimeListSchema.optional(),
|
|
@@ -5689,7 +5732,8 @@ var packageManifestV2Schema = z6.object({
|
|
|
5689
5732
|
suggests: z6.record(z6.string().min(1), packageSuggestionSchema).optional(),
|
|
5690
5733
|
compose: z6.array(packageComposeEntrySchema).optional(),
|
|
5691
5734
|
provides: z6.array(packageProvideSchema).default([])
|
|
5692
|
-
}
|
|
5735
|
+
};
|
|
5736
|
+
function requireProvidesOrDependencies(manifest, ctx) {
|
|
5693
5737
|
const hasProvides = manifest.provides.length > 0;
|
|
5694
5738
|
const hasRequires = Object.keys(manifest.requires ?? {}).length > 0;
|
|
5695
5739
|
if (!hasProvides && !hasRequires) {
|
|
@@ -5699,8 +5743,17 @@ var packageManifestV2Schema = z6.object({
|
|
|
5699
5743
|
message: "OpenPack v2 manifest must declare at least one provides entry or one requires dependency"
|
|
5700
5744
|
});
|
|
5701
5745
|
}
|
|
5702
|
-
}
|
|
5703
|
-
var
|
|
5746
|
+
}
|
|
5747
|
+
var packageManifestV2Schema = z6.object({
|
|
5748
|
+
schemaVersion: z6.literal(2),
|
|
5749
|
+
...packageManifestModernShape
|
|
5750
|
+
}).superRefine(requireProvidesOrDependencies);
|
|
5751
|
+
var packageManifestV3Schema = z6.object({
|
|
5752
|
+
schemaVersion: z6.literal(CURRENT_OPENPACK_SCHEMA_VERSION),
|
|
5753
|
+
...packageManifestModernShape,
|
|
5754
|
+
compositionRules: z6.array(packageCompositionRuleSchema).optional()
|
|
5755
|
+
}).superRefine(requireProvidesOrDependencies);
|
|
5756
|
+
var packageManifestSchema = z6.union([packageManifestV1Schema, packageManifestV2Schema, packageManifestV3Schema]);
|
|
5704
5757
|
var openPackManifestNames = ["openpack.json", "openpack.jsonc"];
|
|
5705
5758
|
var legacyPackageManifestNames = ["agentwheel.json", "agentwheel.jsonc"];
|
|
5706
5759
|
var packageManifestNames = [...openPackManifestNames, ...legacyPackageManifestNames];
|
|
@@ -5741,9 +5794,30 @@ function parsePackageManifest(parsed, path = "package manifest") {
|
|
|
5741
5794
|
return parseWithSchema(packageManifestV1Schema, parsed, path);
|
|
5742
5795
|
}
|
|
5743
5796
|
if (parsed.schemaVersion === 2) {
|
|
5797
|
+
const violations = v2OpenPackViolations(parsed);
|
|
5798
|
+
if (violations.length > 0) {
|
|
5799
|
+
throw new Error(`OpenPack schemaVersion 3 is required for ${violations.join(", ")} in ${path}. Set "schemaVersion": 3.`);
|
|
5800
|
+
}
|
|
5744
5801
|
return parseWithSchema(packageManifestV2Schema, parsed, path);
|
|
5745
5802
|
}
|
|
5746
|
-
|
|
5803
|
+
if (parsed.schemaVersion === 3) {
|
|
5804
|
+
return parseWithSchema(packageManifestV3Schema, parsed, path);
|
|
5805
|
+
}
|
|
5806
|
+
throw new Error(`Invalid package manifest ${path}: schemaVersion must be 1, 2, or 3`);
|
|
5807
|
+
}
|
|
5808
|
+
function v2OpenPackViolations(manifest) {
|
|
5809
|
+
const violations = [];
|
|
5810
|
+
if (Object.prototype.hasOwnProperty.call(manifest, "compositionRules")) violations.push("compositionRules");
|
|
5811
|
+
const provides = Array.isArray(manifest.provides) ? manifest.provides : [];
|
|
5812
|
+
for (const [provideIndex, provide] of provides.entries()) {
|
|
5813
|
+
if (!isRecord7(provide) || !isRecord7(provide.items)) continue;
|
|
5814
|
+
for (const [itemName, item] of Object.entries(provide.items)) {
|
|
5815
|
+
if (isRecord7(item) && Object.prototype.hasOwnProperty.call(item, "supersedes")) {
|
|
5816
|
+
violations.push(`provides[${provideIndex}].items.${itemName}.supersedes`);
|
|
5817
|
+
}
|
|
5818
|
+
}
|
|
5819
|
+
}
|
|
5820
|
+
return violations;
|
|
5747
5821
|
}
|
|
5748
5822
|
function parseWithSchema(schema, parsed, path) {
|
|
5749
5823
|
const result = schema.safeParse(parsed);
|
|
@@ -5753,7 +5827,7 @@ function parseWithSchema(schema, parsed, path) {
|
|
|
5753
5827
|
}
|
|
5754
5828
|
function v1OpenPackViolations(manifest) {
|
|
5755
5829
|
const violations = [];
|
|
5756
|
-
for (const key of ["requires", "items", "compose", "runtimes"]) {
|
|
5830
|
+
for (const key of ["requires", "items", "compose", "runtimes", "compositionRules", "supersedes"]) {
|
|
5757
5831
|
if (Object.prototype.hasOwnProperty.call(manifest, key)) violations.push(key);
|
|
5758
5832
|
}
|
|
5759
5833
|
const provides = Array.isArray(manifest.provides) ? manifest.provides : [];
|
|
@@ -6003,6 +6077,7 @@ async function artifactForFile(type, name, sourcePath, relativePath, packageName
|
|
|
6003
6077
|
requires: item.requires,
|
|
6004
6078
|
suggests: item.suggests,
|
|
6005
6079
|
compose: item.compose,
|
|
6080
|
+
supersedes: item.supersedes,
|
|
6006
6081
|
runtimes: item.runtimes ?? provideRuntimes(provide) ?? manifestRuntimes(manifest)
|
|
6007
6082
|
};
|
|
6008
6083
|
}
|
|
@@ -6023,6 +6098,7 @@ async function artifactForDir(type, name, sourcePath, relativePath, packageName,
|
|
|
6023
6098
|
requires: item.requires,
|
|
6024
6099
|
suggests: item.suggests,
|
|
6025
6100
|
compose: item.compose,
|
|
6101
|
+
supersedes: item.supersedes,
|
|
6026
6102
|
runtimes: item.runtimes ?? provideRuntimes(provide) ?? manifestRuntimes(manifest)
|
|
6027
6103
|
};
|
|
6028
6104
|
}
|
|
@@ -6030,13 +6106,13 @@ function itemMetadata(provide, itemName) {
|
|
|
6030
6106
|
if (!provide || !("items" in provide) || !provide.items || !itemName) return {};
|
|
6031
6107
|
const item = provide.items[itemName];
|
|
6032
6108
|
if (!item) return {};
|
|
6033
|
-
return { format: item.format, requires: item.requires, suggests: item.suggests, compose: item.compose, runtimes: item.runtimes };
|
|
6109
|
+
return { format: item.format, requires: item.requires, suggests: item.suggests, compose: item.compose, supersedes: item.supersedes, runtimes: item.runtimes };
|
|
6034
6110
|
}
|
|
6035
6111
|
function provideRuntimes(provide) {
|
|
6036
6112
|
return provide && "runtimes" in provide ? provide.runtimes : void 0;
|
|
6037
6113
|
}
|
|
6038
6114
|
function manifestRuntimes(manifest) {
|
|
6039
|
-
return manifest && manifest.schemaVersion
|
|
6115
|
+
return manifest && manifest.schemaVersion !== 1 ? manifest.runtimes : void 0;
|
|
6040
6116
|
}
|
|
6041
6117
|
|
|
6042
6118
|
// src/source/clawhub.ts
|
|
@@ -7229,7 +7305,14 @@ async function expandMarkdownIncludes(artifacts, packageRoot, options = {}) {
|
|
|
7229
7305
|
if (files.length === 0) continue;
|
|
7230
7306
|
const composedFrom = [];
|
|
7231
7307
|
for (const file of files) {
|
|
7232
|
-
const
|
|
7308
|
+
const localEntries = composeEntriesForFile(artifact, file).map((entry) => ({
|
|
7309
|
+
entry,
|
|
7310
|
+
packageRoot,
|
|
7311
|
+
artifactPaths,
|
|
7312
|
+
nodeId: options.nodeId
|
|
7313
|
+
}));
|
|
7314
|
+
const externalEntries = isPrimaryMarkdownFile(artifact, file) ? options.additionalComposeEntries?.(artifact) ?? [] : [];
|
|
7315
|
+
const result = await expandFile(file, packageRoot, [...localEntries, ...externalEntries], artifactPaths, options);
|
|
7233
7316
|
if (result.changed) await writeFile17(file, result.content, "utf8");
|
|
7234
7317
|
composedFrom.push(...result.composedFrom);
|
|
7235
7318
|
}
|
|
@@ -7247,7 +7330,8 @@ async function validateMarkdownIncludes(artifacts, packageRoot, options = {}) {
|
|
|
7247
7330
|
const artifactPaths = artifactPathMap(artifacts);
|
|
7248
7331
|
for (const artifact of artifacts) {
|
|
7249
7332
|
for (const file of await markdownFilesForArtifact(artifact)) {
|
|
7250
|
-
|
|
7333
|
+
const entries = composeEntriesForFile(artifact, file).map((entry) => ({ entry, packageRoot, artifactPaths, nodeId: options.nodeId }));
|
|
7334
|
+
await expandFile(file, packageRoot, entries, artifactPaths, options);
|
|
7251
7335
|
}
|
|
7252
7336
|
}
|
|
7253
7337
|
}
|
|
@@ -7257,9 +7341,11 @@ async function expandFile(file, packageRoot, appendEntries, artifactPaths, optio
|
|
|
7257
7341
|
const expanded = await expandContent(raw, packageRoot, [owner], artifactPaths, options);
|
|
7258
7342
|
let content = expanded.content;
|
|
7259
7343
|
const composedFrom = [...expanded.composedFrom];
|
|
7260
|
-
for (const
|
|
7261
|
-
const
|
|
7344
|
+
for (const external of appendEntries) {
|
|
7345
|
+
const { entry } = external;
|
|
7346
|
+
const included = await expandInclude(entry.include, external.packageRoot, external.artifactPaths, {
|
|
7262
7347
|
...options,
|
|
7348
|
+
nodeId: external.nodeId,
|
|
7263
7349
|
optional: entry.optional === true,
|
|
7264
7350
|
markers: entry.markers !== false,
|
|
7265
7351
|
chain: [owner]
|
|
@@ -7437,6 +7523,10 @@ function composeEntriesForFile(artifact, file) {
|
|
|
7437
7523
|
if (artifact.kind === "file") return [resolve11(artifact.stagedPath ?? artifact.sourcePath), resolve11(file)].every(Boolean) && resolve11(artifact.stagedPath ?? artifact.sourcePath) === resolve11(file) ? artifact.compose : [];
|
|
7438
7524
|
return basename16(file) === "SKILL.md" && dirname21(file) === resolve11(artifact.stagedPath ?? artifact.sourcePath) ? artifact.compose : [];
|
|
7439
7525
|
}
|
|
7526
|
+
function isPrimaryMarkdownFile(artifact, file) {
|
|
7527
|
+
if (artifact.kind === "file") return resolve11(artifact.stagedPath ?? artifact.sourcePath) === resolve11(file);
|
|
7528
|
+
return basename16(file) === "SKILL.md" && dirname21(file) === resolve11(artifact.stagedPath ?? artifact.sourcePath);
|
|
7529
|
+
}
|
|
7440
7530
|
function orderedForExpansion(artifacts) {
|
|
7441
7531
|
return [...artifacts].sort((a, b) => Number(a.type === "fragments") - Number(b.type === "fragments"));
|
|
7442
7532
|
}
|
|
@@ -8923,7 +9013,7 @@ Dependency chain: ${requirement.chain.join(" -> ")}`);
|
|
|
8923
9013
|
}
|
|
8924
9014
|
}
|
|
8925
9015
|
async function collectDependencyNeeds(state, fetched, options, chain) {
|
|
8926
|
-
if (fetched.manifest
|
|
9016
|
+
if (!fetched.manifest || fetched.manifest.schemaVersion === 1) return [];
|
|
8927
9017
|
const dependencies = fetched.manifest.requires ?? {};
|
|
8928
9018
|
const suggestions = fetched.manifest.suggests ?? {};
|
|
8929
9019
|
const dependencyEntries = Object.entries(dependencies).sort(([a], [b]) => a.localeCompare(b));
|
|
@@ -9487,6 +9577,14 @@ function detectDirectCollisions(nodes) {
|
|
|
9487
9577
|
for (const [selector, owners] of bySelector) {
|
|
9488
9578
|
const uniqueOwners = [...new Map(owners.map((owner) => [owner.node.id, owner])).values()];
|
|
9489
9579
|
if (uniqueOwners.length <= 1) continue;
|
|
9580
|
+
const replacements = uniqueOwners.filter((owner) => {
|
|
9581
|
+
const artifact = owner.artifacts.find((candidate) => artifactSelectorKey(candidate) === selector);
|
|
9582
|
+
return uniqueOwners.every((other) => {
|
|
9583
|
+
if (other === owner) return true;
|
|
9584
|
+
return artifact?.supersedes?.some((entry) => entry.package === other.node.name && entry.selector === selector) === true;
|
|
9585
|
+
});
|
|
9586
|
+
});
|
|
9587
|
+
if (replacements.length === 1) continue;
|
|
9490
9588
|
throw new Error(
|
|
9491
9589
|
`Direct dependency artifact collision for ${selector}: ${uniqueOwners.map((owner) => `${owner.node.id} required by ${owner.node.requiredBy.join(", ")}`).join("; ")}. Resolve by aliasing, deselecting one artifact, or overriding the dependency selection.`
|
|
9492
9590
|
);
|
|
@@ -9806,7 +9904,8 @@ async function renderGraphForTarget(graph, targetContext = {}) {
|
|
|
9806
9904
|
const includeEdges = /* @__PURE__ */ new Map();
|
|
9807
9905
|
const ambiguousPackageNames = ambiguousGraphPackageNames(graph);
|
|
9808
9906
|
for (const rawNode of graph.rawNodes) {
|
|
9809
|
-
|
|
9907
|
+
const ownsCompositionRules = rawNode.depth === 0 && rawNode.manifest?.schemaVersion === 3 && Boolean(rawNode.manifest.compositionRules?.length);
|
|
9908
|
+
if (rawNode.node.selected.length === 0 && !ownsCompositionRules) continue;
|
|
9810
9909
|
const rawBundle = await stageResolvedArtifactsRaw(rawNode.resolved, rawNode.artifacts);
|
|
9811
9910
|
const fragmentCustomized = targetContext.workspaceRoot && targetContext.adapter ? await applyFragmentCustomizations(rawBundle.artifacts, {
|
|
9812
9911
|
workspaceRoot: targetContext.workspaceRoot,
|
|
@@ -9826,11 +9925,18 @@ async function renderGraphForTarget(graph, targetContext = {}) {
|
|
|
9826
9925
|
});
|
|
9827
9926
|
}
|
|
9828
9927
|
const aliasEdges = aliasEdgeMap(graph);
|
|
9928
|
+
const compositionRules = collectCompositionRules(stagedNodes);
|
|
9829
9929
|
for (const staged of [...stagedNodes.values()].sort((a, b) => a.rawNode.node.id.localeCompare(b.rawNode.node.id))) {
|
|
9830
9930
|
const rawNode = staged.rawNode;
|
|
9831
9931
|
const expandedArtifacts = await expandMarkdownIncludes(staged.artifacts, staged.root, {
|
|
9832
9932
|
nodeId: rawNode.node.id,
|
|
9833
9933
|
originNodeId: rawNode.node.id,
|
|
9934
|
+
additionalComposeEntries: (artifact) => matchingCompositionEntries(
|
|
9935
|
+
artifact,
|
|
9936
|
+
rawNode.node.id,
|
|
9937
|
+
targetContext.adapter?.name,
|
|
9938
|
+
compositionRules
|
|
9939
|
+
),
|
|
9834
9940
|
resolveCrossPackageInclude: async (request) => {
|
|
9835
9941
|
const edge = aliasEdges.get(`${request.fromNodeId}\0${request.alias}`);
|
|
9836
9942
|
if (!edge) {
|
|
@@ -9908,6 +10014,50 @@ async function renderGraphForTarget(graph, targetContext = {}) {
|
|
|
9908
10014
|
graphLock: createGraphLock(graph, sortedArtifacts.map(lockArtifactFor), targetContext.targetFingerprint, [...includeEdges.values()], namespacing, overrides)
|
|
9909
10015
|
};
|
|
9910
10016
|
}
|
|
10017
|
+
function collectCompositionRules(stagedNodes) {
|
|
10018
|
+
const rules = [];
|
|
10019
|
+
for (const staged of stagedNodes.values()) {
|
|
10020
|
+
if (staged.rawNode.depth !== 0) continue;
|
|
10021
|
+
const manifest = staged.rawNode.manifest;
|
|
10022
|
+
if (!manifest || manifest.schemaVersion !== 3) continue;
|
|
10023
|
+
for (const rule of manifest.compositionRules ?? []) {
|
|
10024
|
+
rules.push({
|
|
10025
|
+
ownerNodeId: staged.rawNode.node.id,
|
|
10026
|
+
ownerPackageName: staged.rawNode.node.name,
|
|
10027
|
+
rule,
|
|
10028
|
+
packageRoot: staged.root,
|
|
10029
|
+
artifactPaths: staged.artifactPaths
|
|
10030
|
+
});
|
|
10031
|
+
}
|
|
10032
|
+
}
|
|
10033
|
+
return rules.sort((a, b) => `${a.ownerNodeId}\0${a.rule.target}\0${a.rule.include}`.localeCompare(`${b.ownerNodeId}\0${b.rule.target}\0${b.rule.include}`));
|
|
10034
|
+
}
|
|
10035
|
+
function matchingCompositionEntries(artifact, targetNodeId, runtime, rules) {
|
|
10036
|
+
const selector = `${artifact.type}/${artifact.name}`;
|
|
10037
|
+
const qualifiedSelector = `${artifact.packageName ?? targetNodeId}:${selector}`;
|
|
10038
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10039
|
+
const entries = [];
|
|
10040
|
+
for (const candidate of rules) {
|
|
10041
|
+
const { rule } = candidate;
|
|
10042
|
+
if (rule.runtimes?.length && (!runtime || !rule.runtimes.includes(runtime))) continue;
|
|
10043
|
+
if (!globMatches(rule.target, selector) && !globMatches(rule.target, qualifiedSelector)) continue;
|
|
10044
|
+
if (rule.exclude?.some((pattern) => globMatches(pattern, selector) || globMatches(pattern, qualifiedSelector))) continue;
|
|
10045
|
+
const key = `${candidate.ownerNodeId}\0${rule.include}`;
|
|
10046
|
+
if (seen.has(key)) continue;
|
|
10047
|
+
seen.add(key);
|
|
10048
|
+
entries.push({
|
|
10049
|
+
entry: { include: rule.include, markers: rule.markers },
|
|
10050
|
+
packageRoot: candidate.packageRoot,
|
|
10051
|
+
artifactPaths: candidate.artifactPaths,
|
|
10052
|
+
nodeId: candidate.ownerNodeId
|
|
10053
|
+
});
|
|
10054
|
+
}
|
|
10055
|
+
return entries;
|
|
10056
|
+
}
|
|
10057
|
+
function globMatches(pattern, value) {
|
|
10058
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replaceAll("*", ".*");
|
|
10059
|
+
return new RegExp(`^${escaped}$`).test(value);
|
|
10060
|
+
}
|
|
9911
10061
|
function aliasEdgeMap(graph) {
|
|
9912
10062
|
return new Map(graph.edges.map((edge) => [`${edge.from}\0${edge.alias}`, { to: edge.to }]));
|
|
9913
10063
|
}
|
|
@@ -9939,10 +10089,12 @@ function sha2563(content) {
|
|
|
9939
10089
|
return createHash10("sha256").update(content).digest("hex");
|
|
9940
10090
|
}
|
|
9941
10091
|
function assignInstallNames(graph, artifacts) {
|
|
10092
|
+
const superseded = supersededLogicalSelectors(artifacts);
|
|
10093
|
+
const effectiveArtifacts = artifacts.filter((artifact) => !superseded.has(artifact.logicalSelector));
|
|
9942
10094
|
const aliases = workspaceAliases(graph);
|
|
9943
|
-
validateAliasScopes(graph,
|
|
10095
|
+
validateAliasScopes(graph, effectiveArtifacts, aliases);
|
|
9944
10096
|
const decisions = /* @__PURE__ */ new Map();
|
|
9945
|
-
const withAliases =
|
|
10097
|
+
const withAliases = effectiveArtifacts.map((artifact) => {
|
|
9946
10098
|
const alias = aliasForArtifact(artifact, graph, aliases);
|
|
9947
10099
|
if (!alias) return artifact;
|
|
9948
10100
|
const updated = { ...artifact, installName: alias };
|
|
@@ -9981,6 +10133,23 @@ function assignInstallNames(graph, artifacts) {
|
|
|
9981
10133
|
}));
|
|
9982
10134
|
return { artifacts: out, namespacing: [...decisions.values()], overrides: finalOverrides };
|
|
9983
10135
|
}
|
|
10136
|
+
function supersededLogicalSelectors(artifacts) {
|
|
10137
|
+
const removed = /* @__PURE__ */ new Set();
|
|
10138
|
+
for (const replacement of artifacts) {
|
|
10139
|
+
for (const declaration of replacement.supersedes ?? []) {
|
|
10140
|
+
const replacementSelector = `${replacement.type}/${replacement.name}`;
|
|
10141
|
+
if (declaration.selector !== replacementSelector) {
|
|
10142
|
+
throw new Error(`Supersedes selector must match the replacement artifact ${replacement.logicalSelector}: ${declaration.selector}`);
|
|
10143
|
+
}
|
|
10144
|
+
const matches = artifacts.filter((candidate) => candidate !== replacement && candidate.packageName === declaration.package && `${candidate.type}/${candidate.name}` === declaration.selector);
|
|
10145
|
+
if (matches.length > 1) {
|
|
10146
|
+
throw new Error(`Supersedes target is ambiguous for ${replacement.logicalSelector}: ${declaration.package}:${declaration.selector}`);
|
|
10147
|
+
}
|
|
10148
|
+
if (matches[0]) removed.add(matches[0].logicalSelector);
|
|
10149
|
+
}
|
|
10150
|
+
}
|
|
10151
|
+
return removed;
|
|
10152
|
+
}
|
|
9984
10153
|
function applyWorkspaceOverrides(graph, artifacts) {
|
|
9985
10154
|
const directives = workspaceOverrides(graph);
|
|
9986
10155
|
if (directives.length === 0) return { artifacts, overrides: [] };
|
|
@@ -10180,7 +10349,8 @@ function lockArtifactFor(artifact) {
|
|
|
10180
10349
|
kind: artifact.kind,
|
|
10181
10350
|
hash: artifact.hash,
|
|
10182
10351
|
channel: artifact.channel ?? "managed",
|
|
10183
|
-
composedFrom: artifact.composedFrom
|
|
10352
|
+
composedFrom: artifact.composedFrom,
|
|
10353
|
+
supersedes: artifact.supersedes
|
|
10184
10354
|
};
|
|
10185
10355
|
}
|
|
10186
10356
|
|
|
@@ -10348,8 +10518,8 @@ async function createExactMcpRetirementPlan(desiredArtifacts, adapter, targetRoo
|
|
|
10348
10518
|
}
|
|
10349
10519
|
const removalKeys = Object.keys(operation.mergeRemoval);
|
|
10350
10520
|
const servers = operation.mergeRemoval.mcpServers;
|
|
10351
|
-
if (removalKeys.length !== 1 || removalKeys[0] !== "mcpServers" || !servers || typeof servers !== "object" || Array.isArray(servers) || Object.keys(servers).length
|
|
10352
|
-
throw new Error("Exact MCP retirement requires
|
|
10521
|
+
if (removalKeys.length !== 1 || removalKeys[0] !== "mcpServers" || !servers || typeof servers !== "object" || Array.isArray(servers) || Object.keys(servers).length === 0) {
|
|
10522
|
+
throw new Error("Exact MCP retirement requires one or more MCP servers and no non-MCP configuration.");
|
|
10353
10523
|
}
|
|
10354
10524
|
const entry = manifest?.entries[0];
|
|
10355
10525
|
if (entry) {
|
|
@@ -11336,15 +11506,25 @@ async function validatePackage(root) {
|
|
|
11336
11506
|
} catch (error) {
|
|
11337
11507
|
findings.push({ level: "error", message: error instanceof Error ? error.message : String(error), path: packageRoot });
|
|
11338
11508
|
}
|
|
11339
|
-
if (manifest.schemaVersion
|
|
11509
|
+
if (manifest.schemaVersion !== 1 && manifest.compose) {
|
|
11340
11510
|
for (const entry of manifest.compose) {
|
|
11341
11511
|
await validateManifestComposeInclude(packageRoot, entry.include, entry.optional === true, findings, manifestPath, Object.keys(manifest.requires ?? {}));
|
|
11342
11512
|
}
|
|
11343
11513
|
}
|
|
11514
|
+
if (manifest.schemaVersion === 3) {
|
|
11515
|
+
const aliases = Object.keys(manifest.requires ?? {});
|
|
11516
|
+
for (const [index, rule] of (manifest.compositionRules ?? []).entries()) {
|
|
11517
|
+
validateSelector(rule.include, `compositionRules[${index}].include`, findings, manifestPath, { fragmentsOnly: true, aliases });
|
|
11518
|
+
if (!rule.target.startsWith("skills/")) {
|
|
11519
|
+
findings.push({ level: "error", message: `compositionRules[${index}].target: v3 composition rules may target only skills/*`, path: manifestPath });
|
|
11520
|
+
}
|
|
11521
|
+
await validateManifestComposeInclude(packageRoot, rule.include, false, findings, manifestPath, aliases);
|
|
11522
|
+
}
|
|
11523
|
+
}
|
|
11344
11524
|
return { ok: !findings.some((finding) => finding.level === "error"), manifestPath, findings };
|
|
11345
11525
|
}
|
|
11346
11526
|
function validateDeclaredSelectors(manifest, findings, manifestPath) {
|
|
11347
|
-
if (manifest.schemaVersion
|
|
11527
|
+
if (manifest.schemaVersion !== 1) {
|
|
11348
11528
|
for (const [alias, dependency] of Object.entries(manifest.requires ?? {})) {
|
|
11349
11529
|
if (!alias.trim()) {
|
|
11350
11530
|
findings.push({ level: "error", message: "Dependency alias must be non-empty", path: manifestPath });
|
|
@@ -11365,13 +11545,19 @@ function validateDeclaredSelectors(manifest, findings, manifestPath) {
|
|
|
11365
11545
|
for (const [provideIndex, provide] of manifest.provides.entries()) {
|
|
11366
11546
|
if (!("items" in provide) || !provide.items) continue;
|
|
11367
11547
|
for (const [itemName, item] of Object.entries(provide.items)) {
|
|
11548
|
+
for (const declaration of item.supersedes ?? []) {
|
|
11549
|
+
const expected = `${provide.type}/${itemName}`;
|
|
11550
|
+
if (declaration.selector !== expected) {
|
|
11551
|
+
findings.push({ level: "error", message: `provides[${provideIndex}].items.${itemName}.supersedes: selector must equal ${expected}`, path: manifestPath });
|
|
11552
|
+
}
|
|
11553
|
+
}
|
|
11368
11554
|
for (const requirement of item.requires ?? []) {
|
|
11369
11555
|
const selector = typeof requirement === "string" ? requirement : requirement.selector;
|
|
11370
|
-
validateSelector(selector, `provides[${provideIndex}].items.${itemName}.requires`, findings, manifestPath, { aliases: manifest.schemaVersion
|
|
11556
|
+
validateSelector(selector, `provides[${provideIndex}].items.${itemName}.requires`, findings, manifestPath, { aliases: manifest.schemaVersion !== 1 ? Object.keys(manifest.requires ?? {}) : [] });
|
|
11371
11557
|
}
|
|
11372
11558
|
for (const suggestion of item.suggests ?? []) {
|
|
11373
11559
|
const alias = typeof suggestion === "string" ? suggestion : suggestion.alias;
|
|
11374
|
-
if (manifest.schemaVersion
|
|
11560
|
+
if (manifest.schemaVersion !== 1 && !Object.keys(manifest.suggests ?? {}).includes(alias)) {
|
|
11375
11561
|
findings.push({ level: "error", message: `provides[${provideIndex}].items.${itemName}.suggests: suggestion alias not declared: ${alias}`, path: manifestPath });
|
|
11376
11562
|
}
|
|
11377
11563
|
for (const selector of typeof suggestion === "string" ? [] : suggestion.select ?? []) {
|
|
@@ -11380,7 +11566,7 @@ function validateDeclaredSelectors(manifest, findings, manifestPath) {
|
|
|
11380
11566
|
}
|
|
11381
11567
|
for (const entry of item.compose ?? []) {
|
|
11382
11568
|
validateSelector(entry.include, `provides[${provideIndex}].items.${itemName}.compose.include`, findings, manifestPath, {
|
|
11383
|
-
aliases: manifest.schemaVersion
|
|
11569
|
+
aliases: manifest.schemaVersion !== 1 ? Object.keys(manifest.requires ?? {}) : [],
|
|
11384
11570
|
fragmentsOnly: true
|
|
11385
11571
|
});
|
|
11386
11572
|
}
|
|
@@ -11572,7 +11758,7 @@ async function validateOwnershipHandoff(request, transport) {
|
|
|
11572
11758
|
}
|
|
11573
11759
|
const entry = matches[0];
|
|
11574
11760
|
const fromOwner = workspaceOwnerForRoot(request.fromWorkspaceRoot);
|
|
11575
|
-
const toOwner = workspaceOwnerForRoot(request.toWorkspaceRoot);
|
|
11761
|
+
const toOwner = workspaceOwnerForRoot(request.toWorkspaceRoot, request.toFleetId);
|
|
11576
11762
|
if (fromOwner === toOwner) throw new Error("Ownership handoff requires different workspace roots.");
|
|
11577
11763
|
if (entry.workspaceOwner !== fromOwner) {
|
|
11578
11764
|
throw new Error(`Old owner precondition failed for ${entry.path}: expected ${fromOwner}, found ${entry.workspaceOwner}`);
|
|
@@ -11582,10 +11768,7 @@ async function validateOwnershipHandoff(request, transport) {
|
|
|
11582
11768
|
}
|
|
11583
11769
|
const destPath = containedArtifactPath(request.targetRoot, entry.path);
|
|
11584
11770
|
if (!await transport.pathExists(destPath)) throw new Error(`Managed artifact is missing: ${entry.path}`);
|
|
11585
|
-
const currentHash = await
|
|
11586
|
-
if (currentHash !== entry.hash) {
|
|
11587
|
-
throw new Error(`Managed artifact is drifted at ${entry.path}: manifest ${entry.hash}, current ${currentHash}`);
|
|
11588
|
-
}
|
|
11771
|
+
const currentHash = await verifiedEntryHash(entry, destPath, transport);
|
|
11589
11772
|
if (request.expectedHash && currentHash !== request.expectedHash) {
|
|
11590
11773
|
throw new Error(`Current hash precondition failed for ${entry.path}: expected ${request.expectedHash}, found ${currentHash}`);
|
|
11591
11774
|
}
|
|
@@ -11602,6 +11785,29 @@ async function validateOwnershipHandoff(request, transport) {
|
|
|
11602
11785
|
toOwner
|
|
11603
11786
|
};
|
|
11604
11787
|
}
|
|
11788
|
+
async function verifiedEntryHash(entry, destPath, transport) {
|
|
11789
|
+
if (entry.semanticPlugin) throw new Error(`Ownership handoff cannot verify semantic plugin state at ${destPath}`);
|
|
11790
|
+
if (entry.mode === "managed-block") {
|
|
11791
|
+
const selector = managedInstructionSelector(entry.logicalSelector, entry.artifactType, entry.artifactName);
|
|
11792
|
+
const state = await readManagedInstructionBlockState(destPath, selector, transport);
|
|
11793
|
+
if (!state.hasBlock || state.drifted || state.hash !== entry.hash) {
|
|
11794
|
+
throw new Error(`Managed artifact is drifted at ${destPath}: managed block is missing or changed`);
|
|
11795
|
+
}
|
|
11796
|
+
return entry.hash;
|
|
11797
|
+
}
|
|
11798
|
+
if (entry.mergeStrategy) {
|
|
11799
|
+
if (!hasMergeRemovalContent(entry.mergeRemoval)) {
|
|
11800
|
+
throw new Error(`Ownership handoff cannot verify incomplete merge ownership at ${destPath}`);
|
|
11801
|
+
}
|
|
11802
|
+
assertExactMergeContribution(entry.mergeRemoval, entry.mergeStrategy, await transport.readFile(destPath));
|
|
11803
|
+
return entry.hash;
|
|
11804
|
+
}
|
|
11805
|
+
const currentHash = await transport.hashPath(destPath);
|
|
11806
|
+
if (currentHash !== entry.hash) {
|
|
11807
|
+
throw new Error(`Managed artifact is drifted at ${destPath}: manifest ${entry.hash}, current ${currentHash}`);
|
|
11808
|
+
}
|
|
11809
|
+
return currentHash;
|
|
11810
|
+
}
|
|
11605
11811
|
function containedArtifactPath(targetRoot, relativePath) {
|
|
11606
11812
|
if (!relativePath || relativePath.startsWith("/") || relativePath.includes("\0")) {
|
|
11607
11813
|
throw new Error(`Unsafe managed artifact path: ${relativePath}`);
|
|
@@ -13610,7 +13816,7 @@ async function inspectInstalledState(source, destination, packageNames) {
|
|
|
13610
13816
|
throw new Error(`Partial ownership at ${sourceEntry.renderedPath} includes packages outside the normalization selection.`);
|
|
13611
13817
|
}
|
|
13612
13818
|
const destinationRenderedPath = renderedEntryPathForRoot(destinationState.installRoot, sourceEntry.entry.path);
|
|
13613
|
-
await
|
|
13819
|
+
await assertEquivalentRuntimeState(sourceEntry.entry, sourceEntry.renderedPath, destinationRenderedPath);
|
|
13614
13820
|
plannedDestinationPaths.add(destinationRenderedPath);
|
|
13615
13821
|
sourceRenderedPaths.push(sourceEntry.renderedPath);
|
|
13616
13822
|
destinationRenderedPaths.push(destinationRenderedPath);
|
|
@@ -13774,7 +13980,7 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
13774
13980
|
if (!coveredByCurrentGraph && !matchesCurrentGraph && !orphanedOwners.has(entry.entry.workspaceOwner)) {
|
|
13775
13981
|
throw new Error(`Legacy manifest entry is not covered by its graph lock: ${entry.renderedPath}`);
|
|
13776
13982
|
}
|
|
13777
|
-
await
|
|
13983
|
+
await assertEquivalentRuntimeState(entry.entry, entry.renderedPath, entry.renderedPath);
|
|
13778
13984
|
renderedPaths.add(entry.renderedPath);
|
|
13779
13985
|
sourceRenderedPaths.push(entry.renderedPath);
|
|
13780
13986
|
if (coveredByCurrentGraph || matchesCurrentGraph) {
|
|
@@ -14184,6 +14390,33 @@ async function assertEquivalentRuntimeBytes(sourcePath, destinationPath, expecte
|
|
|
14184
14390
|
throw new Error(`Runtime content drift at ${destinationPath}; source and destination bytes are not equivalent.`);
|
|
14185
14391
|
}
|
|
14186
14392
|
}
|
|
14393
|
+
async function assertEquivalentRuntimeState(entry, sourcePath, destinationPath) {
|
|
14394
|
+
if (entry.mode === "managed-block") {
|
|
14395
|
+
const selector = managedInstructionSelector(entry.logicalSelector, entry.artifactType, entry.artifactName);
|
|
14396
|
+
for (const path of /* @__PURE__ */ new Set([sourcePath, destinationPath])) {
|
|
14397
|
+
const state = await readManagedInstructionBlockState(path, selector, localTransport);
|
|
14398
|
+
if (!state.exists || !state.hasBlock || state.drifted || state.hash !== entry.hash) {
|
|
14399
|
+
throw new Error(`Runtime managed-block drift at ${path}; the installed contribution is missing or changed.`);
|
|
14400
|
+
}
|
|
14401
|
+
}
|
|
14402
|
+
return;
|
|
14403
|
+
}
|
|
14404
|
+
if (entry.mergeStrategy) {
|
|
14405
|
+
for (const path of /* @__PURE__ */ new Set([sourcePath, destinationPath])) {
|
|
14406
|
+
if (!await pathExists(path)) throw new Error(`Runtime merge destination is missing: ${path}`);
|
|
14407
|
+
const content = await readFile37(path, "utf8");
|
|
14408
|
+
if (hasMergeRemovalContent(entry.mergeRemoval)) {
|
|
14409
|
+
assertExactMergeContribution(entry.mergeRemoval, entry.mergeStrategy, content);
|
|
14410
|
+
} else if (resolve25(sourcePath) === resolve25(destinationPath) && entry.mergeStrategy !== "codex-toml-mcp") {
|
|
14411
|
+
assertExactMergeContribution({}, entry.mergeStrategy, content);
|
|
14412
|
+
} else {
|
|
14413
|
+
throw new Error(`Installed-state normalization cannot prove incomplete merge ownership at ${path}.`);
|
|
14414
|
+
}
|
|
14415
|
+
}
|
|
14416
|
+
return;
|
|
14417
|
+
}
|
|
14418
|
+
await assertEquivalentRuntimeBytes(sourcePath, destinationPath, entry.hash);
|
|
14419
|
+
}
|
|
14187
14420
|
function renderedEntryPathForRoot(root, entryPath) {
|
|
14188
14421
|
const normalizedRoot = resolve25(root);
|
|
14189
14422
|
const candidate = resolve25(normalizedRoot, entryPath);
|
|
@@ -14291,8 +14524,8 @@ function workspaceOwners(scope) {
|
|
|
14291
14524
|
]);
|
|
14292
14525
|
}
|
|
14293
14526
|
function assertSimpleVerifiableEntry(entry, path) {
|
|
14294
|
-
if (entry.semanticPlugin
|
|
14295
|
-
throw new Error(`Installed-state normalization cannot byte-verify semantic
|
|
14527
|
+
if (entry.semanticPlugin) {
|
|
14528
|
+
throw new Error(`Installed-state normalization cannot byte-verify semantic plugin entry ${path}.`);
|
|
14296
14529
|
}
|
|
14297
14530
|
if (entry.kind !== "file" && entry.kind !== "dir") throw new Error(`Unsupported installed entry kind at ${path}.`);
|
|
14298
14531
|
}
|
|
@@ -14324,7 +14557,7 @@ function graphArtifactIdentity(artifact) {
|
|
|
14324
14557
|
dependencyRole: artifact.dependencyRole,
|
|
14325
14558
|
owners: artifact.owners,
|
|
14326
14559
|
kind: artifact.kind,
|
|
14327
|
-
sourceHash: artifact.hash,
|
|
14560
|
+
...artifact.composedFrom?.length ? { composedFrom: artifact.composedFrom } : { sourceHash: artifact.hash },
|
|
14328
14561
|
channel: artifact.channel
|
|
14329
14562
|
});
|
|
14330
14563
|
}
|
|
@@ -14338,7 +14571,7 @@ function graphEntryIdentity(entry) {
|
|
|
14338
14571
|
dependencyRole: entry.dependencyRole,
|
|
14339
14572
|
owners: entry.owners,
|
|
14340
14573
|
kind: entry.kind,
|
|
14341
|
-
sourceHash: entry.sourceHash,
|
|
14574
|
+
...entry.composedFrom?.length ? { composedFrom: entry.composedFrom } : { sourceHash: entry.sourceHash },
|
|
14342
14575
|
channel: entry.channel
|
|
14343
14576
|
});
|
|
14344
14577
|
}
|
|
@@ -14977,7 +15210,7 @@ program.command("remember").description("append text to the local instructions o
|
|
|
14977
15210
|
console.log(nextInstallNudge());
|
|
14978
15211
|
});
|
|
14979
15212
|
var ownershipCommand = program.command("ownership").description("inspect and transfer manifest ownership without rewriting runtime artifacts");
|
|
14980
|
-
ownershipCommand.command("handoff").description("transfer one managed artifact between Agentwheel workspace roots").argument("<selector>", "exact artifact selector in type/name form").requiredOption("--from-workspace-root <path>", "current owning workspace root").requiredOption("--to-workspace-root <path>", "new owning workspace root").option("--expected-hash <sha256>", "expected current artifact hash; required when applying").option("--expected-revision <sha256>", "expected install manifest revision; required when applying").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) => {
|
|
15213
|
+
ownershipCommand.command("handoff").description("transfer one managed artifact between Agentwheel workspace roots").argument("<selector>", "exact artifact selector in type/name form").requiredOption("--from-workspace-root <path>", "current owning workspace root").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 current artifact hash; required when applying").option("--expected-revision <sha256>", "expected install manifest revision; required when applying").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) => {
|
|
14981
15214
|
if (!options.dryRun && (!options.expectedHash || !options.expectedRevision)) {
|
|
14982
15215
|
throw new Error("Applying an ownership handoff requires --expected-hash and --expected-revision from a reviewed --dry-run.");
|
|
14983
15216
|
}
|
|
@@ -14995,6 +15228,13 @@ ownershipCommand.command("handoff").description("transfer one managed artifact b
|
|
|
14995
15228
|
const adapter = await resolveAdapterForTarget(target, adapterOptions);
|
|
14996
15229
|
const installationType = normalizedOptions.installationType ?? target.installationType ?? resolveInstallationTypeForAdapter(adapter);
|
|
14997
15230
|
const state = installStateForTarget(target, adapter, adapterOptions, installationType);
|
|
15231
|
+
const toWorkspaceRoot = normalizeCliPath(options.toWorkspaceRoot);
|
|
15232
|
+
if (options.toFleet) {
|
|
15233
|
+
const fleet = await showRegisteredFleet(options.toFleet);
|
|
15234
|
+
if (resolve26(fleet.root) !== resolve26(toWorkspaceRoot)) {
|
|
15235
|
+
throw new Error(`Destination fleet '${options.toFleet}' is registered at ${fleet.root}, not ${toWorkspaceRoot}.`);
|
|
15236
|
+
}
|
|
15237
|
+
}
|
|
14998
15238
|
const request = {
|
|
14999
15239
|
...state,
|
|
15000
15240
|
targetRoot: state.installRoot,
|
|
@@ -15002,7 +15242,8 @@ ownershipCommand.command("handoff").description("transfer one managed artifact b
|
|
|
15002
15242
|
artifactType,
|
|
15003
15243
|
artifactName,
|
|
15004
15244
|
fromWorkspaceRoot: normalizeCliPath(options.fromWorkspaceRoot),
|
|
15005
|
-
toWorkspaceRoot
|
|
15245
|
+
toWorkspaceRoot,
|
|
15246
|
+
toFleetId: options.toFleet,
|
|
15006
15247
|
expectedHash: options.expectedHash,
|
|
15007
15248
|
expectedRevision: options.expectedRevision,
|
|
15008
15249
|
transport: transportForTarget(target)
|
package/openpack.json
CHANGED
package/package.json
CHANGED