agentwheel 0.8.1 → 0.10.0
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 +156 -137
- package/dist/index.js +644 -261
- package/openpack.json +1 -1
- package/package.json +1 -1
- package/skills/agentwheel/SKILL.md +76 -61
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
|
|
10
10
|
// src/cli/index.ts
|
|
11
11
|
import { mkdir as mkdir14, rm as rm9, writeFile as writeFile12 } from "fs/promises";
|
|
12
|
-
import { dirname as dirname21, join as join28, resolve as
|
|
12
|
+
import { dirname as dirname21, join as join28, resolve as resolve18 } from "path";
|
|
13
13
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
14
14
|
import { Command } from "commander";
|
|
15
15
|
|
|
@@ -131,9 +131,9 @@ var copilotAdapter = {
|
|
|
131
131
|
instructions: { enabled: true, dest: ".github/copilot-instructions.md" },
|
|
132
132
|
rules: { enabled: true, dest: ".github/instructions" },
|
|
133
133
|
commands: { enabled: true, dest: ".github/prompts" },
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
134
|
+
skills: { enabled: true, dest: ".github/skills" },
|
|
135
|
+
subagents: { enabled: true, dest: ".github/agents" },
|
|
136
|
+
mcp: { enabled: true, dest: ".vscode/mcp.json", merge: "json-deep" }
|
|
137
137
|
}
|
|
138
138
|
};
|
|
139
139
|
|
|
@@ -162,6 +162,7 @@ var hermesAdapter = {
|
|
|
162
162
|
rules: { enabled: true, dest: ".hermes/rules" },
|
|
163
163
|
skills: { enabled: true, dest: ".hermes/skills" },
|
|
164
164
|
commands: { enabled: true, dest: ".hermes/commands" },
|
|
165
|
+
subagents: { enabled: true, dest: ".hermes/agents" },
|
|
165
166
|
mcp: { enabled: true, dest: ".hermes/mcp", merge: "json-deep" },
|
|
166
167
|
hooks: { enabled: true, dest: ".hermes/hooks", merge: "json-deep" },
|
|
167
168
|
settings: { enabled: true, dest: ".hermes/settings.json", merge: "json-deep" }
|
|
@@ -177,6 +178,7 @@ var openClawAdapter = {
|
|
|
177
178
|
rules: { enabled: true, dest: ".openclaw/rules" },
|
|
178
179
|
skills: { enabled: true, dest: ".openclaw/skills" },
|
|
179
180
|
commands: { enabled: true, dest: ".openclaw/commands" },
|
|
181
|
+
subagents: { enabled: true, dest: ".openclaw/agents" },
|
|
180
182
|
mcp: { enabled: true, dest: ".openclaw/mcp", merge: "json-deep" },
|
|
181
183
|
hooks: { enabled: true, dest: ".openclaw/hooks", merge: "json-deep" },
|
|
182
184
|
settings: { enabled: true, dest: ".openclaw/settings.json", merge: "json-deep" },
|
|
@@ -542,13 +544,13 @@ async function spawnWithInput(command, args, input) {
|
|
|
542
544
|
if (result.stderr) throw new Error(result.stderr);
|
|
543
545
|
}
|
|
544
546
|
function waitForProcess(child, label) {
|
|
545
|
-
return new Promise((
|
|
547
|
+
return new Promise((resolve19, reject) => {
|
|
546
548
|
const stderr = [];
|
|
547
549
|
child.stderr?.on("data", (chunk) => stderr.push(chunk));
|
|
548
550
|
child.on("error", reject);
|
|
549
551
|
child.on("close", (code) => {
|
|
550
552
|
const message = Buffer.concat(stderr).toString("utf8");
|
|
551
|
-
if (code === 0)
|
|
553
|
+
if (code === 0) resolve19({ stderr: "" });
|
|
552
554
|
else reject(new Error(`${label} exited ${code}${message ? `: ${message}` : ""}`));
|
|
553
555
|
});
|
|
554
556
|
});
|
|
@@ -654,6 +656,7 @@ import { resolve as resolve3 } from "path";
|
|
|
654
656
|
// src/model/manifest.ts
|
|
655
657
|
import { z as z4 } from "zod";
|
|
656
658
|
var dependencyRoleSchema = z4.enum(["root", "direct", "transitive", "fragment"]);
|
|
659
|
+
var legacyUnownedWorkspaceOwner = "legacy:unowned";
|
|
657
660
|
var manifestEntryV1Schema = z4.object({
|
|
658
661
|
path: z4.string().min(1),
|
|
659
662
|
artifactType: artifactTypeSchema,
|
|
@@ -676,6 +679,7 @@ var manifestEntrySchema = manifestEntryV1Schema.extend({
|
|
|
676
679
|
dependencyRole: dependencyRoleSchema.default("root"),
|
|
677
680
|
owners: z4.array(z4.string().min(1)).min(1),
|
|
678
681
|
refCount: z4.number().int().positive(),
|
|
682
|
+
workspaceOwner: z4.string().min(1).default(legacyUnownedWorkspaceOwner),
|
|
679
683
|
graphLockDigest: z4.string().min(1).optional()
|
|
680
684
|
}).transform((entry) => {
|
|
681
685
|
const owners = [...new Set(entry.owners)].sort();
|
|
@@ -1050,9 +1054,6 @@ function normalizeOwners(owners) {
|
|
|
1050
1054
|
|
|
1051
1055
|
// src/install/apply.ts
|
|
1052
1056
|
var execFileAsync2 = promisify2(execFile2);
|
|
1053
|
-
async function applyInstallPlan(plan, sourceLock, options = {}) {
|
|
1054
|
-
return applyPlanTransactionally(plan, { ...options, sourceLock });
|
|
1055
|
-
}
|
|
1056
1057
|
async function applyCombinedInstallPlan(plan, options = {}) {
|
|
1057
1058
|
return applyPlanTransactionally(plan, options);
|
|
1058
1059
|
}
|
|
@@ -1165,16 +1166,20 @@ async function applyPlanTransactionally(plan, options = {}) {
|
|
|
1165
1166
|
async function uninstall(plan, options = {}) {
|
|
1166
1167
|
const resolvedOptions = typeof options === "boolean" ? { dryRun: options } : options;
|
|
1167
1168
|
const transport = resolvedOptions.transport ?? localTransport;
|
|
1169
|
+
if (resolvedOptions.keepFiles && resolvedOptions.force) {
|
|
1170
|
+
throw new Error("--keep-files cannot be combined with --force.");
|
|
1171
|
+
}
|
|
1168
1172
|
if (plan.hasBlockingChanges) {
|
|
1169
1173
|
const blockers = plan.operations.filter((operation) => operation.action === "conflict");
|
|
1170
1174
|
throw new Error(`Refusing to uninstall with blocking changes: ${blockers.map((item) => item.relativeDestPath).join(", ")}`);
|
|
1171
1175
|
}
|
|
1172
|
-
const removable = plan.operations.filter((operation) => operation.action === "remove" || resolvedOptions.force && operation
|
|
1173
|
-
const kept =
|
|
1176
|
+
const removable = plan.operations.filter((operation) => operation.action === "remove" || resolvedOptions.force && isForceRemovableKeep(operation)).map((operation) => operation.action === "keep" ? { ...operation, action: "remove", reason: `${operation.reason}; force removing drifted managed file` } : operation);
|
|
1177
|
+
const kept = plan.operations.filter((operation) => operation.action === "keep" && (!resolvedOptions.force || !isForceRemovableKeep(operation)));
|
|
1174
1178
|
const skipped = plan.operations.filter((operation) => operation.action === "skip");
|
|
1175
|
-
const removedDrifted = resolvedOptions.force ? plan.operations.filter((operation) => operation.action === "keep").length : 0;
|
|
1176
|
-
if (resolvedOptions.dryRun) return { removed: removable.length, kept: kept.length, removedDrifted };
|
|
1177
|
-
const
|
|
1179
|
+
const removedDrifted = resolvedOptions.force ? plan.operations.filter((operation) => operation.action === "keep" && isForceRemovableKeep(operation)).length : 0;
|
|
1180
|
+
if (resolvedOptions.dryRun) return { removed: resolvedOptions.keepFiles ? 0 : removable.length, kept: kept.length, removedDrifted };
|
|
1181
|
+
const preservedKept = resolvedOptions.keepFiles ? kept.filter((operation) => shouldPreserveKeptOperationWhenKeepingFiles(operation)) : kept;
|
|
1182
|
+
const preserved = [...preservedKept, ...skipped];
|
|
1178
1183
|
for (const operation of [...removable, ...preserved]) assertOperationContained(operation, plan.targetRoot);
|
|
1179
1184
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1180
1185
|
const finalManifest = withManifestRevision({
|
|
@@ -1209,7 +1214,7 @@ async function uninstall(plan, options = {}) {
|
|
|
1209
1214
|
graphLockDigest: plan.graphLockDigest,
|
|
1210
1215
|
createdAt: now,
|
|
1211
1216
|
updatedAt: now,
|
|
1212
|
-
operations: removable,
|
|
1217
|
+
operations: resolvedOptions.keepFiles ? [] : removable,
|
|
1213
1218
|
completed: [],
|
|
1214
1219
|
manifest: finalManifest,
|
|
1215
1220
|
graphLockPath: resolvedOptions.graphLock?.path,
|
|
@@ -1219,7 +1224,7 @@ async function uninstall(plan, options = {}) {
|
|
|
1219
1224
|
workspaceConfig: resolvedOptions.workspaceConfig?.data
|
|
1220
1225
|
};
|
|
1221
1226
|
await writeApplyJournal(journal, transport);
|
|
1222
|
-
for (const [index, operation] of removable.entries()) {
|
|
1227
|
+
for (const [index, operation] of (resolvedOptions.keepFiles ? [] : removable).entries()) {
|
|
1223
1228
|
const backup = await recordBackup(operation, index, plan.targetRoot, plan.adapter, transport);
|
|
1224
1229
|
journal.completed.push(backup);
|
|
1225
1230
|
await writeApplyJournal(journal, transport);
|
|
@@ -1231,7 +1236,13 @@ async function uninstall(plan, options = {}) {
|
|
|
1231
1236
|
} finally {
|
|
1232
1237
|
await lock.release();
|
|
1233
1238
|
}
|
|
1234
|
-
return { removed: removable.length, kept: kept.length, removedDrifted };
|
|
1239
|
+
return { removed: resolvedOptions.keepFiles ? 0 : removable.length, kept: kept.length, removedDrifted };
|
|
1240
|
+
}
|
|
1241
|
+
function shouldPreserveKeptOperationWhenKeepingFiles(operation) {
|
|
1242
|
+
return operation.preserveInManifest === true;
|
|
1243
|
+
}
|
|
1244
|
+
function isForceRemovableKeep(operation) {
|
|
1245
|
+
return operation.action === "keep" && operation.preserveInManifest !== true;
|
|
1235
1246
|
}
|
|
1236
1247
|
async function commitJournalState(journal, transport, entries, now) {
|
|
1237
1248
|
const manifest = withManifestRevision({
|
|
@@ -1332,6 +1343,17 @@ async function applyOperation(operation, context) {
|
|
|
1332
1343
|
graphLockDigest: context.graphLockDigest
|
|
1333
1344
|
});
|
|
1334
1345
|
}
|
|
1346
|
+
if (operation.action === "keep") {
|
|
1347
|
+
if (!operation.manifestHash || !operation.desiredHash) {
|
|
1348
|
+
throw new Error(`Invalid keep operation missing manifest/source hash: ${operation.relativeDestPath}`);
|
|
1349
|
+
}
|
|
1350
|
+
return manifestEntryForOperation(operation, {
|
|
1351
|
+
now,
|
|
1352
|
+
hash: operation.manifestHash,
|
|
1353
|
+
sourceHash: operation.desiredHash,
|
|
1354
|
+
graphLockDigest: operation.graphLockDigest ?? context.graphLockDigest
|
|
1355
|
+
});
|
|
1356
|
+
}
|
|
1335
1357
|
if (operation.action === "remove") {
|
|
1336
1358
|
await transport.rm(operation.destPath);
|
|
1337
1359
|
return void 0;
|
|
@@ -1370,6 +1392,7 @@ function manifestEntryForOperation(operation, values) {
|
|
|
1370
1392
|
dependencyRole: operation.dependencyRole ?? "root",
|
|
1371
1393
|
owners,
|
|
1372
1394
|
refCount: owners.length,
|
|
1395
|
+
workspaceOwner: operation.workspaceOwner ?? "workspace:unknown",
|
|
1373
1396
|
kind: operation.kind,
|
|
1374
1397
|
hash: values.hash,
|
|
1375
1398
|
sourceHash: values.sourceHash,
|
|
@@ -1377,7 +1400,7 @@ function manifestEntryForOperation(operation, values) {
|
|
|
1377
1400
|
channel: operation.channel,
|
|
1378
1401
|
packageName: operation.packageName,
|
|
1379
1402
|
semanticCommand: operation.semanticCommand,
|
|
1380
|
-
executed: values.executed,
|
|
1403
|
+
executed: values.executed ?? operation.execute,
|
|
1381
1404
|
mergeStrategy: operation.mergeStrategy,
|
|
1382
1405
|
composedFrom: operation.composedFrom,
|
|
1383
1406
|
graphLockDigest: operation.graphLockDigest ?? values.graphLockDigest
|
|
@@ -1441,22 +1464,6 @@ function openClawPluginInstallCommand(request) {
|
|
|
1441
1464
|
}
|
|
1442
1465
|
|
|
1443
1466
|
// src/install/plan.ts
|
|
1444
|
-
async function createInstallPlan(bundle, adapter, targetRoot, manifest, transport = localTransport) {
|
|
1445
|
-
const desired = [];
|
|
1446
|
-
for (const artifact of bundle.artifacts) {
|
|
1447
|
-
const op = operationForArtifact(artifact, adapter, targetRoot, {
|
|
1448
|
-
logicalSelector: `${artifact.type}/${artifact.name}`,
|
|
1449
|
-
dependencyRole: "root",
|
|
1450
|
-
owners: [artifact.packageName ?? bundle.source.packageName ?? bundle.source.source],
|
|
1451
|
-
composedFrom: artifact.composedFrom
|
|
1452
|
-
});
|
|
1453
|
-
if (op) {
|
|
1454
|
-
desired.push(op);
|
|
1455
|
-
}
|
|
1456
|
-
}
|
|
1457
|
-
await addProgrammaticOperations(desired, adapter, targetRoot);
|
|
1458
|
-
return createPlanFromOperations(desired, adapter, targetRoot, manifest, transport, {});
|
|
1459
|
-
}
|
|
1460
1467
|
async function createCombinedInstallPlan(desiredArtifacts, adapter, targetRoot, manifest, transport = localTransport, options = {}) {
|
|
1461
1468
|
for (const artifact of desiredArtifacts) {
|
|
1462
1469
|
if (artifact.meta.dependencyRole !== "root" && isGuardedMergeTarget(artifact.type)) {
|
|
@@ -1474,6 +1481,10 @@ async function createCombinedInstallPlan(desiredArtifacts, adapter, targetRoot,
|
|
|
1474
1481
|
return createPlanFromOperations(desired, adapter, targetRoot, manifest, transport, options);
|
|
1475
1482
|
}
|
|
1476
1483
|
async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifest, transport, options) {
|
|
1484
|
+
const workspaceOwner = options.workspaceOwner;
|
|
1485
|
+
if (workspaceOwner) {
|
|
1486
|
+
for (const op of desiredOps) op.workspaceOwner = workspaceOwner;
|
|
1487
|
+
}
|
|
1477
1488
|
for (const op of desiredOps) assertOperationContained(op, targetRoot);
|
|
1478
1489
|
const migration = await migrateManifestForPlan(manifest, desiredOps, targetRoot, transport);
|
|
1479
1490
|
const effectiveEntries = migration.entries;
|
|
@@ -1516,6 +1527,12 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
|
|
|
1516
1527
|
continue;
|
|
1517
1528
|
}
|
|
1518
1529
|
const existing = manifestByPath.get(op.relativeDestPath);
|
|
1530
|
+
if (existing && workspaceOwner && !entryOwnedByWorkspace(existing, workspaceOwner)) {
|
|
1531
|
+
if (!await canAdoptLegacyUnownedEntry(existing, op, transport)) {
|
|
1532
|
+
operations.push(keepForeignManifestEntryOperation(existing, targetRoot, workspaceOwner, op));
|
|
1533
|
+
continue;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1519
1536
|
const exists = await transport.pathExists(op.destPath);
|
|
1520
1537
|
if (!exists) {
|
|
1521
1538
|
operations.push({ ...op, action: "create", reason: "destination missing" });
|
|
@@ -1557,6 +1574,10 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
|
|
|
1557
1574
|
const destPath = join5(targetRoot, entry.path);
|
|
1558
1575
|
if (!await transport.pathExists(destPath)) continue;
|
|
1559
1576
|
const currentHash = await transport.hashPath(destPath);
|
|
1577
|
+
if (workspaceOwner && !entryOwnedByWorkspace(entry, workspaceOwner)) {
|
|
1578
|
+
operations.push(keepForeignManifestEntryOperation(entry, targetRoot, workspaceOwner, void 0, currentHash));
|
|
1579
|
+
continue;
|
|
1580
|
+
}
|
|
1560
1581
|
if (currentHash !== entry.hash) {
|
|
1561
1582
|
operations.push({
|
|
1562
1583
|
action: "drift",
|
|
@@ -1712,6 +1733,16 @@ function operationMatchesManifestEntry(op, entry) {
|
|
|
1712
1733
|
if ("graphNodeId" in entry && entry.graphNodeId && op.graphNodeId && entry.graphNodeId === op.graphNodeId) return true;
|
|
1713
1734
|
return op.packageName !== void 0 && entry.packageName === op.packageName;
|
|
1714
1735
|
}
|
|
1736
|
+
function entryOwnedByWorkspace(entry, workspaceOwner) {
|
|
1737
|
+
return "workspaceOwner" in entry && entry.workspaceOwner === workspaceOwner;
|
|
1738
|
+
}
|
|
1739
|
+
async function canAdoptLegacyUnownedEntry(entry, op, transport) {
|
|
1740
|
+
if (!("workspaceOwner" in entry) || entry.workspaceOwner !== legacyUnownedWorkspaceOwner) return false;
|
|
1741
|
+
if (entry.artifactType !== op.artifactType || entry.kind !== op.kind) return false;
|
|
1742
|
+
if (!op.desiredHash || entry.sourceHash !== op.desiredHash) return false;
|
|
1743
|
+
if (!await transport.pathExists(op.destPath)) return false;
|
|
1744
|
+
return await transport.hashPath(op.destPath) === entry.hash;
|
|
1745
|
+
}
|
|
1715
1746
|
function collisionOperation(op, group, incumbent) {
|
|
1716
1747
|
const owners = group.map(describeOperationOwner).sort();
|
|
1717
1748
|
const incumbentText = incumbent ? `; incumbent ${describeOperationOwner(incumbent)} keeps the plain name` : "";
|
|
@@ -1735,6 +1766,7 @@ function adoptLegacyEntry(entry, op) {
|
|
|
1735
1766
|
dependencyRole: op.dependencyRole ?? "root",
|
|
1736
1767
|
owners,
|
|
1737
1768
|
refCount: owners.length,
|
|
1769
|
+
workspaceOwner: op.workspaceOwner ?? legacyUnownedWorkspaceOwner,
|
|
1738
1770
|
graphLockDigest: op.graphLockDigest,
|
|
1739
1771
|
composedFrom: op.composedFrom ?? entry.composedFrom
|
|
1740
1772
|
};
|
|
@@ -1757,6 +1789,7 @@ function operationMetadataFromEntry(entry) {
|
|
|
1757
1789
|
graphNodeId: entry.graphNodeId,
|
|
1758
1790
|
dependencyRole: entry.dependencyRole,
|
|
1759
1791
|
owners: entry.owners,
|
|
1792
|
+
workspaceOwner: entry.workspaceOwner,
|
|
1760
1793
|
graphLockDigest: entry.graphLockDigest
|
|
1761
1794
|
};
|
|
1762
1795
|
}
|
|
@@ -1764,7 +1797,31 @@ function operationMetadataFromEntry(entry) {
|
|
|
1764
1797
|
installName: entry.artifactName,
|
|
1765
1798
|
logicalSelector: `${entry.artifactType}/${entry.artifactName}`,
|
|
1766
1799
|
dependencyRole: "root",
|
|
1767
|
-
owners: [entry.packageName ?? "legacy"]
|
|
1800
|
+
owners: [entry.packageName ?? "legacy"],
|
|
1801
|
+
workspaceOwner: legacyUnownedWorkspaceOwner
|
|
1802
|
+
};
|
|
1803
|
+
}
|
|
1804
|
+
function keepForeignManifestEntryOperation(entry, targetRoot, workspaceOwner, operation, currentHash) {
|
|
1805
|
+
const owner = "workspaceOwner" in entry ? entry.workspaceOwner : legacyUnownedWorkspaceOwner;
|
|
1806
|
+
return {
|
|
1807
|
+
action: "keep",
|
|
1808
|
+
artifactType: entry.artifactType,
|
|
1809
|
+
artifactName: entry.artifactName,
|
|
1810
|
+
kind: entry.kind,
|
|
1811
|
+
destPath: operation?.destPath ?? join5(targetRoot, entry.path),
|
|
1812
|
+
relativeDestPath: entry.path,
|
|
1813
|
+
desiredHash: entry.sourceHash,
|
|
1814
|
+
currentHash: currentHash ?? operation?.currentHash ?? entry.hash,
|
|
1815
|
+
manifestHash: entry.hash,
|
|
1816
|
+
reason: `foreign artifact owned by ${owner}; kept outside workspace ${workspaceOwner}`,
|
|
1817
|
+
channel: entry.channel,
|
|
1818
|
+
packageName: entry.packageName,
|
|
1819
|
+
semanticCommand: entry.semanticCommand,
|
|
1820
|
+
execute: entry.executed,
|
|
1821
|
+
mergeStrategy: entry.mergeStrategy,
|
|
1822
|
+
composedFrom: entry.composedFrom,
|
|
1823
|
+
preserveInManifest: true,
|
|
1824
|
+
...operationMetadataFromEntry(entry)
|
|
1768
1825
|
};
|
|
1769
1826
|
}
|
|
1770
1827
|
function normalizeOperationOwners(op) {
|
|
@@ -1938,7 +1995,8 @@ async function createOwnershipUninstallPlan(manifest, remainingDesired, adapter,
|
|
|
1938
1995
|
mergeStrategy: entry.mergeStrategy,
|
|
1939
1996
|
composedFrom: entry.composedFrom,
|
|
1940
1997
|
...operationMetadataFromEntry2(entry, remainingOwners),
|
|
1941
|
-
graphLockDigest: options.graphLockDigest
|
|
1998
|
+
graphLockDigest: options.graphLockDigest,
|
|
1999
|
+
preserveInManifest: true
|
|
1942
2000
|
});
|
|
1943
2001
|
continue;
|
|
1944
2002
|
}
|
|
@@ -2038,7 +2096,7 @@ function formatPlan(plan) {
|
|
|
2038
2096
|
const dropped = plan.migrationReport.dropped.length > 0 ? `; dropped unmanaged ${plan.migrationReport.dropped.join(", ")}` : "";
|
|
2039
2097
|
lines.push(`MIGRATE adopted ${plan.migrationReport.adopted} legacy entries${dropped}`);
|
|
2040
2098
|
}
|
|
2041
|
-
for (const operation of plan.operations) {
|
|
2099
|
+
for (const operation of sortedPlanOperations(plan.operations)) {
|
|
2042
2100
|
const source = operation.sourcePath ? `${operation.sourcePath} -> ` : "";
|
|
2043
2101
|
const command = operation.semanticCommand ? ` :: ${operation.semanticCommand.join(" ")}` : "";
|
|
2044
2102
|
const blocked = operation.blockedReason ? `; ${operation.blockedReason}` : "";
|
|
@@ -2050,6 +2108,14 @@ function formatPlan(plan) {
|
|
|
2050
2108
|
);
|
|
2051
2109
|
return lines.join("\n");
|
|
2052
2110
|
}
|
|
2111
|
+
function sortedPlanOperations(operations) {
|
|
2112
|
+
return [...operations].sort((a, b) => {
|
|
2113
|
+
const destructiveA = a.action === "remove" || a.action === "drift" || a.action === "conflict";
|
|
2114
|
+
const destructiveB = b.action === "remove" || b.action === "drift" || b.action === "conflict";
|
|
2115
|
+
if (destructiveA !== destructiveB) return destructiveA ? -1 : 1;
|
|
2116
|
+
return a.relativeDestPath.localeCompare(b.relativeDestPath);
|
|
2117
|
+
});
|
|
2118
|
+
}
|
|
2053
2119
|
function formatGraphPlan(result) {
|
|
2054
2120
|
const lines = [
|
|
2055
2121
|
...formatDependencyTree(result.graph),
|
|
@@ -2252,7 +2318,17 @@ var packageManifestV2Schema = z5.object({
|
|
|
2252
2318
|
runtimes: runtimeListSchema.optional(),
|
|
2253
2319
|
requires: z5.record(z5.string().min(1), packageDependencySchema).optional(),
|
|
2254
2320
|
compose: z5.array(packageComposeEntrySchema).optional(),
|
|
2255
|
-
provides: z5.array(packageProvideSchema).
|
|
2321
|
+
provides: z5.array(packageProvideSchema).default([])
|
|
2322
|
+
}).superRefine((manifest, ctx) => {
|
|
2323
|
+
const hasProvides = manifest.provides.length > 0;
|
|
2324
|
+
const hasRequires = Object.keys(manifest.requires ?? {}).length > 0;
|
|
2325
|
+
if (!hasProvides && !hasRequires) {
|
|
2326
|
+
ctx.addIssue({
|
|
2327
|
+
code: z5.ZodIssueCode.custom,
|
|
2328
|
+
path: ["provides"],
|
|
2329
|
+
message: "OpenPack v2 manifest must declare at least one provides entry or one requires dependency"
|
|
2330
|
+
});
|
|
2331
|
+
}
|
|
2256
2332
|
});
|
|
2257
2333
|
var packageManifestSchema = z5.union([packageManifestV1Schema, packageManifestV2Schema]);
|
|
2258
2334
|
var openPackManifestNames = ["openpack.json", "openpack.jsonc"];
|
|
@@ -2693,7 +2769,7 @@ async function withFilesystemLock(lockPath, timeoutMs, fn) {
|
|
|
2693
2769
|
if (Date.now() - started > timeoutMs) {
|
|
2694
2770
|
throw new Error(`Timed out waiting for git cache lock at ${lockPath}`);
|
|
2695
2771
|
}
|
|
2696
|
-
await new Promise((
|
|
2772
|
+
await new Promise((resolve19) => setTimeout(resolve19, 50));
|
|
2697
2773
|
}
|
|
2698
2774
|
}
|
|
2699
2775
|
try {
|
|
@@ -4239,6 +4315,7 @@ async function resolveDependencyGraph(roots, options) {
|
|
|
4239
4315
|
requiredBy: `workspace:${rootId}`,
|
|
4240
4316
|
rootId,
|
|
4241
4317
|
aliases: root.aliases,
|
|
4318
|
+
useLock: root.useLock ?? options.lockedResolution,
|
|
4242
4319
|
depth: 0,
|
|
4243
4320
|
optional: false,
|
|
4244
4321
|
chain: [`workspace:${rootId}`]
|
|
@@ -4295,19 +4372,41 @@ function createGraphLock(graph, artifacts = [], targetFingerprint, includeEdges
|
|
|
4295
4372
|
}
|
|
4296
4373
|
async function processRequirement(requirement, options, fetchCache, nodesByKey, rootResults, edgeMap) {
|
|
4297
4374
|
try {
|
|
4298
|
-
const
|
|
4375
|
+
const lockLabel = options.offline ? "Offline" : options.frozenLock ? "Frozen lock" : "Locked install";
|
|
4376
|
+
let lockedByReference = lockedNodeForRequirementReference(requirement, options, lockLabel);
|
|
4377
|
+
let normalized = lockedByReference ? normalizedSourceFromLockedNode(lockedByReference.node) : await normalizeDependencySource(requirement.source, {
|
|
4299
4378
|
declaringPackageRoot: requirement.declaringPackageRoot,
|
|
4300
4379
|
workspaceRoot: options.workspaceRoot,
|
|
4301
4380
|
ref: requirement.ref,
|
|
4302
4381
|
registryClient: options.registryClient
|
|
4303
4382
|
});
|
|
4304
|
-
|
|
4305
|
-
|
|
4383
|
+
if (lockedByReference && shouldCheckLockedRootSource(requirement)) {
|
|
4384
|
+
const declared = await normalizeDependencySource(requirement.source, {
|
|
4385
|
+
declaringPackageRoot: requirement.declaringPackageRoot,
|
|
4386
|
+
workspaceRoot: options.workspaceRoot,
|
|
4387
|
+
ref: requirement.ref,
|
|
4388
|
+
registryClient: options.registryClient
|
|
4389
|
+
});
|
|
4390
|
+
if (lockedRootSourceDrifted(declared, lockedByReference.node)) {
|
|
4391
|
+
if (options.frozenLock || options.offline) {
|
|
4392
|
+
throw new Error(
|
|
4393
|
+
`${lockLabel} root '${requirement.rootId}' source differs from declared source:
|
|
4394
|
+
- declared: ${declared.normalizedSource}
|
|
4395
|
+
- locked: ${lockedByReference.node.normalizedSource}
|
|
4396
|
+
Run without ${lockLabel === "Offline" ? "--offline" : "--frozen-lock"} first.`
|
|
4397
|
+
);
|
|
4398
|
+
}
|
|
4399
|
+
lockedByReference = void 0;
|
|
4400
|
+
normalized = declared;
|
|
4401
|
+
}
|
|
4402
|
+
}
|
|
4403
|
+
const frozen = lockedByReference ?? lockedNodeForRequirement(normalized.normalizedSource, requirement, options, lockLabel);
|
|
4306
4404
|
let fetched;
|
|
4307
4405
|
try {
|
|
4308
4406
|
fetched = await fetchPackage(normalized, requirement.mode, options, fetchCache, frozen?.requestedRef);
|
|
4309
4407
|
} catch (error) {
|
|
4310
|
-
|
|
4408
|
+
const usingLockedNode = frozen?.node !== void 0;
|
|
4409
|
+
if (!options.frozenLock && !options.offline && !usingLockedNode) throw error;
|
|
4311
4410
|
const message = error instanceof Error ? error.message : String(error);
|
|
4312
4411
|
const label = frozen?.node ? `${frozen.node.id} (${normalized.normalizedSource})` : normalized.normalizedSource;
|
|
4313
4412
|
throw new Error(`${lockLabel} cache missing or stale for locked graph node:
|
|
@@ -4407,7 +4506,7 @@ async function collectDependencyNeeds(state, fetched, options, chain) {
|
|
|
4407
4506
|
if (!dependency.select?.length && !(state.fullPackageSelected && dependency.select === void 0)) continue;
|
|
4408
4507
|
state.processedPackageAliases.add(alias);
|
|
4409
4508
|
if (!dependencyTargetsRuntime(dependency.runtimes, options.runtime, state.node.id, alias, options.warn)) continue;
|
|
4410
|
-
requirements.push(dependencyRequirement(state, fetched, alias, dependency, dependency.select, chain));
|
|
4509
|
+
requirements.push(dependencyRequirement(state, fetched, alias, dependency, dependency.select, chain, options.lockedResolution === true));
|
|
4411
4510
|
}
|
|
4412
4511
|
}
|
|
4413
4512
|
const artifactsBySelector = new Map(fetched.artifacts.map((artifact) => [artifactSelectorKey(artifact), artifact]));
|
|
@@ -4437,6 +4536,7 @@ async function collectDependencyNeeds(state, fetched, options, chain) {
|
|
|
4437
4536
|
dependency,
|
|
4438
4537
|
sortedUnique3([...dependency.select ?? [], parsed.selector]),
|
|
4439
4538
|
chain,
|
|
4539
|
+
options.lockedResolution === true,
|
|
4440
4540
|
parsed.optional || dependency.optional === true,
|
|
4441
4541
|
`required by ${parentSelector}`
|
|
4442
4542
|
));
|
|
@@ -4466,6 +4566,7 @@ async function collectDependencyNeeds(state, fetched, options, chain) {
|
|
|
4466
4566
|
dependency,
|
|
4467
4567
|
sortedUnique3([...dependency.select ?? [], include.selector]),
|
|
4468
4568
|
chain,
|
|
4569
|
+
options.lockedResolution === true,
|
|
4469
4570
|
include.optional || dependency.optional === true
|
|
4470
4571
|
));
|
|
4471
4572
|
}
|
|
@@ -4475,12 +4576,13 @@ async function collectDependencyNeeds(state, fetched, options, chain) {
|
|
|
4475
4576
|
refreshNode(state);
|
|
4476
4577
|
return requirements;
|
|
4477
4578
|
}
|
|
4478
|
-
function dependencyRequirement(state, fetched, alias, dependency, select, chain, optional = dependency.optional ?? false, selectionReason) {
|
|
4579
|
+
function dependencyRequirement(state, fetched, alias, dependency, select, chain, lockByDefault, optional = dependency.optional ?? false, selectionReason) {
|
|
4479
4580
|
return {
|
|
4480
4581
|
source: dependency.source,
|
|
4481
4582
|
select,
|
|
4482
4583
|
mode: dependency.mode ?? "pinned",
|
|
4483
4584
|
ref: dependency.ref,
|
|
4585
|
+
useLock: lockByDefault || dependency.mode !== "tracking",
|
|
4484
4586
|
declaringPackageRoot: fetched.resolved.resolvedPath,
|
|
4485
4587
|
requiredBy: state.node.id,
|
|
4486
4588
|
alias,
|
|
@@ -4626,7 +4728,8 @@ function dependencyTargetsRuntime(runtimes, runtime, nodeId, alias, warn) {
|
|
|
4626
4728
|
return false;
|
|
4627
4729
|
}
|
|
4628
4730
|
async function fetchPackage(normalized, mode, options, fetchCache, refOverride) {
|
|
4629
|
-
const
|
|
4731
|
+
const hardLockedCheckout = options.frozenLock === true || options.offline === true;
|
|
4732
|
+
const key = `${normalized.driver}\0${normalized.normalizedSource}\0${mode}\0${refOverride ?? ""}\0${hardLockedCheckout ? "hard-locked" : "mutable"}`;
|
|
4630
4733
|
const existing = fetchCache.get(key);
|
|
4631
4734
|
if (existing) return existing;
|
|
4632
4735
|
const promise = (async () => {
|
|
@@ -4635,7 +4738,7 @@ async function fetchPackage(normalized, mode, options, fetchCache, refOverride)
|
|
|
4635
4738
|
cacheRoot: options.cacheRoot ?? join18(options.workspaceRoot, ".agentwheel", "cache"),
|
|
4636
4739
|
mode,
|
|
4637
4740
|
ref: refOverride ?? normalized.requestedRef,
|
|
4638
|
-
frozenLock:
|
|
4741
|
+
frozenLock: hardLockedCheckout
|
|
4639
4742
|
});
|
|
4640
4743
|
const fetched = await withCachePathLock(resolved.resolvedPath, () => driver.fetch(resolved));
|
|
4641
4744
|
const translated = await driver.translate(fetched);
|
|
@@ -4664,15 +4767,20 @@ async function fetchPackage(normalized, mode, options, fetchCache, refOverride)
|
|
|
4664
4767
|
fetchCache.set(key, promise);
|
|
4665
4768
|
return promise;
|
|
4666
4769
|
}
|
|
4667
|
-
function
|
|
4668
|
-
|
|
4669
|
-
|
|
4770
|
+
function lockedNodeForRequirement(normalizedSource, requirement, options, label) {
|
|
4771
|
+
const hard = options.frozenLock === true || options.offline === true;
|
|
4772
|
+
if (!hard && !requirement.useLock) return void 0;
|
|
4773
|
+
if (!options.previousLock) {
|
|
4774
|
+
if (hard) throw new Error(`${label} requires an existing graph lock before resolving ${normalizedSource}.`);
|
|
4775
|
+
return void 0;
|
|
4670
4776
|
}
|
|
4671
|
-
const matches =
|
|
4777
|
+
const matches = options.previousLock.canonical.nodes.filter((node2) => node2.normalizedSource === normalizedSource);
|
|
4672
4778
|
if (matches.length === 0) {
|
|
4673
|
-
throw new Error(`${label} cannot resolve new source: ${normalizedSource}. Run without ${label === "Offline" ? "--offline" : "--frozen-lock"} first.`);
|
|
4779
|
+
if (hard) throw new Error(`${label} cannot resolve new source: ${normalizedSource}. Run without ${label === "Offline" ? "--offline" : "--frozen-lock"} first.`);
|
|
4780
|
+
return void 0;
|
|
4674
4781
|
}
|
|
4675
4782
|
if (matches.length > 1) {
|
|
4783
|
+
if (!hard) return void 0;
|
|
4676
4784
|
throw new Error(`${label} has multiple nodes for ${normalizedSource}; cannot choose a cached source deterministically.`);
|
|
4677
4785
|
}
|
|
4678
4786
|
const node = matches[0];
|
|
@@ -4681,6 +4789,59 @@ function lockedNodeForSource(normalizedSource, lock, label) {
|
|
|
4681
4789
|
requestedRef: node.driver === "git" ? node.resolvedCommit ?? node.requestedRef : node.requestedRef
|
|
4682
4790
|
};
|
|
4683
4791
|
}
|
|
4792
|
+
function lockedNodeForRequirementReference(requirement, options, label) {
|
|
4793
|
+
const hard = options.frozenLock === true || options.offline === true;
|
|
4794
|
+
if (!hard && !requirement.useLock) return void 0;
|
|
4795
|
+
if (!options.previousLock) {
|
|
4796
|
+
if (hard) throw new Error(`${label} requires an existing graph lock before resolving ${requirement.source}.`);
|
|
4797
|
+
return void 0;
|
|
4798
|
+
}
|
|
4799
|
+
let nodeId;
|
|
4800
|
+
let description;
|
|
4801
|
+
if (requirement.depth === 0 && requirement.rootId) {
|
|
4802
|
+
const root = options.previousLock.canonical.roots.find((candidate) => candidate.rootId === requirement.rootId);
|
|
4803
|
+
nodeId = root?.graphNodeId;
|
|
4804
|
+
description = `root ${requirement.rootId}`;
|
|
4805
|
+
} else if (requirement.parentId && requirement.alias) {
|
|
4806
|
+
const edge = options.previousLock.canonical.edges.find((candidate) => candidate.from === requirement.parentId && candidate.alias === requirement.alias);
|
|
4807
|
+
nodeId = edge?.to;
|
|
4808
|
+
description = `dependency ${requirement.parentId}:${requirement.alias}`;
|
|
4809
|
+
}
|
|
4810
|
+
if (!nodeId) {
|
|
4811
|
+
if (hard && description) {
|
|
4812
|
+
throw new Error(`${label} cannot resolve new locked ${description}. Run without ${label === "Offline" ? "--offline" : "--frozen-lock"} first.`);
|
|
4813
|
+
}
|
|
4814
|
+
return void 0;
|
|
4815
|
+
}
|
|
4816
|
+
const node = options.previousLock.canonical.nodes.find((candidate) => candidate.id === nodeId);
|
|
4817
|
+
if (!node) {
|
|
4818
|
+
throw new Error(`${label} graph lock is missing node ${nodeId} for ${description ?? requirement.source}.`);
|
|
4819
|
+
}
|
|
4820
|
+
return {
|
|
4821
|
+
node,
|
|
4822
|
+
requestedRef: node.driver === "git" ? node.resolvedCommit ?? node.requestedRef : node.requestedRef
|
|
4823
|
+
};
|
|
4824
|
+
}
|
|
4825
|
+
function shouldCheckLockedRootSource(requirement) {
|
|
4826
|
+
if (!requirement.useLock) return false;
|
|
4827
|
+
if (requirement.depth !== 0 || !requirement.rootId) return false;
|
|
4828
|
+
return isExplicitNonRegistrySource(requirement.source);
|
|
4829
|
+
}
|
|
4830
|
+
function isExplicitNonRegistrySource(source) {
|
|
4831
|
+
const trimmed = source.trim();
|
|
4832
|
+
return trimmed === "~" || trimmed.startsWith("~/") || trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed.startsWith("/") || trimmed.startsWith("local:") || trimmed.startsWith("github:") || trimmed.startsWith("git:") || trimmed.startsWith("skillkit:") || trimmed.startsWith("vercel:");
|
|
4833
|
+
}
|
|
4834
|
+
function lockedRootSourceDrifted(declared, locked) {
|
|
4835
|
+
return declared.normalizedSource !== locked.normalizedSource || declared.requestedRef !== locked.requestedRef;
|
|
4836
|
+
}
|
|
4837
|
+
function normalizedSourceFromLockedNode(node) {
|
|
4838
|
+
return {
|
|
4839
|
+
source: node.source,
|
|
4840
|
+
normalizedSource: node.normalizedSource,
|
|
4841
|
+
driver: node.driver,
|
|
4842
|
+
requestedRef: node.requestedRef
|
|
4843
|
+
};
|
|
4844
|
+
}
|
|
4684
4845
|
function verifyIntegrity(integrity, sourceHash, label) {
|
|
4685
4846
|
if (!integrity) return;
|
|
4686
4847
|
const expected = integrity.replace(/^sha256[-:]/i, "");
|
|
@@ -4691,8 +4852,8 @@ function verifyIntegrity(integrity, sourceHash, label) {
|
|
|
4691
4852
|
async function withCachePathLock(path, fn) {
|
|
4692
4853
|
const previous = cacheLocks.get(path) ?? Promise.resolve();
|
|
4693
4854
|
let release = () => void 0;
|
|
4694
|
-
const current = previous.then(() => new Promise((
|
|
4695
|
-
release =
|
|
4855
|
+
const current = previous.then(() => new Promise((resolve19) => {
|
|
4856
|
+
release = resolve19;
|
|
4696
4857
|
}));
|
|
4697
4858
|
cacheLocks.set(path, current);
|
|
4698
4859
|
await previous;
|
|
@@ -4900,7 +5061,7 @@ import { rm as rm8 } from "fs/promises";
|
|
|
4900
5061
|
// src/lifecycle/source-plan.ts
|
|
4901
5062
|
import { createHash as createHash6 } from "crypto";
|
|
4902
5063
|
import { mkdir as mkdir12 } from "fs/promises";
|
|
4903
|
-
import { dirname as dirname17, join as join22 } from "path";
|
|
5064
|
+
import { dirname as dirname17, join as join22, resolve as resolve14 } from "path";
|
|
4904
5065
|
|
|
4905
5066
|
// src/resolve/graph-diff.ts
|
|
4906
5067
|
function diffGraphLocks(previous, next) {
|
|
@@ -5381,26 +5542,6 @@ function defaultTrustStorePath() {
|
|
|
5381
5542
|
}
|
|
5382
5543
|
|
|
5383
5544
|
// src/lifecycle/source-plan.ts
|
|
5384
|
-
async function createSourcePlan(options) {
|
|
5385
|
-
const workspaceRoot = options.workspaceRoot ?? options.targetRoot;
|
|
5386
|
-
const lockMode = options.frozenLock === true || options.offline === true;
|
|
5387
|
-
const resolvedInput = await resolvePackageSource(options.source, workspaceRoot, { offline: lockMode, warn: options.warn });
|
|
5388
|
-
const resolvedSource = resolvedInput.source;
|
|
5389
|
-
const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedSource));
|
|
5390
|
-
const bundle = await stageSource(driver, resolvedSource, {
|
|
5391
|
-
workspaceRoot,
|
|
5392
|
-
adapter: options.adapter,
|
|
5393
|
-
cacheRoot: join22(workspaceRoot, ".agentwheel", "cache"),
|
|
5394
|
-
mode: options.mode,
|
|
5395
|
-
frozenLock: lockMode,
|
|
5396
|
-
select: options.select,
|
|
5397
|
-
skills: options.skills
|
|
5398
|
-
});
|
|
5399
|
-
const transport = options.transport ?? localTransport;
|
|
5400
|
-
const manifest = await readInstallManifest(options.targetRoot, options.adapter.name, transport);
|
|
5401
|
-
const plan = await createInstallPlan(bundle, options.adapter, options.targetRoot, manifest, transport);
|
|
5402
|
-
return { plan, bundle, resolvedSource, registryEntryName: resolvedInput.registryEntry?.name };
|
|
5403
|
-
}
|
|
5404
5545
|
async function createGraphSourcePlan(options) {
|
|
5405
5546
|
if (options.roots.length === 0) {
|
|
5406
5547
|
throw new Error("At least one source is required for a graph plan.");
|
|
@@ -5412,14 +5553,14 @@ async function createGraphSourcePlan(options) {
|
|
|
5412
5553
|
warnings.push(message);
|
|
5413
5554
|
options.warn?.(message);
|
|
5414
5555
|
};
|
|
5415
|
-
const recoveredPendingApply = await recoverPendingApplyIfSafe(options.targetRoot, options.adapter.name, transport);
|
|
5556
|
+
const recoveredPendingApply = options.readOnly === true ? false : await recoverPendingApplyIfSafe(options.targetRoot, options.adapter.name, transport);
|
|
5416
5557
|
const workspaceConfig = await readMergedWorkspaceConfig(workspaceRoot, { globalRoot: options.globalRoot });
|
|
5417
5558
|
const trustPolicy = {
|
|
5418
5559
|
...normalizeTrustPolicy(workspaceConfig.trust),
|
|
5419
5560
|
acceptedSources: await readTrustedSources(workspaceRoot, options.trustStorePath)
|
|
5420
5561
|
};
|
|
5421
5562
|
const lockMode = options.frozenLock === true || options.offline === true;
|
|
5422
|
-
const lockLabel = options.offline === true ? "Offline" : "Frozen lock";
|
|
5563
|
+
const lockLabel = options.offline === true ? "Offline" : options.frozenLock === true ? "Frozen lock" : options.lockedResolution === true ? "Locked install" : "Fresh resolve";
|
|
5423
5564
|
const targetFingerprint = computeTargetFingerprint(options.targetFingerprintParts ?? {
|
|
5424
5565
|
adapter: options.adapter.name,
|
|
5425
5566
|
targetRoot: options.targetRoot,
|
|
@@ -5434,6 +5575,7 @@ async function createGraphSourcePlan(options) {
|
|
|
5434
5575
|
cacheRoot: join22(workspaceRoot, ".agentwheel", "cache"),
|
|
5435
5576
|
registryClient,
|
|
5436
5577
|
noDeps: options.noDeps,
|
|
5578
|
+
lockedResolution: options.lockedResolution,
|
|
5437
5579
|
frozenLock: lockMode,
|
|
5438
5580
|
offline: options.offline,
|
|
5439
5581
|
previousLock,
|
|
@@ -5444,7 +5586,7 @@ async function createGraphSourcePlan(options) {
|
|
|
5444
5586
|
assertTrustArtifactPolicy(graph, trustPolicy);
|
|
5445
5587
|
const trustEvaluation = evaluateTransitiveTrust(graph, previousLock, trustPolicy, options.trustPatterns ?? [], options.yes === true);
|
|
5446
5588
|
await assertTrusted(trustEvaluation.promptSources, options);
|
|
5447
|
-
const persistedTrustSources = await rememberTrustedSources(workspaceRoot, trustEvaluation.persistSources, options.trustStorePath);
|
|
5589
|
+
const persistedTrustSources = options.readOnly === true ? [] : await rememberTrustedSources(workspaceRoot, trustEvaluation.persistSources, options.trustStorePath);
|
|
5448
5590
|
for (const source of persistedTrustSources) warn(`remembered trusted transitive source: ${source}`);
|
|
5449
5591
|
const bundle = await renderGraphForTarget(graph, {
|
|
5450
5592
|
workspaceRoot,
|
|
@@ -5457,7 +5599,8 @@ async function createGraphSourcePlan(options) {
|
|
|
5457
5599
|
const manifest = await readInstallManifest(options.targetRoot, options.adapter.name, transport);
|
|
5458
5600
|
const plan = await createCombinedInstallPlan(desiredArtifacts, options.adapter, options.targetRoot, manifest, transport, {
|
|
5459
5601
|
baseRevision: manifest?.revision ?? null,
|
|
5460
|
-
graphLockDigest
|
|
5602
|
+
graphLockDigest,
|
|
5603
|
+
workspaceOwner: workspaceOwnerId(workspaceRoot)
|
|
5461
5604
|
});
|
|
5462
5605
|
return {
|
|
5463
5606
|
plan,
|
|
@@ -5515,6 +5658,9 @@ function sanitizePathSegment(value) {
|
|
|
5515
5658
|
function digestGraphLock(lock) {
|
|
5516
5659
|
return createHash6("sha256").update(canonicalGraphLockJson(lock)).digest("hex");
|
|
5517
5660
|
}
|
|
5661
|
+
function workspaceOwnerId(workspaceRoot) {
|
|
5662
|
+
return `workspace-root:${resolve14(workspaceRoot)}`;
|
|
5663
|
+
}
|
|
5518
5664
|
function assertFrozenGraph(previousLock, graph, frozen, label) {
|
|
5519
5665
|
if (!frozen) return;
|
|
5520
5666
|
if (!previousLock) {
|
|
@@ -5542,6 +5688,10 @@ ${mismatches.map((item) => `- ${item}`).join("\n")}`);
|
|
|
5542
5688
|
}
|
|
5543
5689
|
async function assertTrusted(sources, options) {
|
|
5544
5690
|
if (sources.length === 0) return;
|
|
5691
|
+
if (options.readOnly === true) {
|
|
5692
|
+
throw new Error(`New transitive sources require trust. Re-run with --yes or --trust <pattern>:
|
|
5693
|
+
${sources.map((source) => `- ${source}`).join("\n")}`);
|
|
5694
|
+
}
|
|
5545
5695
|
if (options.promptTrust) {
|
|
5546
5696
|
if (await options.promptTrust(sources)) return;
|
|
5547
5697
|
throw new Error(`Untrusted transitive sources:
|
|
@@ -5609,10 +5759,12 @@ async function syncProfile(options) {
|
|
|
5609
5759
|
transport: target.transport.kind
|
|
5610
5760
|
},
|
|
5611
5761
|
noDeps: options.noDeps,
|
|
5762
|
+
lockedResolution: options.lockedResolution,
|
|
5612
5763
|
frozenLock: options.frozenLock,
|
|
5613
5764
|
offline: options.offline,
|
|
5614
5765
|
yes: options.yes,
|
|
5615
5766
|
trustPatterns: options.trustPatterns ?? [],
|
|
5767
|
+
readOnly: options.readOnly,
|
|
5616
5768
|
isTTY: options.isTTY,
|
|
5617
5769
|
warn: options.warn
|
|
5618
5770
|
});
|
|
@@ -5682,7 +5834,7 @@ async function packageFromSource(source, options) {
|
|
|
5682
5834
|
}
|
|
5683
5835
|
|
|
5684
5836
|
// src/runtime/target.ts
|
|
5685
|
-
import { basename as basename12, dirname as dirname18, join as join23, resolve as
|
|
5837
|
+
import { basename as basename12, dirname as dirname18, join as join23, resolve as resolve15 } from "path";
|
|
5686
5838
|
var runtimeMarkers = [
|
|
5687
5839
|
{ adapter: "openclaw", dirs: [".openclaw", ".clawdbot", ".moltbot"] },
|
|
5688
5840
|
{ adapter: "claude", dirs: [".claude"] },
|
|
@@ -5691,9 +5843,9 @@ var runtimeMarkers = [
|
|
|
5691
5843
|
{ adapter: "copilot", dirs: [".github"] }
|
|
5692
5844
|
];
|
|
5693
5845
|
async function resolveRuntimeTarget(request = {}) {
|
|
5694
|
-
const cwd =
|
|
5846
|
+
const cwd = resolve15(request.cwd ?? process.cwd());
|
|
5695
5847
|
if (request.targetRoot) {
|
|
5696
|
-
const targetRoot =
|
|
5848
|
+
const targetRoot = resolve15(request.targetRoot);
|
|
5697
5849
|
return {
|
|
5698
5850
|
adapter: request.adapter ?? "openclaw",
|
|
5699
5851
|
targetRoot,
|
|
@@ -5722,7 +5874,7 @@ async function resolveRuntimeTarget(request = {}) {
|
|
|
5722
5874
|
async function resolveAllRuntimeTargets(request = {}) {
|
|
5723
5875
|
if (request.targetRoot) return [await resolveRuntimeTarget(request)];
|
|
5724
5876
|
if (request.agent) return [await resolveRuntimeTarget(request)];
|
|
5725
|
-
const cwd =
|
|
5877
|
+
const cwd = resolve15(request.cwd ?? process.cwd());
|
|
5726
5878
|
const workspaceRoot = await findWorkspaceRoot(cwd);
|
|
5727
5879
|
const config = await readMergedWorkspaceConfig(workspaceRoot, { globalRoot: request.globalRoot });
|
|
5728
5880
|
const targets = Object.entries(config.agents).map(([name]) => targetFromAgent(name, config, workspaceRoot));
|
|
@@ -5731,8 +5883,29 @@ async function resolveAllRuntimeTargets(request = {}) {
|
|
|
5731
5883
|
}
|
|
5732
5884
|
return targets;
|
|
5733
5885
|
}
|
|
5886
|
+
async function resolveAllDetectedRuntimeTargets(request = {}) {
|
|
5887
|
+
if (request.agent) return [await resolveRuntimeTarget(request)];
|
|
5888
|
+
const scanRoot = runtimeScanRoot(request);
|
|
5889
|
+
const matches = await detectRuntimeTargets(scanRoot, request.adapter);
|
|
5890
|
+
if (matches.length === 0) {
|
|
5891
|
+
throw new Error(`No runtime directories detected at ${scanRoot}. Pass --target-root or --agent.`);
|
|
5892
|
+
}
|
|
5893
|
+
return Promise.all(matches.map(async (match) => ({
|
|
5894
|
+
...match,
|
|
5895
|
+
workspaceRoot: await findWorkspaceRoot(match.targetRoot),
|
|
5896
|
+
transport: "local",
|
|
5897
|
+
source: "auto-detect"
|
|
5898
|
+
})));
|
|
5899
|
+
}
|
|
5734
5900
|
async function detectRuntimeTarget(cwd = process.cwd(), adapterFilter) {
|
|
5735
|
-
const
|
|
5901
|
+
const unique = await detectRuntimeTargets(cwd, adapterFilter);
|
|
5902
|
+
if (unique.length > 1) {
|
|
5903
|
+
throw new Error(`Multiple runtime directories detected: ${unique.map((item) => `${item.adapter} at ${item.targetRoot}`).join(", ")}. Pass --adapter, --agent, or --all-detected.`);
|
|
5904
|
+
}
|
|
5905
|
+
return unique[0];
|
|
5906
|
+
}
|
|
5907
|
+
async function detectRuntimeTargets(cwd = process.cwd(), adapterFilter) {
|
|
5908
|
+
const root = resolve15(cwd);
|
|
5736
5909
|
const matches = [];
|
|
5737
5910
|
for (const marker of runtimeMarkers) {
|
|
5738
5911
|
if (adapterFilter && marker.adapter !== adapterFilter) continue;
|
|
@@ -5744,11 +5917,7 @@ async function detectRuntimeTarget(cwd = process.cwd(), adapterFilter) {
|
|
|
5744
5917
|
}
|
|
5745
5918
|
}
|
|
5746
5919
|
}
|
|
5747
|
-
|
|
5748
|
-
if (unique.length > 1) {
|
|
5749
|
-
throw new Error(`Multiple runtime directories detected: ${unique.map((item) => `${item.adapter} at ${item.targetRoot}`).join(", ")}. Pass --adapter or --agent.`);
|
|
5750
|
-
}
|
|
5751
|
-
return unique[0];
|
|
5920
|
+
return dedupeTargets(matches);
|
|
5752
5921
|
}
|
|
5753
5922
|
function targetFromAgent(name, config, workspaceRoot) {
|
|
5754
5923
|
const agent = config.agents[name];
|
|
@@ -5777,6 +5946,11 @@ function dedupeTargets(matches) {
|
|
|
5777
5946
|
}
|
|
5778
5947
|
return [...byKey.values()];
|
|
5779
5948
|
}
|
|
5949
|
+
function runtimeScanRoot(request) {
|
|
5950
|
+
const root = resolve15(request.targetRoot ?? request.cwd ?? process.cwd());
|
|
5951
|
+
if (request.targetRoot) return root;
|
|
5952
|
+
return runtimeMarkers.some((marker) => marker.dirs.includes(basename12(root))) ? dirname18(root) : root;
|
|
5953
|
+
}
|
|
5780
5954
|
|
|
5781
5955
|
// src/cli/update-check.ts
|
|
5782
5956
|
import { mkdir as mkdir13, readFile as readFile16, writeFile as writeFile10 } from "fs/promises";
|
|
@@ -5858,9 +6032,9 @@ function normalizeVersion(version) {
|
|
|
5858
6032
|
|
|
5859
6033
|
// src/model/package-validate.ts
|
|
5860
6034
|
import { stat as stat11 } from "fs/promises";
|
|
5861
|
-
import { resolve as
|
|
6035
|
+
import { resolve as resolve16 } from "path";
|
|
5862
6036
|
async function validatePackage(root) {
|
|
5863
|
-
const packageRoot =
|
|
6037
|
+
const packageRoot = resolve16(root);
|
|
5864
6038
|
const findings = [];
|
|
5865
6039
|
const manifestPath = await findPackageManifestPath(packageRoot);
|
|
5866
6040
|
if (!manifestPath) {
|
|
@@ -5928,7 +6102,7 @@ async function validateManifestComposeInclude(packageRoot, selector, optional, f
|
|
|
5928
6102
|
try {
|
|
5929
6103
|
validateSelector(selector, "compose.include", findings, manifestPath, { fragmentsOnly: true, aliases });
|
|
5930
6104
|
if (isCrossPackageSelector(selector)) return;
|
|
5931
|
-
const full =
|
|
6105
|
+
const full = resolve16(packageRoot, selector);
|
|
5932
6106
|
if (full !== packageRoot && !full.startsWith(`${packageRoot}/`)) {
|
|
5933
6107
|
findings.push({ level: "error", message: `Compose include escapes package root: ${selector}`, path: manifestPath });
|
|
5934
6108
|
return;
|
|
@@ -5982,10 +6156,10 @@ function isCrossPackageSelector(value) {
|
|
|
5982
6156
|
|
|
5983
6157
|
// src/model/package-migrate.ts
|
|
5984
6158
|
import { readFile as readFile17, rename as rename4, writeFile as writeFile11 } from "fs/promises";
|
|
5985
|
-
import { join as join26, resolve as
|
|
6159
|
+
import { join as join26, resolve as resolve17 } from "path";
|
|
5986
6160
|
import { applyEdits, modify, parse as parse3 } from "jsonc-parser";
|
|
5987
6161
|
async function migratePackageManifest(root) {
|
|
5988
|
-
const packageRoot =
|
|
6162
|
+
const packageRoot = resolve17(root);
|
|
5989
6163
|
for (const name of openPackManifestNames) {
|
|
5990
6164
|
const path = join26(packageRoot, name);
|
|
5991
6165
|
if (await pathExists(path)) {
|
|
@@ -6047,8 +6221,14 @@ function resolveCliVersion() {
|
|
|
6047
6221
|
// src/cli/index.ts
|
|
6048
6222
|
var CLI_VERSION = resolveCliVersion();
|
|
6049
6223
|
var program = new Command();
|
|
6050
|
-
program.name("agentwheel").description("Multi-runtime agent artifact orchestrator").version(CLI_VERSION).option("--no-update-check", "disable npm version update check", false)
|
|
6051
|
-
|
|
6224
|
+
program.name("agentwheel").description("Multi-runtime agent artifact orchestrator").version(CLI_VERSION).showSuggestionAfterError(false).option("--no-update-check", "disable npm version update check", false).addHelpText("after", `
|
|
6225
|
+
|
|
6226
|
+
Core flow:
|
|
6227
|
+
$ agentwheel add github:org/agent-pack --adapter codex
|
|
6228
|
+
$ agentwheel plan
|
|
6229
|
+
$ agentwheel install
|
|
6230
|
+
`);
|
|
6231
|
+
program.command("init").description("initialize an agentwheel workspace or package").argument("[kind]", "workspace or package", "workspace").option("--target-root <path>", "workspace root", process.cwd()).option("--fleet-example", "scaffold example agents and profiles in workspace config", false).action(async (kind, options) => {
|
|
6052
6232
|
const root = normalizeTargetRoot(options.targetRoot);
|
|
6053
6233
|
if (kind === "package") {
|
|
6054
6234
|
await initPackage(root);
|
|
@@ -6062,48 +6242,17 @@ program.command("init").argument("[kind]", "workspace or package", "workspace").
|
|
|
6062
6242
|
const bootstrapPackage = config.bootstrapSkills === false ? void 0 : await defaultBootstrapPackage(root);
|
|
6063
6243
|
const withBootstrap = bootstrapPackage ? upsertPackage(config, bootstrapPackage) : config;
|
|
6064
6244
|
await writeWorkspaceConfig(root, options.fleetExample ? withFleetExample(withBootstrap) : withBootstrap);
|
|
6065
|
-
console.log(
|
|
6245
|
+
console.log(`Initialized ${workspaceConfigPath(root)}.`);
|
|
6246
|
+
if (bootstrapPackage) console.log("Auto-added the agentwheel bootstrap skill for openclaw.");
|
|
6247
|
+
console.log(nextInstallNudge());
|
|
6066
6248
|
});
|
|
6067
|
-
program.command("add").argument("<source>", "package source").option("--driver <driver>", "source driver (local, git, skillkit, or vercel-skills)").option("--adapter <adapter>", "built-in adapter", "openclaw").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("--target-root <path>", "workspace root", process.cwd()).option("--mode <mode>", "pinned or tracking", "pinned").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, []).action(async (source, options) => {
|
|
6249
|
+
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, or vercel-skills)").option("--adapter <adapter>", "built-in adapter", "openclaw").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("--target-root <path>", "workspace root", process.cwd()).option("--mode <mode>", "pinned or tracking", "pinned").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, []).action(async (source, options) => {
|
|
6068
6250
|
const targetRoot = normalizeTargetRoot(options.targetRoot);
|
|
6069
|
-
const
|
|
6070
|
-
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
6071
|
-
const resolvedSource = resolvedInput.source;
|
|
6072
|
-
const driverName = options.driver ?? inferSourceDriverName(resolvedSource);
|
|
6073
|
-
const driver = getSourceDriver(driverName);
|
|
6074
|
-
const adapter = await resolveAdapter({
|
|
6075
|
-
adapter: options.adapter,
|
|
6076
|
-
adapterConfig: options.adapterConfig,
|
|
6077
|
-
adapterModule: options.adapterModule,
|
|
6078
|
-
allowAdapterCode: options.allowAdapterCode,
|
|
6079
|
-
baseDir: targetRoot,
|
|
6080
|
-
warn: (message) => console.warn(message)
|
|
6081
|
-
});
|
|
6082
|
-
const bundle = await stageSource(driver, resolvedSource, {
|
|
6083
|
-
workspaceRoot: targetRoot,
|
|
6084
|
-
adapter,
|
|
6085
|
-
cacheRoot: join28(targetRoot, ".agentwheel", "cache"),
|
|
6086
|
-
mode: options.mode,
|
|
6087
|
-
select: selectedArtifacts
|
|
6088
|
-
});
|
|
6089
|
-
const name = options.name ?? resolvedInput.registryEntry?.name ?? bundle.source.packageName ?? source;
|
|
6090
|
-
const entry = {
|
|
6091
|
-
name,
|
|
6092
|
-
source: resolvedSource,
|
|
6093
|
-
driver: driverName,
|
|
6094
|
-
adapter: adapter.name,
|
|
6095
|
-
adapterConfig: options.adapterConfig,
|
|
6096
|
-
adapterModule: options.adapterModule,
|
|
6097
|
-
adapterCodeHash: adapter.programmatic?.hash,
|
|
6098
|
-
mode: options.mode,
|
|
6099
|
-
requestedRef: bundle.source.requestedRef,
|
|
6100
|
-
select: selectedArtifacts
|
|
6101
|
-
};
|
|
6251
|
+
const entry = await packageEntryFromSource(source, targetRoot, options);
|
|
6102
6252
|
await writeWorkspaceConfig(targetRoot, upsertPackage(await readWorkspaceConfig(targetRoot), entry));
|
|
6103
|
-
|
|
6104
|
-
console.log(`Added ${name}.`);
|
|
6253
|
+
console.log(`Added ${entry.name}. Preview: agentwheel plan - Apply: agentwheel install`);
|
|
6105
6254
|
});
|
|
6106
|
-
program.command("list").argument("<source>", "package source").option("--driver <driver>", "source driver").option("--target-root <path>", "workspace root", process.cwd()).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) => {
|
|
6255
|
+
program.command("list").description("list artifacts exposed by a package source").argument("<source>", "package source").option("--driver <driver>", "source driver").option("--target-root <path>", "workspace root", process.cwd()).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) => {
|
|
6107
6256
|
const targetRoot = normalizeTargetRoot(options.targetRoot);
|
|
6108
6257
|
const selectedArtifacts = selectedArtifactsFromOptions(options);
|
|
6109
6258
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
@@ -6114,7 +6263,7 @@ program.command("list").argument("<source>", "package source").option("--driver
|
|
|
6114
6263
|
console.log(`${artifact.type} ${artifact.name} ${artifact.relativePath}`);
|
|
6115
6264
|
}
|
|
6116
6265
|
});
|
|
6117
|
-
program.command("scan").argument("<source>", "package source").option("--driver <driver>", "source driver").option("--target-root <path>", "workspace root", process.cwd()).action(async (source, options) => {
|
|
6266
|
+
program.command("scan").description("scan a package source for validation findings").argument("<source>", "package source").option("--driver <driver>", "source driver").option("--target-root <path>", "workspace root", process.cwd()).action(async (source, options) => {
|
|
6118
6267
|
const targetRoot = normalizeTargetRoot(options.targetRoot);
|
|
6119
6268
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
6120
6269
|
const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
|
|
@@ -6129,99 +6278,28 @@ program.command("scan").argument("<source>", "package source").option("--driver
|
|
|
6129
6278
|
}
|
|
6130
6279
|
if (!result.ok) process.exitCode = 1;
|
|
6131
6280
|
});
|
|
6132
|
-
program.command("plan").argument("[source]", "source
|
|
6133
|
-
|
|
6134
|
-
for (const target of targets) {
|
|
6135
|
-
if (source && options.noDeps && options.onlySource) {
|
|
6136
|
-
const { plan, bundle } = await buildPlan(source, target, options);
|
|
6137
|
-
console.log(formatPlan(plan));
|
|
6138
|
-
await rm9(bundle.root, { recursive: true, force: true });
|
|
6139
|
-
if (plan.hasBlockingChanges) process.exitCode = 1;
|
|
6140
|
-
continue;
|
|
6141
|
-
}
|
|
6142
|
-
for (const result of await buildGraphPlansForTarget(target, source, options, { useUpdateDecision: false })) {
|
|
6143
|
-
console.log(formatGraphPlan(result));
|
|
6144
|
-
await rm9(result.bundle.root, { recursive: true, force: true });
|
|
6145
|
-
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
6146
|
-
}
|
|
6147
|
-
}
|
|
6281
|
+
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").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("--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("--dry-run", "accepted for symmetry; plan never writes", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude 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("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
|
|
6282
|
+
await runInstallCommand(source, { ...options, dryRun: true }, { apply: false });
|
|
6148
6283
|
});
|
|
6149
|
-
program.command("
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
source,
|
|
6156
|
-
driver: options.driver,
|
|
6157
|
-
mode: options.mode,
|
|
6158
|
-
select: selectedArtifactsFromOptions(options),
|
|
6159
|
-
dryRun: options.dryRun,
|
|
6160
|
-
executePlugins: options.executePlugins,
|
|
6161
|
-
allowAdapterCode: options.allowAdapterCode,
|
|
6162
|
-
noDeps: options.noDeps,
|
|
6163
|
-
frozenLock: options.frozenLock,
|
|
6164
|
-
offline: options.offline,
|
|
6165
|
-
yes: options.yes,
|
|
6166
|
-
trustPatterns: options.trust ?? [],
|
|
6167
|
-
isTTY: process.stdin.isTTY === true,
|
|
6168
|
-
warn: (message) => console.warn(message)
|
|
6169
|
-
});
|
|
6170
|
-
for (const result of results) {
|
|
6171
|
-
console.log(`Profile ${options.profile} / ${result.runtime} / ${result.packageName} at ${result.targetRoot} (${result.transport}):`);
|
|
6172
|
-
console.log(formatPlan(result.plan));
|
|
6173
|
-
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
6174
|
-
}
|
|
6175
|
-
if (!options.dryRun) console.log("Applied.");
|
|
6176
|
-
return;
|
|
6177
|
-
}
|
|
6178
|
-
const targets = await resolveCliTargets(options);
|
|
6179
|
-
if (!source) {
|
|
6180
|
-
for (const target of targets) {
|
|
6181
|
-
await runConfiguredGraphPackages(target, options, { useUpdateDecision: false });
|
|
6182
|
-
}
|
|
6183
|
-
return;
|
|
6184
|
-
}
|
|
6185
|
-
for (const target of targets) {
|
|
6186
|
-
if (options.noDeps && options.onlySource) {
|
|
6187
|
-
const { plan, bundle } = await buildPlan(source, target, options);
|
|
6188
|
-
console.log(formatPlan(plan));
|
|
6189
|
-
if (!options.dryRun) {
|
|
6190
|
-
await applyInstallPlan(plan, bundle.sourceLock, { executePlugins: options.executePlugins, transport: transportForTarget(target) });
|
|
6191
|
-
console.log(`Applied ${target.adapter} at ${target.targetRoot}.`);
|
|
6192
|
-
}
|
|
6193
|
-
await rm9(bundle.root, { recursive: true, force: true });
|
|
6194
|
-
if (plan.hasBlockingChanges) process.exitCode = 1;
|
|
6195
|
-
continue;
|
|
6196
|
-
}
|
|
6197
|
-
for (const result of await buildGraphPlansForTarget(target, source, options, { useUpdateDecision: false })) {
|
|
6198
|
-
console.log(formatGraphPlan(result));
|
|
6199
|
-
if (!options.dryRun) {
|
|
6200
|
-
await applyCombinedInstallPlan(result.plan, {
|
|
6201
|
-
executePlugins: options.executePlugins,
|
|
6202
|
-
transport: transportForTarget(target),
|
|
6203
|
-
graphLockDigest: result.graphLockDigest,
|
|
6204
|
-
graphLock: { path: result.graphLockPath, lock: result.bundle.graphLock }
|
|
6205
|
-
});
|
|
6206
|
-
console.log(`Applied ${result.plan.adapter} at ${result.plan.targetRoot}.`);
|
|
6207
|
-
}
|
|
6208
|
-
await rm9(result.bundle.root, { recursive: true, force: true });
|
|
6209
|
-
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
6210
|
-
}
|
|
6211
|
-
}
|
|
6284
|
+
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").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("--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("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude 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("--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) => {
|
|
6285
|
+
await runInstallCommand(source, options, { apply: !options.dryRun });
|
|
6286
|
+
});
|
|
6287
|
+
program.command("sync", { hidden: true }).argument("[name-or-source]", "configured package name/source or package source").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter").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("--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("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude 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("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
|
|
6288
|
+
console.error("warning: 'agentwheel sync' is deprecated and will be removed in 0.10. Use 'agentwheel install'.");
|
|
6289
|
+
await runInstallCommand(source, options, { apply: !options.dryRun });
|
|
6212
6290
|
});
|
|
6213
|
-
program.command("update").option("--adapter <adapter>", "built-in adapter").option("--target-root <path>", "workspace root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--dry-run", "show plans without writing", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--allow-adapter-code", "allow loading local adapter code from configured packages", false).option("--select <type/name>", "temporarily select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "temporarily select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--no-deps", "resolve only root sources and ignore requires with a warning"
|
|
6291
|
+
program.command("update").description("re-resolve tracking packages, then apply the result").argument("[name]", "configured package name or source to update").option("--adapter <adapter>", "built-in adapter").option("--target-root <path>", "workspace root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--dry-run", "show plans without writing", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--allow-adapter-code", "allow loading local adapter code from configured packages", false).option("--select <type/name>", "temporarily select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "temporarily select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--no-deps", "resolve only root sources and ignore requires with a warning").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("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (name, options) => {
|
|
6214
6292
|
const targets = await resolveCliTargets(options);
|
|
6215
6293
|
for (const target of targets) {
|
|
6216
|
-
await runConfiguredGraphPackages(target, options, {
|
|
6294
|
+
await runConfiguredGraphPackages(target, { ...options, scope: name }, { mode: "update" });
|
|
6217
6295
|
}
|
|
6218
6296
|
});
|
|
6219
6297
|
program.command("deps").description("inspect the OpenPack dependency graph").addCommand(
|
|
6220
|
-
new Command("tree").argument("[source]", "optional source
|
|
6298
|
+
new Command("tree").description("print the OpenPack dependency graph").argument("[source]", "optional package source to resolve").option("--adapter <adapter>", "built-in adapter").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("--target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", 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("--no-deps", "resolve only root sources and ignore requires with a warning").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("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
|
|
6221
6299
|
const targets = await resolveCliTargets(options);
|
|
6222
6300
|
for (const target of targets) {
|
|
6223
6301
|
if (source) {
|
|
6224
|
-
for (const result of await buildGraphPlansForTarget(target, source, options, {
|
|
6302
|
+
for (const result of await buildGraphPlansForTarget(target, source, options, { mode: "install" })) {
|
|
6225
6303
|
console.log(formatDependencyTree(result.graph).join("\n"));
|
|
6226
6304
|
for (const decision of result.bundle.graphLock.canonical.namespacing) {
|
|
6227
6305
|
console.log(`NAMESPACE ${decision.graphNodeId}:${decision.type}/${decision.name} -> ${decision.type}/${decision.installName} (${decision.reason})`);
|
|
@@ -6235,7 +6313,7 @@ program.command("deps").description("inspect the OpenPack dependency graph").add
|
|
|
6235
6313
|
}
|
|
6236
6314
|
})
|
|
6237
6315
|
).addCommand(
|
|
6238
|
-
new Command("why").argument("<selector>", "installed path, type/installName, or graphNodeId:type/name").option("--adapter <adapter>", "built-in adapter").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("--target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).action(async (selector, options) => {
|
|
6316
|
+
new Command("why").description("explain why an artifact is installed").argument("<selector>", "installed path, type/installName, or graphNodeId:type/name").option("--adapter <adapter>", "built-in adapter").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("--target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).action(async (selector, options) => {
|
|
6239
6317
|
const targets = await resolveCliTargets(options);
|
|
6240
6318
|
for (const target of targets) {
|
|
6241
6319
|
const { lock, adapter } = await readTargetGraphLock(target, options);
|
|
@@ -6262,7 +6340,7 @@ program.command("registry").description("manage optional registry indexes").addC
|
|
|
6262
6340
|
})
|
|
6263
6341
|
);
|
|
6264
6342
|
program.command("trust").description("manage persisted source trust decisions").addCommand(
|
|
6265
|
-
new Command("forget").argument("<pattern>", "trusted source glob to revoke").option("--target-root <path>", "workspace root", process.cwd()).action(async (pattern, options) => {
|
|
6343
|
+
new Command("forget").description("forget a persisted trusted source pattern").argument("<pattern>", "trusted source glob to revoke").option("--target-root <path>", "workspace root", process.cwd()).action(async (pattern, options) => {
|
|
6266
6344
|
const removed = await forgetTrustedSources(normalizeTargetRoot(options.targetRoot), pattern);
|
|
6267
6345
|
if (removed.length === 0) {
|
|
6268
6346
|
console.log(`No persisted trust matched ${pattern}.`);
|
|
@@ -6289,23 +6367,31 @@ program.command("package").description("validate and migrate OpenPack packages")
|
|
|
6289
6367
|
console.log(result.message);
|
|
6290
6368
|
})
|
|
6291
6369
|
);
|
|
6292
|
-
program.command("remember").requiredOption("--runtime <runtime>", "runtime/adapter name").option("--target-root <path>", "workspace root", process.cwd()).argument("<text>", "text to append to the local instructions overlay").action(async (text, options) => {
|
|
6370
|
+
program.command("remember").description("append text to the local instructions overlay").requiredOption("--runtime <runtime>", "runtime/adapter name").option("--target-root <path>", "workspace root", process.cwd()).argument("<text>", "text to append to the local instructions overlay").action(async (text, options) => {
|
|
6293
6371
|
const targetRoot = normalizeTargetRoot(options.targetRoot);
|
|
6294
6372
|
const result = await remember(targetRoot, options.runtime, text);
|
|
6295
|
-
console.log(`Remembered in ${result.overlayPath}
|
|
6373
|
+
console.log(`Remembered in ${result.overlayPath}.`);
|
|
6374
|
+
console.log(nextInstallNudge());
|
|
6296
6375
|
});
|
|
6297
|
-
program.command("eject").argument("<item>", "package/type/name").option("--target-root <path>", "workspace root", process.cwd()).action(async (item, options) => {
|
|
6376
|
+
program.command("eject").description("copy a managed artifact into local ownership").argument("<item>", "package/type/name").option("--target-root <path>", "workspace root", process.cwd()).action(async (item, options) => {
|
|
6298
6377
|
const targetRoot = normalizeTargetRoot(options.targetRoot);
|
|
6299
6378
|
const result = await ejectArtifact(targetRoot, item);
|
|
6300
6379
|
console.log(`Ejected ${item} to ${result.ejectedPath}.`);
|
|
6380
|
+
console.log(nextInstallNudge());
|
|
6301
6381
|
});
|
|
6302
|
-
program.command("uninstall").argument("[package]", "configured package name or source to remove from the ownership graph").option("--adapter <adapter>", "adapter").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("--target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--dry-run", "show removals without writing", false).option("--force", "remove drifted managed files too", false).option("--select <type/name>", "uninstall only selected artifact type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "uninstall only selected skill name (repeatable or comma-separated)", collectSkillOption, []).option("--frozen-lock", "resolve remaining packages strictly from the existing graph lock and cached sources", false).option("--offline", "resolve remaining packages strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources while resolving remaining packages", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (packageName, options) => {
|
|
6382
|
+
program.command("uninstall").description("remove configured packages or managed runtime files").argument("[package]", "configured package name or source to remove from the ownership graph").option("--adapter <adapter>", "adapter").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("--target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--dry-run", "show removals without writing", false).option("--force", "remove drifted managed files too", false).option("--keep-files", "remove from config and manifest but leave runtime files unmanaged", false).option("--select <type/name>", "uninstall only selected artifact type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "uninstall only selected skill name (repeatable or comma-separated)", collectSkillOption, []).option("--frozen-lock", "resolve remaining packages strictly from the existing graph lock and cached sources", false).option("--offline", "resolve remaining packages strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources while resolving remaining packages", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (packageName, options) => {
|
|
6383
|
+
if (options.keepFiles && options.force) {
|
|
6384
|
+
throw new Error("--keep-files cannot be combined with --force.");
|
|
6385
|
+
}
|
|
6303
6386
|
const targets = await resolveCliTargets(options);
|
|
6304
6387
|
for (const target of targets) {
|
|
6305
6388
|
if (packageName) {
|
|
6306
6389
|
await uninstallConfiguredPackage(target, packageName, options);
|
|
6307
6390
|
continue;
|
|
6308
6391
|
}
|
|
6392
|
+
if (options.keepFiles) {
|
|
6393
|
+
throw new Error("--keep-files requires a configured package name or source.");
|
|
6394
|
+
}
|
|
6309
6395
|
const adapter = await resolveAdapterForTarget(target, options);
|
|
6310
6396
|
const transport = transportForTarget(target);
|
|
6311
6397
|
const manifest = await readInstallManifest(target.targetRoot, adapter.name, transport);
|
|
@@ -6326,28 +6412,149 @@ program.command("uninstall").argument("[package]", "configured package name or s
|
|
|
6326
6412
|
if (plan.hasBlockingChanges) process.exitCode = 1;
|
|
6327
6413
|
}
|
|
6328
6414
|
});
|
|
6329
|
-
|
|
6330
|
-
const
|
|
6331
|
-
const
|
|
6332
|
-
|
|
6333
|
-
|
|
6334
|
-
|
|
6335
|
-
|
|
6415
|
+
program.command("status").description("show configured packages and runtime install state").option("--adapter <adapter>", "built-in adapter").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("--target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).action(async (options) => {
|
|
6416
|
+
const targets = await resolveCliTargets(options);
|
|
6417
|
+
for (const target of targets) {
|
|
6418
|
+
await printStatus(target, options);
|
|
6419
|
+
}
|
|
6420
|
+
});
|
|
6421
|
+
async function runInstallCommand(nameOrSource, options, behavior) {
|
|
6422
|
+
if (options.profile) {
|
|
6423
|
+
const target = await resolveRuntimeTarget({ targetRoot: options.targetRoot, adapter: options.adapter, agent: options.agent });
|
|
6424
|
+
const results = await syncProfile({
|
|
6425
|
+
workspaceRoot: target.workspaceRoot,
|
|
6426
|
+
profile: options.profile,
|
|
6427
|
+
source: nameOrSource,
|
|
6428
|
+
driver: options.driver,
|
|
6429
|
+
mode: options.mode,
|
|
6430
|
+
select: selectedArtifactsFromOptions(options),
|
|
6431
|
+
dryRun: !behavior.apply,
|
|
6432
|
+
executePlugins: options.executePlugins,
|
|
6433
|
+
allowAdapterCode: options.allowAdapterCode,
|
|
6434
|
+
noDeps: noDepsFromOptions(options),
|
|
6435
|
+
lockedResolution: true,
|
|
6436
|
+
frozenLock: options.frozenLock,
|
|
6437
|
+
offline: options.offline,
|
|
6438
|
+
yes: options.yes,
|
|
6439
|
+
trustPatterns: options.trust ?? [],
|
|
6440
|
+
readOnly: !behavior.apply,
|
|
6441
|
+
isTTY: process.stdin.isTTY === true,
|
|
6442
|
+
warn: (message) => console.warn(message)
|
|
6443
|
+
});
|
|
6444
|
+
for (const result of results) {
|
|
6445
|
+
console.log(`Profile ${options.profile} / ${result.runtime} / ${result.packageName} at ${result.targetRoot} (${result.transport}):`);
|
|
6446
|
+
console.log(formatPlan(result.plan));
|
|
6447
|
+
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
6448
|
+
}
|
|
6449
|
+
if (behavior.apply) console.log("Applied.");
|
|
6450
|
+
return;
|
|
6451
|
+
}
|
|
6452
|
+
const targets = await resolveCliTargets(options);
|
|
6453
|
+
for (const target of targets) {
|
|
6454
|
+
const config = await readMergedWorkspaceConfig(target.workspaceRoot);
|
|
6455
|
+
const configured = nameOrSource ? findConfiguredPackage(config.packages, nameOrSource) : void 0;
|
|
6456
|
+
let source;
|
|
6457
|
+
let scope = configured?.name;
|
|
6458
|
+
let extraPackage;
|
|
6459
|
+
if (nameOrSource && !configured) {
|
|
6460
|
+
try {
|
|
6461
|
+
const entry = await packageEntryFromSource(nameOrSource, target.workspaceRoot, { ...options, adapter: options.adapter ?? target.adapter });
|
|
6462
|
+
scope = entry.name;
|
|
6463
|
+
if (behavior.apply) {
|
|
6464
|
+
await writeWorkspaceConfig(target.workspaceRoot, upsertPackage(await readWorkspaceConfig(target.workspaceRoot), entry));
|
|
6465
|
+
} else {
|
|
6466
|
+
source = nameOrSource;
|
|
6467
|
+
extraPackage = entry;
|
|
6468
|
+
}
|
|
6469
|
+
} catch (error) {
|
|
6470
|
+
throw teachingInstallError(nameOrSource, error);
|
|
6471
|
+
}
|
|
6472
|
+
}
|
|
6473
|
+
for (const result of await buildGraphPlansForTarget(target, source, { ...options, scope, extraPackage }, { mode: "install" })) {
|
|
6474
|
+
console.log(formatGraphPlan(result));
|
|
6475
|
+
if (behavior.apply) {
|
|
6476
|
+
await applyCombinedInstallPlan(result.plan, {
|
|
6477
|
+
executePlugins: options.executePlugins,
|
|
6478
|
+
transport: transportForTarget(target),
|
|
6479
|
+
graphLockDigest: result.graphLockDigest,
|
|
6480
|
+
graphLock: { path: result.graphLockPath, lock: result.bundle.graphLock }
|
|
6481
|
+
});
|
|
6482
|
+
console.log(`Applied ${result.plan.adapter} at ${result.plan.targetRoot}.`);
|
|
6483
|
+
}
|
|
6484
|
+
await rm9(result.bundle.root, { recursive: true, force: true });
|
|
6485
|
+
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
6486
|
+
}
|
|
6487
|
+
}
|
|
6488
|
+
}
|
|
6489
|
+
async function packageEntryFromSource(source, targetRoot, options) {
|
|
6490
|
+
const selectedArtifacts = selectedArtifactsFromOptions(options);
|
|
6491
|
+
const lockMode = options.frozenLock === true || options.offline === true;
|
|
6492
|
+
const resolvedInput = await resolvePackageSource(source, targetRoot, { offline: lockMode });
|
|
6493
|
+
const resolvedSource = resolvedInput.source;
|
|
6494
|
+
const driverName = options.driver ?? inferSourceDriverName(resolvedSource);
|
|
6495
|
+
const driver = getSourceDriver(driverName);
|
|
6496
|
+
const adapter = await resolveAdapter({
|
|
6497
|
+
adapter: options.adapter ?? "openclaw",
|
|
6498
|
+
adapterConfig: options.adapterConfig,
|
|
6499
|
+
adapterModule: options.adapterModule,
|
|
6500
|
+
allowAdapterCode: options.allowAdapterCode,
|
|
6501
|
+
baseDir: targetRoot,
|
|
6502
|
+
warn: (message) => console.warn(message)
|
|
6503
|
+
});
|
|
6504
|
+
const bundle = await stageSource(driver, resolvedSource, {
|
|
6505
|
+
workspaceRoot: targetRoot,
|
|
6336
6506
|
adapter,
|
|
6337
|
-
|
|
6507
|
+
cacheRoot: join28(targetRoot, ".agentwheel", "cache"),
|
|
6338
6508
|
mode: options.mode,
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
offline: options.offline,
|
|
6342
|
-
warn: (message) => console.warn(message),
|
|
6343
|
-
transport
|
|
6509
|
+
frozenLock: lockMode,
|
|
6510
|
+
select: selectedArtifacts
|
|
6344
6511
|
});
|
|
6345
|
-
|
|
6512
|
+
try {
|
|
6513
|
+
return {
|
|
6514
|
+
name: options.name ?? resolvedInput.registryEntry?.name ?? bundle.source.packageName ?? source,
|
|
6515
|
+
source: resolvedSource,
|
|
6516
|
+
driver: driverName,
|
|
6517
|
+
adapter: adapter.name,
|
|
6518
|
+
adapterConfig: options.adapterConfig,
|
|
6519
|
+
adapterModule: options.adapterModule,
|
|
6520
|
+
adapterCodeHash: adapter.programmatic?.hash,
|
|
6521
|
+
mode: options.mode ?? "pinned",
|
|
6522
|
+
requestedRef: bundle.source.requestedRef,
|
|
6523
|
+
select: selectedArtifacts
|
|
6524
|
+
};
|
|
6525
|
+
} finally {
|
|
6526
|
+
await rm9(bundle.root, { recursive: true, force: true });
|
|
6527
|
+
}
|
|
6528
|
+
}
|
|
6529
|
+
function findConfiguredPackage(packages, value) {
|
|
6530
|
+
return packages.find((pkg) => pkg.name === value || pkg.source === value);
|
|
6531
|
+
}
|
|
6532
|
+
function noDepsFromOptions(options) {
|
|
6533
|
+
return options.noDeps === true || options.deps === false;
|
|
6534
|
+
}
|
|
6535
|
+
function teachingInstallError(input, cause) {
|
|
6536
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
6537
|
+
return new Error(
|
|
6538
|
+
`'${input}' is not a configured package and could not be resolved as a source.
|
|
6539
|
+
To add and install a new package: agentwheel install <source> (e.g. github:org/pack)
|
|
6540
|
+
To see what's configured: agentwheel status
|
|
6541
|
+
|
|
6542
|
+
Resolver error: ${message}`
|
|
6543
|
+
);
|
|
6544
|
+
}
|
|
6545
|
+
function nextInstallNudge() {
|
|
6546
|
+
return "Preview: agentwheel plan - Apply: agentwheel install";
|
|
6346
6547
|
}
|
|
6347
6548
|
async function resolveCliTargets(options) {
|
|
6549
|
+
if (options.all && options.allDetected) {
|
|
6550
|
+
throw new Error("Choose either --all for configured agents or --all-detected for detected runtime directories.");
|
|
6551
|
+
}
|
|
6348
6552
|
if (options.all) {
|
|
6349
6553
|
return resolveAllRuntimeTargets({ targetRoot: options.targetRoot, adapter: options.adapter, agent: options.agent, all: options.all });
|
|
6350
6554
|
}
|
|
6555
|
+
if (options.allDetected) {
|
|
6556
|
+
return resolveAllDetectedRuntimeTargets({ targetRoot: options.targetRoot, adapter: options.adapter, agent: options.agent, allDetected: options.allDetected });
|
|
6557
|
+
}
|
|
6351
6558
|
return [await resolveRuntimeTarget({ targetRoot: options.targetRoot, adapter: options.adapter, agent: options.agent })];
|
|
6352
6559
|
}
|
|
6353
6560
|
async function resolveAdapterForTarget(target, options) {
|
|
@@ -6363,7 +6570,7 @@ async function resolveAdapterForTarget(target, options) {
|
|
|
6363
6570
|
async function runConfiguredGraphPackages(target, options, behavior) {
|
|
6364
6571
|
const results = await buildGraphPlansForTarget(target, void 0, options, behavior);
|
|
6365
6572
|
for (const result of results) {
|
|
6366
|
-
console.log(`${behavior.
|
|
6573
|
+
console.log(`${behavior.mode === "update" ? "Update" : "Install"} ${result.plan.adapter} at ${result.plan.targetRoot}:`);
|
|
6367
6574
|
console.log(formatGraphPlan(result));
|
|
6368
6575
|
if (!options.dryRun) {
|
|
6369
6576
|
await applyCombinedInstallPlan(result.plan, {
|
|
@@ -6378,10 +6585,13 @@ async function runConfiguredGraphPackages(target, options, behavior) {
|
|
|
6378
6585
|
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
6379
6586
|
}
|
|
6380
6587
|
}
|
|
6381
|
-
async function buildGraphPlansForTarget(target, source, options,
|
|
6588
|
+
async function buildGraphPlansForTarget(target, source, options, behavior) {
|
|
6382
6589
|
const config = await readMergedWorkspaceConfig(target.workspaceRoot);
|
|
6383
6590
|
const groups = /* @__PURE__ */ new Map();
|
|
6384
6591
|
const selectedArtifacts = selectedArtifactsFromOptions(options);
|
|
6592
|
+
const scopedPackage = options.scope ? findConfiguredPackage(config.packages, options.scope) : void 0;
|
|
6593
|
+
const scopedRootId = scopedPackage?.name ?? (source ? options.scope : void 0);
|
|
6594
|
+
if (options.scope && !scopedPackage && !source) throw new Error(`Configured package not found: ${options.scope}`);
|
|
6385
6595
|
if (!source || !options.onlySource) {
|
|
6386
6596
|
for (const pkg of config.packages) {
|
|
6387
6597
|
const group = graphGroupForPackage(groups, target, pkg, options);
|
|
@@ -6403,14 +6613,11 @@ async function buildGraphPlansForTarget(target, source, options, _behavior) {
|
|
|
6403
6613
|
allowAdapterCode: options.allowAdapterCode
|
|
6404
6614
|
},
|
|
6405
6615
|
packages: [],
|
|
6406
|
-
extraRoots: []
|
|
6616
|
+
extraRoots: [],
|
|
6617
|
+
extraPackages: []
|
|
6407
6618
|
};
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
source,
|
|
6411
|
-
mode: options.mode ?? "pinned",
|
|
6412
|
-
select: selectedArtifacts
|
|
6413
|
-
});
|
|
6619
|
+
const entry = options.extraPackage ?? await packageEntryFromSource(source, target.workspaceRoot, options);
|
|
6620
|
+
group.extraPackages.push(entry);
|
|
6414
6621
|
groups.set(key, group);
|
|
6415
6622
|
}
|
|
6416
6623
|
if (groups.size === 0) {
|
|
@@ -6420,36 +6627,161 @@ async function buildGraphPlansForTarget(target, source, options, _behavior) {
|
|
|
6420
6627
|
const results = [];
|
|
6421
6628
|
for (const group of groups.values()) {
|
|
6422
6629
|
const adapter = await resolveAdapterForTarget(group.target, group.adapterOptions);
|
|
6630
|
+
const transport = transportForTarget(group.target);
|
|
6631
|
+
const allPackages = [...group.packages, ...group.extraPackages];
|
|
6632
|
+
const groupHasScope = !scopedRootId || allPackages.some((pkg) => pkg.name === scopedRootId || pkg.source === options.scope);
|
|
6633
|
+
if (behavior.mode === "install" && scopedRootId && !groupHasScope) continue;
|
|
6634
|
+
const updateScope = behavior.mode === "update" ? scopedPackage ? /* @__PURE__ */ new Set([scopedPackage.name]) : void 0 : void 0;
|
|
6423
6635
|
const roots = [
|
|
6424
|
-
...
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6636
|
+
...allPackages.map((pkg) => {
|
|
6637
|
+
const updateThisPackage = behavior.mode === "update" && pkg.mode === "tracking" && (!updateScope || updateScope.has(pkg.name) || updateScope.has(pkg.source));
|
|
6638
|
+
return {
|
|
6639
|
+
rootId: pkg.name,
|
|
6640
|
+
source: pkg.source,
|
|
6641
|
+
mode: pkg.mode,
|
|
6642
|
+
ref: pkg.requestedRef,
|
|
6643
|
+
select: selectedArtifacts ?? normalizeArtifactSelectors(pkg.select, pkg.skills),
|
|
6644
|
+
aliases: pkg.aliases,
|
|
6645
|
+
useLock: behavior.mode === "install" ? true : !updateThisPackage
|
|
6646
|
+
};
|
|
6647
|
+
}),
|
|
6432
6648
|
...group.extraRoots
|
|
6433
6649
|
];
|
|
6650
|
+
if (behavior.mode === "update") {
|
|
6651
|
+
const changed = roots.filter((root) => root.useLock === false);
|
|
6652
|
+
if (changed.length === 0) {
|
|
6653
|
+
const label = options.scope ? ` ${options.scope}` : "";
|
|
6654
|
+
console.log(`No tracking packages to update${label}.`);
|
|
6655
|
+
continue;
|
|
6656
|
+
}
|
|
6657
|
+
}
|
|
6434
6658
|
if (roots.length === 0) continue;
|
|
6435
|
-
|
|
6659
|
+
const result = await createGraphSourcePlan({
|
|
6436
6660
|
roots,
|
|
6437
6661
|
targetRoot: group.target.targetRoot,
|
|
6438
6662
|
workspaceRoot: group.target.workspaceRoot,
|
|
6439
6663
|
adapter,
|
|
6440
|
-
transport
|
|
6664
|
+
transport,
|
|
6441
6665
|
targetKey: group.target.agentName ?? group.target.source,
|
|
6442
6666
|
targetFingerprintParts: targetFingerprintParts(group.target, adapter, group.adapterOptions),
|
|
6443
|
-
noDeps: options
|
|
6667
|
+
noDeps: noDepsFromOptions(options),
|
|
6668
|
+
lockedResolution: behavior.mode === "install",
|
|
6444
6669
|
frozenLock: options.frozenLock,
|
|
6445
6670
|
offline: options.offline,
|
|
6446
6671
|
yes: options.yes,
|
|
6447
6672
|
trustPatterns: options.trust ?? [],
|
|
6673
|
+
readOnly: options.dryRun === true,
|
|
6448
6674
|
isTTY: process.stdin.isTTY === true
|
|
6449
|
-
})
|
|
6675
|
+
});
|
|
6676
|
+
if (behavior.mode === "install" && scopedRootId) {
|
|
6677
|
+
const manifest = await readInstallManifest(group.target.targetRoot, adapter.name, transport);
|
|
6678
|
+
results.push(scopeInstallPlanToRoot(result, scopedRootId, manifest));
|
|
6679
|
+
} else {
|
|
6680
|
+
results.push(result);
|
|
6681
|
+
}
|
|
6450
6682
|
}
|
|
6451
6683
|
return results;
|
|
6452
6684
|
}
|
|
6685
|
+
function scopeInstallPlanToRoot(result, rootId, manifest) {
|
|
6686
|
+
const scopedOwners = scopedGraphOwnerKeys(result, rootId);
|
|
6687
|
+
const manifestByPath = new Map((manifest?.entries ?? []).map((entry) => [entry.path, entry]));
|
|
6688
|
+
const preservedPaths = /* @__PURE__ */ new Set();
|
|
6689
|
+
const plannedPaths = /* @__PURE__ */ new Set();
|
|
6690
|
+
const operations = [];
|
|
6691
|
+
for (const operation of result.plan.operations) {
|
|
6692
|
+
plannedPaths.add(operation.relativeDestPath);
|
|
6693
|
+
if (operationBelongsToScopedRoot(operation, scopedOwners)) {
|
|
6694
|
+
operations.push(operation);
|
|
6695
|
+
continue;
|
|
6696
|
+
}
|
|
6697
|
+
const entry = manifestByPath.get(operation.relativeDestPath);
|
|
6698
|
+
const transformed = transformOutOfScopeOperation(operation, entry, result.plan.targetRoot, rootId);
|
|
6699
|
+
for (const scopedOperation of transformed) {
|
|
6700
|
+
if (preservedPaths.has(scopedOperation.relativeDestPath)) continue;
|
|
6701
|
+
preservedPaths.add(scopedOperation.relativeDestPath);
|
|
6702
|
+
operations.push(scopedOperation);
|
|
6703
|
+
}
|
|
6704
|
+
}
|
|
6705
|
+
for (const entry of manifest?.entries ?? []) {
|
|
6706
|
+
if (plannedPaths.has(entry.path) || preservedPaths.has(entry.path) || entryBelongsToScopedRoot(entry, scopedOwners)) continue;
|
|
6707
|
+
preservedPaths.add(entry.path);
|
|
6708
|
+
operations.push(keepManifestEntryOperation(entry, result.plan.targetRoot, rootId));
|
|
6709
|
+
}
|
|
6710
|
+
return {
|
|
6711
|
+
...result,
|
|
6712
|
+
plan: {
|
|
6713
|
+
...result.plan,
|
|
6714
|
+
operations,
|
|
6715
|
+
hasBlockingChanges: operations.some((operation) => operation.action === "drift" || operation.action === "conflict")
|
|
6716
|
+
}
|
|
6717
|
+
};
|
|
6718
|
+
}
|
|
6719
|
+
function transformOutOfScopeOperation(operation, entry, targetRoot, rootId) {
|
|
6720
|
+
if (operation.action === "skip") return [operation];
|
|
6721
|
+
if (operation.action === "update" || operation.action === "drift") {
|
|
6722
|
+
return entry ? [keepManifestEntryOperation(entry, targetRoot, rootId, operation, { freshMetadata: true })] : [];
|
|
6723
|
+
}
|
|
6724
|
+
if (operation.action === "remove") {
|
|
6725
|
+
return entry ? [keepManifestEntryOperation(entry, targetRoot, rootId)] : [];
|
|
6726
|
+
}
|
|
6727
|
+
if (operation.action === "plugin" || operation.action === "program") {
|
|
6728
|
+
return entry ? [keepManifestEntryOperation(entry, targetRoot, rootId)] : [];
|
|
6729
|
+
}
|
|
6730
|
+
return [];
|
|
6731
|
+
}
|
|
6732
|
+
function scopedGraphOwnerKeys(result, rootId) {
|
|
6733
|
+
const root = result.graph.roots.find((candidate) => candidate.rootId === rootId);
|
|
6734
|
+
if (!root) throw new Error(`Resolved graph root not found for scoped install: ${rootId}`);
|
|
6735
|
+
const keys = /* @__PURE__ */ new Set([`workspace:${rootId}`]);
|
|
6736
|
+
const queue = [root.graphNodeId];
|
|
6737
|
+
while (queue.length > 0) {
|
|
6738
|
+
const nodeId = queue.shift();
|
|
6739
|
+
if (keys.has(nodeId)) continue;
|
|
6740
|
+
keys.add(nodeId);
|
|
6741
|
+
for (const edge of result.graph.edges) {
|
|
6742
|
+
if (edge.from === nodeId) queue.push(edge.to);
|
|
6743
|
+
}
|
|
6744
|
+
}
|
|
6745
|
+
return keys;
|
|
6746
|
+
}
|
|
6747
|
+
function operationBelongsToScopedRoot(operation, scopedOwners) {
|
|
6748
|
+
if (operation.graphNodeId && scopedOwners.has(operation.graphNodeId)) return true;
|
|
6749
|
+
return operation.owners?.some((owner) => scopedOwners.has(owner)) === true;
|
|
6750
|
+
}
|
|
6751
|
+
function entryBelongsToScopedRoot(entry, scopedOwners) {
|
|
6752
|
+
if ("graphNodeId" in entry && entry.graphNodeId && scopedOwners.has(entry.graphNodeId)) return true;
|
|
6753
|
+
const owners = "owners" in entry ? entry.owners : [entry.packageName ?? "legacy"];
|
|
6754
|
+
return owners.some((owner) => scopedOwners.has(owner));
|
|
6755
|
+
}
|
|
6756
|
+
function keepManifestEntryOperation(entry, targetRoot, rootId, operation, options = {}) {
|
|
6757
|
+
const owners = "owners" in entry ? entry.owners : [entry.packageName ?? "legacy"];
|
|
6758
|
+
const fresh = options.freshMetadata ? operation : void 0;
|
|
6759
|
+
return {
|
|
6760
|
+
action: "keep",
|
|
6761
|
+
artifactType: entry.artifactType,
|
|
6762
|
+
artifactName: entry.artifactName,
|
|
6763
|
+
kind: entry.kind,
|
|
6764
|
+
destPath: operation?.destPath ?? join28(targetRoot, entry.path),
|
|
6765
|
+
relativeDestPath: entry.path,
|
|
6766
|
+
desiredHash: entry.sourceHash,
|
|
6767
|
+
currentHash: operation?.currentHash ?? entry.hash,
|
|
6768
|
+
manifestHash: entry.hash,
|
|
6769
|
+
reason: `preserved outside scoped install ${rootId}`,
|
|
6770
|
+
channel: entry.channel,
|
|
6771
|
+
packageName: entry.packageName,
|
|
6772
|
+
semanticCommand: entry.semanticCommand,
|
|
6773
|
+
execute: entry.executed,
|
|
6774
|
+
mergeStrategy: entry.mergeStrategy,
|
|
6775
|
+
composedFrom: entry.composedFrom,
|
|
6776
|
+
installName: fresh?.installName ?? ("installName" in entry ? entry.installName : entry.artifactName),
|
|
6777
|
+
logicalSelector: fresh?.logicalSelector ?? ("logicalSelector" in entry ? entry.logicalSelector : `${entry.artifactType}/${entry.artifactName}`),
|
|
6778
|
+
graphNodeId: fresh?.graphNodeId ?? ("graphNodeId" in entry ? entry.graphNodeId : void 0),
|
|
6779
|
+
dependencyRole: fresh?.dependencyRole ?? ("dependencyRole" in entry ? entry.dependencyRole : "root"),
|
|
6780
|
+
owners: fresh?.owners ?? owners,
|
|
6781
|
+
workspaceOwner: fresh?.workspaceOwner ?? ("workspaceOwner" in entry ? entry.workspaceOwner : "legacy:unowned"),
|
|
6782
|
+
graphLockDigest: fresh ? void 0 : "graphLockDigest" in entry ? entry.graphLockDigest : void 0
|
|
6783
|
+
};
|
|
6784
|
+
}
|
|
6453
6785
|
async function uninstallConfiguredPackage(target, packageName, options) {
|
|
6454
6786
|
const config = await readMergedWorkspaceConfig(target.workspaceRoot);
|
|
6455
6787
|
const removed = config.packages.filter((pkg) => pkg.name === packageName || pkg.source === packageName);
|
|
@@ -6498,10 +6830,12 @@ async function uninstallConfiguredPackage(target, packageName, options) {
|
|
|
6498
6830
|
transport,
|
|
6499
6831
|
targetKey: remainingGroup.target.agentName ?? remainingGroup.target.source,
|
|
6500
6832
|
targetFingerprintParts: targetFingerprintParts(remainingGroup.target, remainingAdapter, remainingGroup.adapterOptions),
|
|
6833
|
+
lockedResolution: true,
|
|
6501
6834
|
frozenLock: options.frozenLock,
|
|
6502
6835
|
offline: options.offline,
|
|
6503
6836
|
yes: options.yes,
|
|
6504
6837
|
trustPatterns: options.trust ?? [],
|
|
6838
|
+
readOnly: options.dryRun === true,
|
|
6505
6839
|
isTTY: process.stdin.isTTY === true
|
|
6506
6840
|
});
|
|
6507
6841
|
remainingGraphPlan = result2;
|
|
@@ -6522,6 +6856,7 @@ async function uninstallConfiguredPackage(target, packageName, options) {
|
|
|
6522
6856
|
const result = await uninstall(plan, {
|
|
6523
6857
|
dryRun: options.dryRun,
|
|
6524
6858
|
force: options.force,
|
|
6859
|
+
keepFiles: options.keepFiles,
|
|
6525
6860
|
transport,
|
|
6526
6861
|
...graphLockFinalState,
|
|
6527
6862
|
workspaceConfig: {
|
|
@@ -6550,7 +6885,8 @@ function graphGroupForPackage(groups, target, pkg, options) {
|
|
|
6550
6885
|
target: packageTarget,
|
|
6551
6886
|
adapterOptions,
|
|
6552
6887
|
packages: [],
|
|
6553
|
-
extraRoots: []
|
|
6888
|
+
extraRoots: [],
|
|
6889
|
+
extraPackages: []
|
|
6554
6890
|
};
|
|
6555
6891
|
groups.set(key, created);
|
|
6556
6892
|
return created;
|
|
@@ -6593,6 +6929,53 @@ async function readTargetGraphLock(target, options) {
|
|
|
6593
6929
|
}
|
|
6594
6930
|
return { adapter, path, lock: await readGraphLock(path) };
|
|
6595
6931
|
}
|
|
6932
|
+
async function printStatus(target, options) {
|
|
6933
|
+
const config = await readMergedWorkspaceConfig(target.workspaceRoot);
|
|
6934
|
+
const adapter = await resolveAdapterForTarget(target, options);
|
|
6935
|
+
const transport = transportForTarget(target);
|
|
6936
|
+
console.log(`Status for ${adapter.name} at ${target.targetRoot}`);
|
|
6937
|
+
if (config.packages.length === 0) {
|
|
6938
|
+
console.log(`Configured packages: none at ${target.workspaceRoot}`);
|
|
6939
|
+
return;
|
|
6940
|
+
}
|
|
6941
|
+
console.log("Configured packages:");
|
|
6942
|
+
for (const pkg of config.packages) {
|
|
6943
|
+
console.log(`- ${pkg.name} (${pkg.mode}) ${pkg.source}`);
|
|
6944
|
+
}
|
|
6945
|
+
const manifest = await readInstallManifest(target.targetRoot, adapter.name, transport);
|
|
6946
|
+
console.log(manifest ? `Install manifest: ${manifest.entries.length} entries, revision ${manifest.revision}` : "Install manifest: missing");
|
|
6947
|
+
try {
|
|
6948
|
+
const { path, lock } = await readTargetGraphLock(target, options);
|
|
6949
|
+
console.log(`Graph lock: ${path}`);
|
|
6950
|
+
console.log(`Locked graph: ${lock.canonical.roots.length} roots, ${lock.canonical.nodes.length} nodes, ${lock.canonical.artifacts.length} artifacts`);
|
|
6951
|
+
} catch {
|
|
6952
|
+
console.log("Graph lock: missing");
|
|
6953
|
+
}
|
|
6954
|
+
await printPendingInstallWork(target, options);
|
|
6955
|
+
}
|
|
6956
|
+
async function printPendingInstallWork(target, options) {
|
|
6957
|
+
let results = [];
|
|
6958
|
+
try {
|
|
6959
|
+
results = await buildGraphPlansForTarget(target, void 0, { ...options, dryRun: true }, { mode: "install" });
|
|
6960
|
+
const operations = results.flatMap((result) => result.plan.operations);
|
|
6961
|
+
const pending = operations.filter((operation) => operation.action !== "skip");
|
|
6962
|
+
if (pending.length === 0) {
|
|
6963
|
+
console.log("Pending install work: none");
|
|
6964
|
+
return;
|
|
6965
|
+
}
|
|
6966
|
+
const counts = [...pending.reduce((map, operation) => {
|
|
6967
|
+
map.set(operation.action, (map.get(operation.action) ?? 0) + 1);
|
|
6968
|
+
return map;
|
|
6969
|
+
}, /* @__PURE__ */ new Map())].map(([action, count]) => `${action}=${count}`).join(", ");
|
|
6970
|
+
const blocking = pending.filter((operation) => operation.action === "conflict" || operation.action === "drift").length;
|
|
6971
|
+
console.log(`Pending install work: ${pending.length} operations (${counts}${blocking ? `; blocking=${blocking}` : ""})`);
|
|
6972
|
+
} catch (error) {
|
|
6973
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6974
|
+
console.log(`Pending install work: unavailable (${message})`);
|
|
6975
|
+
} finally {
|
|
6976
|
+
await Promise.all(results.map((result) => rm9(result.bundle.root, { recursive: true, force: true })));
|
|
6977
|
+
}
|
|
6978
|
+
}
|
|
6596
6979
|
function collectSelectOption(value, previous) {
|
|
6597
6980
|
return [...previous, ...splitSelectorList(value)];
|
|
6598
6981
|
}
|
|
@@ -6692,7 +7075,7 @@ function withFleetExample(config) {
|
|
|
6692
7075
|
};
|
|
6693
7076
|
}
|
|
6694
7077
|
async function findAgentwheelPackageRoot(start) {
|
|
6695
|
-
let current =
|
|
7078
|
+
let current = resolve18(start);
|
|
6696
7079
|
while (true) {
|
|
6697
7080
|
if (await findPackageManifestPath(current, { warnLegacy: false })) return current;
|
|
6698
7081
|
const parent = dirname21(current);
|