agentwheel 0.8.0 → 0.9.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 +131 -136
- package/dist/index.js +530 -245
- package/openpack.json +1 -1
- package/package.json +1 -1
- package/skills/agentwheel/SKILL.md +76 -61
package/dist/index.js
CHANGED
|
@@ -9,8 +9,8 @@ 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
|
|
13
|
-
import { fileURLToPath as
|
|
12
|
+
import { dirname as dirname21, join as join28, resolve as resolve17 } from "path";
|
|
13
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
14
14
|
import { Command } from "commander";
|
|
15
15
|
|
|
16
16
|
// src/adapters/resolve.ts
|
|
@@ -1050,9 +1050,6 @@ function normalizeOwners(owners) {
|
|
|
1050
1050
|
|
|
1051
1051
|
// src/install/apply.ts
|
|
1052
1052
|
var execFileAsync2 = promisify2(execFile2);
|
|
1053
|
-
async function applyInstallPlan(plan, sourceLock, options = {}) {
|
|
1054
|
-
return applyPlanTransactionally(plan, { ...options, sourceLock });
|
|
1055
|
-
}
|
|
1056
1053
|
async function applyCombinedInstallPlan(plan, options = {}) {
|
|
1057
1054
|
return applyPlanTransactionally(plan, options);
|
|
1058
1055
|
}
|
|
@@ -1165,16 +1162,20 @@ async function applyPlanTransactionally(plan, options = {}) {
|
|
|
1165
1162
|
async function uninstall(plan, options = {}) {
|
|
1166
1163
|
const resolvedOptions = typeof options === "boolean" ? { dryRun: options } : options;
|
|
1167
1164
|
const transport = resolvedOptions.transport ?? localTransport;
|
|
1165
|
+
if (resolvedOptions.keepFiles && resolvedOptions.force) {
|
|
1166
|
+
throw new Error("--keep-files cannot be combined with --force.");
|
|
1167
|
+
}
|
|
1168
1168
|
if (plan.hasBlockingChanges) {
|
|
1169
1169
|
const blockers = plan.operations.filter((operation) => operation.action === "conflict");
|
|
1170
1170
|
throw new Error(`Refusing to uninstall with blocking changes: ${blockers.map((item) => item.relativeDestPath).join(", ")}`);
|
|
1171
1171
|
}
|
|
1172
|
-
const removable = plan.operations.filter((operation) => operation.action === "remove" || resolvedOptions.force && operation
|
|
1173
|
-
const kept =
|
|
1172
|
+
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);
|
|
1173
|
+
const kept = plan.operations.filter((operation) => operation.action === "keep" && (!resolvedOptions.force || !isForceRemovableKeep(operation)));
|
|
1174
1174
|
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
|
|
1175
|
+
const removedDrifted = resolvedOptions.force ? plan.operations.filter((operation) => operation.action === "keep" && isForceRemovableKeep(operation)).length : 0;
|
|
1176
|
+
if (resolvedOptions.dryRun) return { removed: resolvedOptions.keepFiles ? 0 : removable.length, kept: kept.length, removedDrifted };
|
|
1177
|
+
const preservedKept = resolvedOptions.keepFiles ? kept.filter((operation) => shouldPreserveKeptOperationWhenKeepingFiles(operation)) : kept;
|
|
1178
|
+
const preserved = [...preservedKept, ...skipped];
|
|
1178
1179
|
for (const operation of [...removable, ...preserved]) assertOperationContained(operation, plan.targetRoot);
|
|
1179
1180
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1180
1181
|
const finalManifest = withManifestRevision({
|
|
@@ -1209,7 +1210,7 @@ async function uninstall(plan, options = {}) {
|
|
|
1209
1210
|
graphLockDigest: plan.graphLockDigest,
|
|
1210
1211
|
createdAt: now,
|
|
1211
1212
|
updatedAt: now,
|
|
1212
|
-
operations: removable,
|
|
1213
|
+
operations: resolvedOptions.keepFiles ? [] : removable,
|
|
1213
1214
|
completed: [],
|
|
1214
1215
|
manifest: finalManifest,
|
|
1215
1216
|
graphLockPath: resolvedOptions.graphLock?.path,
|
|
@@ -1219,7 +1220,7 @@ async function uninstall(plan, options = {}) {
|
|
|
1219
1220
|
workspaceConfig: resolvedOptions.workspaceConfig?.data
|
|
1220
1221
|
};
|
|
1221
1222
|
await writeApplyJournal(journal, transport);
|
|
1222
|
-
for (const [index, operation] of removable.entries()) {
|
|
1223
|
+
for (const [index, operation] of (resolvedOptions.keepFiles ? [] : removable).entries()) {
|
|
1223
1224
|
const backup = await recordBackup(operation, index, plan.targetRoot, plan.adapter, transport);
|
|
1224
1225
|
journal.completed.push(backup);
|
|
1225
1226
|
await writeApplyJournal(journal, transport);
|
|
@@ -1231,7 +1232,13 @@ async function uninstall(plan, options = {}) {
|
|
|
1231
1232
|
} finally {
|
|
1232
1233
|
await lock.release();
|
|
1233
1234
|
}
|
|
1234
|
-
return { removed: removable.length, kept: kept.length, removedDrifted };
|
|
1235
|
+
return { removed: resolvedOptions.keepFiles ? 0 : removable.length, kept: kept.length, removedDrifted };
|
|
1236
|
+
}
|
|
1237
|
+
function shouldPreserveKeptOperationWhenKeepingFiles(operation) {
|
|
1238
|
+
return operation.preserveInManifest === true;
|
|
1239
|
+
}
|
|
1240
|
+
function isForceRemovableKeep(operation) {
|
|
1241
|
+
return operation.action === "keep" && operation.preserveInManifest !== true;
|
|
1235
1242
|
}
|
|
1236
1243
|
async function commitJournalState(journal, transport, entries, now) {
|
|
1237
1244
|
const manifest = withManifestRevision({
|
|
@@ -1332,6 +1339,17 @@ async function applyOperation(operation, context) {
|
|
|
1332
1339
|
graphLockDigest: context.graphLockDigest
|
|
1333
1340
|
});
|
|
1334
1341
|
}
|
|
1342
|
+
if (operation.action === "keep") {
|
|
1343
|
+
if (!operation.manifestHash || !operation.desiredHash) {
|
|
1344
|
+
throw new Error(`Invalid keep operation missing manifest/source hash: ${operation.relativeDestPath}`);
|
|
1345
|
+
}
|
|
1346
|
+
return manifestEntryForOperation(operation, {
|
|
1347
|
+
now,
|
|
1348
|
+
hash: operation.manifestHash,
|
|
1349
|
+
sourceHash: operation.desiredHash,
|
|
1350
|
+
graphLockDigest: operation.graphLockDigest ?? context.graphLockDigest
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1335
1353
|
if (operation.action === "remove") {
|
|
1336
1354
|
await transport.rm(operation.destPath);
|
|
1337
1355
|
return void 0;
|
|
@@ -1377,7 +1395,7 @@ function manifestEntryForOperation(operation, values) {
|
|
|
1377
1395
|
channel: operation.channel,
|
|
1378
1396
|
packageName: operation.packageName,
|
|
1379
1397
|
semanticCommand: operation.semanticCommand,
|
|
1380
|
-
executed: values.executed,
|
|
1398
|
+
executed: values.executed ?? operation.execute,
|
|
1381
1399
|
mergeStrategy: operation.mergeStrategy,
|
|
1382
1400
|
composedFrom: operation.composedFrom,
|
|
1383
1401
|
graphLockDigest: operation.graphLockDigest ?? values.graphLockDigest
|
|
@@ -1441,22 +1459,6 @@ function openClawPluginInstallCommand(request) {
|
|
|
1441
1459
|
}
|
|
1442
1460
|
|
|
1443
1461
|
// 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
1462
|
async function createCombinedInstallPlan(desiredArtifacts, adapter, targetRoot, manifest, transport = localTransport, options = {}) {
|
|
1461
1463
|
for (const artifact of desiredArtifacts) {
|
|
1462
1464
|
if (artifact.meta.dependencyRole !== "root" && isGuardedMergeTarget(artifact.type)) {
|
|
@@ -1938,7 +1940,8 @@ async function createOwnershipUninstallPlan(manifest, remainingDesired, adapter,
|
|
|
1938
1940
|
mergeStrategy: entry.mergeStrategy,
|
|
1939
1941
|
composedFrom: entry.composedFrom,
|
|
1940
1942
|
...operationMetadataFromEntry2(entry, remainingOwners),
|
|
1941
|
-
graphLockDigest: options.graphLockDigest
|
|
1943
|
+
graphLockDigest: options.graphLockDigest,
|
|
1944
|
+
preserveInManifest: true
|
|
1942
1945
|
});
|
|
1943
1946
|
continue;
|
|
1944
1947
|
}
|
|
@@ -2038,7 +2041,7 @@ function formatPlan(plan) {
|
|
|
2038
2041
|
const dropped = plan.migrationReport.dropped.length > 0 ? `; dropped unmanaged ${plan.migrationReport.dropped.join(", ")}` : "";
|
|
2039
2042
|
lines.push(`MIGRATE adopted ${plan.migrationReport.adopted} legacy entries${dropped}`);
|
|
2040
2043
|
}
|
|
2041
|
-
for (const operation of plan.operations) {
|
|
2044
|
+
for (const operation of sortedPlanOperations(plan.operations)) {
|
|
2042
2045
|
const source = operation.sourcePath ? `${operation.sourcePath} -> ` : "";
|
|
2043
2046
|
const command = operation.semanticCommand ? ` :: ${operation.semanticCommand.join(" ")}` : "";
|
|
2044
2047
|
const blocked = operation.blockedReason ? `; ${operation.blockedReason}` : "";
|
|
@@ -2050,6 +2053,14 @@ function formatPlan(plan) {
|
|
|
2050
2053
|
);
|
|
2051
2054
|
return lines.join("\n");
|
|
2052
2055
|
}
|
|
2056
|
+
function sortedPlanOperations(operations) {
|
|
2057
|
+
return [...operations].sort((a, b) => {
|
|
2058
|
+
const destructiveA = a.action === "remove" || a.action === "drift" || a.action === "conflict";
|
|
2059
|
+
const destructiveB = b.action === "remove" || b.action === "drift" || b.action === "conflict";
|
|
2060
|
+
if (destructiveA !== destructiveB) return destructiveA ? -1 : 1;
|
|
2061
|
+
return a.relativeDestPath.localeCompare(b.relativeDestPath);
|
|
2062
|
+
});
|
|
2063
|
+
}
|
|
2053
2064
|
function formatGraphPlan(result) {
|
|
2054
2065
|
const lines = [
|
|
2055
2066
|
...formatDependencyTree(result.graph),
|
|
@@ -2252,7 +2263,17 @@ var packageManifestV2Schema = z5.object({
|
|
|
2252
2263
|
runtimes: runtimeListSchema.optional(),
|
|
2253
2264
|
requires: z5.record(z5.string().min(1), packageDependencySchema).optional(),
|
|
2254
2265
|
compose: z5.array(packageComposeEntrySchema).optional(),
|
|
2255
|
-
provides: z5.array(packageProvideSchema).
|
|
2266
|
+
provides: z5.array(packageProvideSchema).default([])
|
|
2267
|
+
}).superRefine((manifest, ctx) => {
|
|
2268
|
+
const hasProvides = manifest.provides.length > 0;
|
|
2269
|
+
const hasRequires = Object.keys(manifest.requires ?? {}).length > 0;
|
|
2270
|
+
if (!hasProvides && !hasRequires) {
|
|
2271
|
+
ctx.addIssue({
|
|
2272
|
+
code: z5.ZodIssueCode.custom,
|
|
2273
|
+
path: ["provides"],
|
|
2274
|
+
message: "OpenPack v2 manifest must declare at least one provides entry or one requires dependency"
|
|
2275
|
+
});
|
|
2276
|
+
}
|
|
2256
2277
|
});
|
|
2257
2278
|
var packageManifestSchema = z5.union([packageManifestV1Schema, packageManifestV2Schema]);
|
|
2258
2279
|
var openPackManifestNames = ["openpack.json", "openpack.jsonc"];
|
|
@@ -4239,6 +4260,7 @@ async function resolveDependencyGraph(roots, options) {
|
|
|
4239
4260
|
requiredBy: `workspace:${rootId}`,
|
|
4240
4261
|
rootId,
|
|
4241
4262
|
aliases: root.aliases,
|
|
4263
|
+
useLock: root.useLock ?? options.lockedResolution,
|
|
4242
4264
|
depth: 0,
|
|
4243
4265
|
optional: false,
|
|
4244
4266
|
chain: [`workspace:${rootId}`]
|
|
@@ -4295,19 +4317,21 @@ function createGraphLock(graph, artifacts = [], targetFingerprint, includeEdges
|
|
|
4295
4317
|
}
|
|
4296
4318
|
async function processRequirement(requirement, options, fetchCache, nodesByKey, rootResults, edgeMap) {
|
|
4297
4319
|
try {
|
|
4298
|
-
const
|
|
4320
|
+
const lockLabel = options.offline ? "Offline" : options.frozenLock ? "Frozen lock" : "Locked install";
|
|
4321
|
+
const lockedByReference = lockedNodeForRequirementReference(requirement, options, lockLabel);
|
|
4322
|
+
const normalized = lockedByReference ? normalizedSourceFromLockedNode(lockedByReference.node) : await normalizeDependencySource(requirement.source, {
|
|
4299
4323
|
declaringPackageRoot: requirement.declaringPackageRoot,
|
|
4300
4324
|
workspaceRoot: options.workspaceRoot,
|
|
4301
4325
|
ref: requirement.ref,
|
|
4302
4326
|
registryClient: options.registryClient
|
|
4303
4327
|
});
|
|
4304
|
-
const
|
|
4305
|
-
const frozen = options.frozenLock ? lockedNodeForSource(normalized.normalizedSource, options.previousLock, lockLabel) : void 0;
|
|
4328
|
+
const frozen = lockedByReference ?? lockedNodeForRequirement(normalized.normalizedSource, requirement, options, lockLabel);
|
|
4306
4329
|
let fetched;
|
|
4307
4330
|
try {
|
|
4308
4331
|
fetched = await fetchPackage(normalized, requirement.mode, options, fetchCache, frozen?.requestedRef);
|
|
4309
4332
|
} catch (error) {
|
|
4310
|
-
|
|
4333
|
+
const usingLockedNode = frozen?.node !== void 0;
|
|
4334
|
+
if (!options.frozenLock && !options.offline && !usingLockedNode) throw error;
|
|
4311
4335
|
const message = error instanceof Error ? error.message : String(error);
|
|
4312
4336
|
const label = frozen?.node ? `${frozen.node.id} (${normalized.normalizedSource})` : normalized.normalizedSource;
|
|
4313
4337
|
throw new Error(`${lockLabel} cache missing or stale for locked graph node:
|
|
@@ -4407,7 +4431,7 @@ async function collectDependencyNeeds(state, fetched, options, chain) {
|
|
|
4407
4431
|
if (!dependency.select?.length && !(state.fullPackageSelected && dependency.select === void 0)) continue;
|
|
4408
4432
|
state.processedPackageAliases.add(alias);
|
|
4409
4433
|
if (!dependencyTargetsRuntime(dependency.runtimes, options.runtime, state.node.id, alias, options.warn)) continue;
|
|
4410
|
-
requirements.push(dependencyRequirement(state, fetched, alias, dependency, dependency.select, chain));
|
|
4434
|
+
requirements.push(dependencyRequirement(state, fetched, alias, dependency, dependency.select, chain, options.lockedResolution === true));
|
|
4411
4435
|
}
|
|
4412
4436
|
}
|
|
4413
4437
|
const artifactsBySelector = new Map(fetched.artifacts.map((artifact) => [artifactSelectorKey(artifact), artifact]));
|
|
@@ -4437,6 +4461,7 @@ async function collectDependencyNeeds(state, fetched, options, chain) {
|
|
|
4437
4461
|
dependency,
|
|
4438
4462
|
sortedUnique3([...dependency.select ?? [], parsed.selector]),
|
|
4439
4463
|
chain,
|
|
4464
|
+
options.lockedResolution === true,
|
|
4440
4465
|
parsed.optional || dependency.optional === true,
|
|
4441
4466
|
`required by ${parentSelector}`
|
|
4442
4467
|
));
|
|
@@ -4466,6 +4491,7 @@ async function collectDependencyNeeds(state, fetched, options, chain) {
|
|
|
4466
4491
|
dependency,
|
|
4467
4492
|
sortedUnique3([...dependency.select ?? [], include.selector]),
|
|
4468
4493
|
chain,
|
|
4494
|
+
options.lockedResolution === true,
|
|
4469
4495
|
include.optional || dependency.optional === true
|
|
4470
4496
|
));
|
|
4471
4497
|
}
|
|
@@ -4475,12 +4501,13 @@ async function collectDependencyNeeds(state, fetched, options, chain) {
|
|
|
4475
4501
|
refreshNode(state);
|
|
4476
4502
|
return requirements;
|
|
4477
4503
|
}
|
|
4478
|
-
function dependencyRequirement(state, fetched, alias, dependency, select, chain, optional = dependency.optional ?? false, selectionReason) {
|
|
4504
|
+
function dependencyRequirement(state, fetched, alias, dependency, select, chain, lockByDefault, optional = dependency.optional ?? false, selectionReason) {
|
|
4479
4505
|
return {
|
|
4480
4506
|
source: dependency.source,
|
|
4481
4507
|
select,
|
|
4482
4508
|
mode: dependency.mode ?? "pinned",
|
|
4483
4509
|
ref: dependency.ref,
|
|
4510
|
+
useLock: lockByDefault || dependency.mode !== "tracking",
|
|
4484
4511
|
declaringPackageRoot: fetched.resolved.resolvedPath,
|
|
4485
4512
|
requiredBy: state.node.id,
|
|
4486
4513
|
alias,
|
|
@@ -4626,7 +4653,8 @@ function dependencyTargetsRuntime(runtimes, runtime, nodeId, alias, warn) {
|
|
|
4626
4653
|
return false;
|
|
4627
4654
|
}
|
|
4628
4655
|
async function fetchPackage(normalized, mode, options, fetchCache, refOverride) {
|
|
4629
|
-
const
|
|
4656
|
+
const hardLockedCheckout = options.frozenLock === true || options.offline === true;
|
|
4657
|
+
const key = `${normalized.driver}\0${normalized.normalizedSource}\0${mode}\0${refOverride ?? ""}\0${hardLockedCheckout ? "hard-locked" : "mutable"}`;
|
|
4630
4658
|
const existing = fetchCache.get(key);
|
|
4631
4659
|
if (existing) return existing;
|
|
4632
4660
|
const promise = (async () => {
|
|
@@ -4635,7 +4663,7 @@ async function fetchPackage(normalized, mode, options, fetchCache, refOverride)
|
|
|
4635
4663
|
cacheRoot: options.cacheRoot ?? join18(options.workspaceRoot, ".agentwheel", "cache"),
|
|
4636
4664
|
mode,
|
|
4637
4665
|
ref: refOverride ?? normalized.requestedRef,
|
|
4638
|
-
frozenLock:
|
|
4666
|
+
frozenLock: hardLockedCheckout
|
|
4639
4667
|
});
|
|
4640
4668
|
const fetched = await withCachePathLock(resolved.resolvedPath, () => driver.fetch(resolved));
|
|
4641
4669
|
const translated = await driver.translate(fetched);
|
|
@@ -4664,15 +4692,20 @@ async function fetchPackage(normalized, mode, options, fetchCache, refOverride)
|
|
|
4664
4692
|
fetchCache.set(key, promise);
|
|
4665
4693
|
return promise;
|
|
4666
4694
|
}
|
|
4667
|
-
function
|
|
4668
|
-
|
|
4669
|
-
|
|
4695
|
+
function lockedNodeForRequirement(normalizedSource, requirement, options, label) {
|
|
4696
|
+
const hard = options.frozenLock === true || options.offline === true;
|
|
4697
|
+
if (!hard && !requirement.useLock) return void 0;
|
|
4698
|
+
if (!options.previousLock) {
|
|
4699
|
+
if (hard) throw new Error(`${label} requires an existing graph lock before resolving ${normalizedSource}.`);
|
|
4700
|
+
return void 0;
|
|
4670
4701
|
}
|
|
4671
|
-
const matches =
|
|
4702
|
+
const matches = options.previousLock.canonical.nodes.filter((node2) => node2.normalizedSource === normalizedSource);
|
|
4672
4703
|
if (matches.length === 0) {
|
|
4673
|
-
throw new Error(`${label} cannot resolve new source: ${normalizedSource}. Run without ${label === "Offline" ? "--offline" : "--frozen-lock"} first.`);
|
|
4704
|
+
if (hard) throw new Error(`${label} cannot resolve new source: ${normalizedSource}. Run without ${label === "Offline" ? "--offline" : "--frozen-lock"} first.`);
|
|
4705
|
+
return void 0;
|
|
4674
4706
|
}
|
|
4675
4707
|
if (matches.length > 1) {
|
|
4708
|
+
if (!hard) return void 0;
|
|
4676
4709
|
throw new Error(`${label} has multiple nodes for ${normalizedSource}; cannot choose a cached source deterministically.`);
|
|
4677
4710
|
}
|
|
4678
4711
|
const node = matches[0];
|
|
@@ -4681,6 +4714,47 @@ function lockedNodeForSource(normalizedSource, lock, label) {
|
|
|
4681
4714
|
requestedRef: node.driver === "git" ? node.resolvedCommit ?? node.requestedRef : node.requestedRef
|
|
4682
4715
|
};
|
|
4683
4716
|
}
|
|
4717
|
+
function lockedNodeForRequirementReference(requirement, options, label) {
|
|
4718
|
+
const hard = options.frozenLock === true || options.offline === true;
|
|
4719
|
+
if (!hard && !requirement.useLock) return void 0;
|
|
4720
|
+
if (!options.previousLock) {
|
|
4721
|
+
if (hard) throw new Error(`${label} requires an existing graph lock before resolving ${requirement.source}.`);
|
|
4722
|
+
return void 0;
|
|
4723
|
+
}
|
|
4724
|
+
let nodeId;
|
|
4725
|
+
let description;
|
|
4726
|
+
if (requirement.depth === 0 && requirement.rootId) {
|
|
4727
|
+
const root = options.previousLock.canonical.roots.find((candidate) => candidate.rootId === requirement.rootId);
|
|
4728
|
+
nodeId = root?.graphNodeId;
|
|
4729
|
+
description = `root ${requirement.rootId}`;
|
|
4730
|
+
} else if (requirement.parentId && requirement.alias) {
|
|
4731
|
+
const edge = options.previousLock.canonical.edges.find((candidate) => candidate.from === requirement.parentId && candidate.alias === requirement.alias);
|
|
4732
|
+
nodeId = edge?.to;
|
|
4733
|
+
description = `dependency ${requirement.parentId}:${requirement.alias}`;
|
|
4734
|
+
}
|
|
4735
|
+
if (!nodeId) {
|
|
4736
|
+
if (hard && description) {
|
|
4737
|
+
throw new Error(`${label} cannot resolve new locked ${description}. Run without ${label === "Offline" ? "--offline" : "--frozen-lock"} first.`);
|
|
4738
|
+
}
|
|
4739
|
+
return void 0;
|
|
4740
|
+
}
|
|
4741
|
+
const node = options.previousLock.canonical.nodes.find((candidate) => candidate.id === nodeId);
|
|
4742
|
+
if (!node) {
|
|
4743
|
+
throw new Error(`${label} graph lock is missing node ${nodeId} for ${description ?? requirement.source}.`);
|
|
4744
|
+
}
|
|
4745
|
+
return {
|
|
4746
|
+
node,
|
|
4747
|
+
requestedRef: node.driver === "git" ? node.resolvedCommit ?? node.requestedRef : node.requestedRef
|
|
4748
|
+
};
|
|
4749
|
+
}
|
|
4750
|
+
function normalizedSourceFromLockedNode(node) {
|
|
4751
|
+
return {
|
|
4752
|
+
source: node.source,
|
|
4753
|
+
normalizedSource: node.normalizedSource,
|
|
4754
|
+
driver: node.driver,
|
|
4755
|
+
requestedRef: node.requestedRef
|
|
4756
|
+
};
|
|
4757
|
+
}
|
|
4684
4758
|
function verifyIntegrity(integrity, sourceHash, label) {
|
|
4685
4759
|
if (!integrity) return;
|
|
4686
4760
|
const expected = integrity.replace(/^sha256[-:]/i, "");
|
|
@@ -5381,26 +5455,6 @@ function defaultTrustStorePath() {
|
|
|
5381
5455
|
}
|
|
5382
5456
|
|
|
5383
5457
|
// 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
5458
|
async function createGraphSourcePlan(options) {
|
|
5405
5459
|
if (options.roots.length === 0) {
|
|
5406
5460
|
throw new Error("At least one source is required for a graph plan.");
|
|
@@ -5412,14 +5466,14 @@ async function createGraphSourcePlan(options) {
|
|
|
5412
5466
|
warnings.push(message);
|
|
5413
5467
|
options.warn?.(message);
|
|
5414
5468
|
};
|
|
5415
|
-
const recoveredPendingApply = await recoverPendingApplyIfSafe(options.targetRoot, options.adapter.name, transport);
|
|
5469
|
+
const recoveredPendingApply = options.readOnly === true ? false : await recoverPendingApplyIfSafe(options.targetRoot, options.adapter.name, transport);
|
|
5416
5470
|
const workspaceConfig = await readMergedWorkspaceConfig(workspaceRoot, { globalRoot: options.globalRoot });
|
|
5417
5471
|
const trustPolicy = {
|
|
5418
5472
|
...normalizeTrustPolicy(workspaceConfig.trust),
|
|
5419
5473
|
acceptedSources: await readTrustedSources(workspaceRoot, options.trustStorePath)
|
|
5420
5474
|
};
|
|
5421
5475
|
const lockMode = options.frozenLock === true || options.offline === true;
|
|
5422
|
-
const lockLabel = options.offline === true ? "Offline" : "Frozen lock";
|
|
5476
|
+
const lockLabel = options.offline === true ? "Offline" : options.frozenLock === true ? "Frozen lock" : options.lockedResolution === true ? "Locked install" : "Fresh resolve";
|
|
5423
5477
|
const targetFingerprint = computeTargetFingerprint(options.targetFingerprintParts ?? {
|
|
5424
5478
|
adapter: options.adapter.name,
|
|
5425
5479
|
targetRoot: options.targetRoot,
|
|
@@ -5434,6 +5488,7 @@ async function createGraphSourcePlan(options) {
|
|
|
5434
5488
|
cacheRoot: join22(workspaceRoot, ".agentwheel", "cache"),
|
|
5435
5489
|
registryClient,
|
|
5436
5490
|
noDeps: options.noDeps,
|
|
5491
|
+
lockedResolution: options.lockedResolution,
|
|
5437
5492
|
frozenLock: lockMode,
|
|
5438
5493
|
offline: options.offline,
|
|
5439
5494
|
previousLock,
|
|
@@ -5444,7 +5499,7 @@ async function createGraphSourcePlan(options) {
|
|
|
5444
5499
|
assertTrustArtifactPolicy(graph, trustPolicy);
|
|
5445
5500
|
const trustEvaluation = evaluateTransitiveTrust(graph, previousLock, trustPolicy, options.trustPatterns ?? [], options.yes === true);
|
|
5446
5501
|
await assertTrusted(trustEvaluation.promptSources, options);
|
|
5447
|
-
const persistedTrustSources = await rememberTrustedSources(workspaceRoot, trustEvaluation.persistSources, options.trustStorePath);
|
|
5502
|
+
const persistedTrustSources = options.readOnly === true ? [] : await rememberTrustedSources(workspaceRoot, trustEvaluation.persistSources, options.trustStorePath);
|
|
5448
5503
|
for (const source of persistedTrustSources) warn(`remembered trusted transitive source: ${source}`);
|
|
5449
5504
|
const bundle = await renderGraphForTarget(graph, {
|
|
5450
5505
|
workspaceRoot,
|
|
@@ -5542,6 +5597,10 @@ ${mismatches.map((item) => `- ${item}`).join("\n")}`);
|
|
|
5542
5597
|
}
|
|
5543
5598
|
async function assertTrusted(sources, options) {
|
|
5544
5599
|
if (sources.length === 0) return;
|
|
5600
|
+
if (options.readOnly === true) {
|
|
5601
|
+
throw new Error(`New transitive sources require trust. Re-run with --yes or --trust <pattern>:
|
|
5602
|
+
${sources.map((source) => `- ${source}`).join("\n")}`);
|
|
5603
|
+
}
|
|
5545
5604
|
if (options.promptTrust) {
|
|
5546
5605
|
if (await options.promptTrust(sources)) return;
|
|
5547
5606
|
throw new Error(`Untrusted transitive sources:
|
|
@@ -5609,10 +5668,12 @@ async function syncProfile(options) {
|
|
|
5609
5668
|
transport: target.transport.kind
|
|
5610
5669
|
},
|
|
5611
5670
|
noDeps: options.noDeps,
|
|
5671
|
+
lockedResolution: options.lockedResolution,
|
|
5612
5672
|
frozenLock: options.frozenLock,
|
|
5613
5673
|
offline: options.offline,
|
|
5614
5674
|
yes: options.yes,
|
|
5615
5675
|
trustPatterns: options.trustPatterns ?? [],
|
|
5676
|
+
readOnly: options.readOnly,
|
|
5616
5677
|
isTTY: options.isTTY,
|
|
5617
5678
|
warn: options.warn
|
|
5618
5679
|
});
|
|
@@ -6023,10 +6084,38 @@ function updateSchemaVersion(content) {
|
|
|
6023
6084
|
return applyEdits(content, edits);
|
|
6024
6085
|
}
|
|
6025
6086
|
|
|
6087
|
+
// src/cli/version.ts
|
|
6088
|
+
import { readFileSync } from "fs";
|
|
6089
|
+
import { dirname as dirname20, join as join27 } from "path";
|
|
6090
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6091
|
+
var FALLBACK_VERSION = "0.0.0";
|
|
6092
|
+
function resolveCliVersion() {
|
|
6093
|
+
let dir = dirname20(fileURLToPath2(import.meta.url));
|
|
6094
|
+
while (true) {
|
|
6095
|
+
try {
|
|
6096
|
+
const pkg = JSON.parse(readFileSync(join27(dir, "package.json"), "utf8"));
|
|
6097
|
+
if (pkg.name === "agentwheel" && typeof pkg.version === "string") {
|
|
6098
|
+
return pkg.version;
|
|
6099
|
+
}
|
|
6100
|
+
} catch {
|
|
6101
|
+
}
|
|
6102
|
+
const parent = dirname20(dir);
|
|
6103
|
+
if (parent === dir) return FALLBACK_VERSION;
|
|
6104
|
+
dir = parent;
|
|
6105
|
+
}
|
|
6106
|
+
}
|
|
6107
|
+
|
|
6026
6108
|
// src/cli/index.ts
|
|
6109
|
+
var CLI_VERSION = resolveCliVersion();
|
|
6027
6110
|
var program = new Command();
|
|
6028
|
-
program.name("agentwheel").description("Multi-runtime agent artifact orchestrator").version(
|
|
6029
|
-
|
|
6111
|
+
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", `
|
|
6112
|
+
|
|
6113
|
+
Core flow:
|
|
6114
|
+
$ agentwheel add github:org/agent-pack --adapter codex
|
|
6115
|
+
$ agentwheel plan
|
|
6116
|
+
$ agentwheel install
|
|
6117
|
+
`);
|
|
6118
|
+
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) => {
|
|
6030
6119
|
const root = normalizeTargetRoot(options.targetRoot);
|
|
6031
6120
|
if (kind === "package") {
|
|
6032
6121
|
await initPackage(root);
|
|
@@ -6040,63 +6129,32 @@ program.command("init").argument("[kind]", "workspace or package", "workspace").
|
|
|
6040
6129
|
const bootstrapPackage = config.bootstrapSkills === false ? void 0 : await defaultBootstrapPackage(root);
|
|
6041
6130
|
const withBootstrap = bootstrapPackage ? upsertPackage(config, bootstrapPackage) : config;
|
|
6042
6131
|
await writeWorkspaceConfig(root, options.fleetExample ? withFleetExample(withBootstrap) : withBootstrap);
|
|
6043
|
-
console.log(
|
|
6132
|
+
console.log(`Initialized ${workspaceConfigPath(root)}.`);
|
|
6133
|
+
if (bootstrapPackage) console.log("Auto-added the agentwheel bootstrap skill for openclaw.");
|
|
6134
|
+
console.log(nextInstallNudge());
|
|
6044
6135
|
});
|
|
6045
|
-
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) => {
|
|
6136
|
+
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) => {
|
|
6046
6137
|
const targetRoot = normalizeTargetRoot(options.targetRoot);
|
|
6047
|
-
const
|
|
6048
|
-
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
6049
|
-
const resolvedSource = resolvedInput.source;
|
|
6050
|
-
const driverName = options.driver ?? inferSourceDriverName(resolvedSource);
|
|
6051
|
-
const driver = getSourceDriver(driverName);
|
|
6052
|
-
const adapter = await resolveAdapter({
|
|
6053
|
-
adapter: options.adapter,
|
|
6054
|
-
adapterConfig: options.adapterConfig,
|
|
6055
|
-
adapterModule: options.adapterModule,
|
|
6056
|
-
allowAdapterCode: options.allowAdapterCode,
|
|
6057
|
-
baseDir: targetRoot,
|
|
6058
|
-
warn: (message) => console.warn(message)
|
|
6059
|
-
});
|
|
6060
|
-
const bundle = await stageSource(driver, resolvedSource, {
|
|
6061
|
-
workspaceRoot: targetRoot,
|
|
6062
|
-
adapter,
|
|
6063
|
-
cacheRoot: join27(targetRoot, ".agentwheel", "cache"),
|
|
6064
|
-
mode: options.mode,
|
|
6065
|
-
select: selectedArtifacts
|
|
6066
|
-
});
|
|
6067
|
-
const name = options.name ?? resolvedInput.registryEntry?.name ?? bundle.source.packageName ?? source;
|
|
6068
|
-
const entry = {
|
|
6069
|
-
name,
|
|
6070
|
-
source: resolvedSource,
|
|
6071
|
-
driver: driverName,
|
|
6072
|
-
adapter: adapter.name,
|
|
6073
|
-
adapterConfig: options.adapterConfig,
|
|
6074
|
-
adapterModule: options.adapterModule,
|
|
6075
|
-
adapterCodeHash: adapter.programmatic?.hash,
|
|
6076
|
-
mode: options.mode,
|
|
6077
|
-
requestedRef: bundle.source.requestedRef,
|
|
6078
|
-
select: selectedArtifacts
|
|
6079
|
-
};
|
|
6138
|
+
const entry = await packageEntryFromSource(source, targetRoot, options);
|
|
6080
6139
|
await writeWorkspaceConfig(targetRoot, upsertPackage(await readWorkspaceConfig(targetRoot), entry));
|
|
6081
|
-
|
|
6082
|
-
console.log(`Added ${name}.`);
|
|
6140
|
+
console.log(`Added ${entry.name}. Preview: agentwheel plan - Apply: agentwheel install`);
|
|
6083
6141
|
});
|
|
6084
|
-
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) => {
|
|
6142
|
+
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) => {
|
|
6085
6143
|
const targetRoot = normalizeTargetRoot(options.targetRoot);
|
|
6086
6144
|
const selectedArtifacts = selectedArtifactsFromOptions(options);
|
|
6087
6145
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
6088
6146
|
const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
|
|
6089
|
-
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot:
|
|
6147
|
+
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join28(targetRoot, ".agentwheel", "cache") }))));
|
|
6090
6148
|
const artifacts = filterArtifactsBySelection(await driver.list(resolved), selectedArtifacts);
|
|
6091
6149
|
for (const artifact of artifacts) {
|
|
6092
6150
|
console.log(`${artifact.type} ${artifact.name} ${artifact.relativePath}`);
|
|
6093
6151
|
}
|
|
6094
6152
|
});
|
|
6095
|
-
program.command("scan").argument("<source>", "package source").option("--driver <driver>", "source driver").option("--target-root <path>", "workspace root", process.cwd()).action(async (source, options) => {
|
|
6153
|
+
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) => {
|
|
6096
6154
|
const targetRoot = normalizeTargetRoot(options.targetRoot);
|
|
6097
6155
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
6098
6156
|
const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
|
|
6099
|
-
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot:
|
|
6157
|
+
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join28(targetRoot, ".agentwheel", "cache") }))));
|
|
6100
6158
|
const result = await driver.scan(resolved);
|
|
6101
6159
|
if (result.findings.length === 0) {
|
|
6102
6160
|
console.log("Scan ok: no findings");
|
|
@@ -6107,99 +6165,28 @@ program.command("scan").argument("<source>", "package source").option("--driver
|
|
|
6107
6165
|
}
|
|
6108
6166
|
if (!result.ok) process.exitCode = 1;
|
|
6109
6167
|
});
|
|
6110
|
-
program.command("plan").argument("[source]", "source
|
|
6111
|
-
|
|
6112
|
-
for (const target of targets) {
|
|
6113
|
-
if (source && options.noDeps && options.onlySource) {
|
|
6114
|
-
const { plan, bundle } = await buildPlan(source, target, options);
|
|
6115
|
-
console.log(formatPlan(plan));
|
|
6116
|
-
await rm9(bundle.root, { recursive: true, force: true });
|
|
6117
|
-
if (plan.hasBlockingChanges) process.exitCode = 1;
|
|
6118
|
-
continue;
|
|
6119
|
-
}
|
|
6120
|
-
for (const result of await buildGraphPlansForTarget(target, source, options, { useUpdateDecision: false })) {
|
|
6121
|
-
console.log(formatGraphPlan(result));
|
|
6122
|
-
await rm9(result.bundle.root, { recursive: true, force: true });
|
|
6123
|
-
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
6124
|
-
}
|
|
6125
|
-
}
|
|
6168
|
+
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("--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) => {
|
|
6169
|
+
await runInstallCommand(source, { ...options, dryRun: true }, { apply: false });
|
|
6126
6170
|
});
|
|
6127
|
-
program.command("
|
|
6128
|
-
|
|
6129
|
-
const target = await resolveRuntimeTarget({ targetRoot: options.targetRoot, adapter: options.adapter, agent: options.agent });
|
|
6130
|
-
const results = await syncProfile({
|
|
6131
|
-
workspaceRoot: target.workspaceRoot,
|
|
6132
|
-
profile: options.profile,
|
|
6133
|
-
source,
|
|
6134
|
-
driver: options.driver,
|
|
6135
|
-
mode: options.mode,
|
|
6136
|
-
select: selectedArtifactsFromOptions(options),
|
|
6137
|
-
dryRun: options.dryRun,
|
|
6138
|
-
executePlugins: options.executePlugins,
|
|
6139
|
-
allowAdapterCode: options.allowAdapterCode,
|
|
6140
|
-
noDeps: options.noDeps,
|
|
6141
|
-
frozenLock: options.frozenLock,
|
|
6142
|
-
offline: options.offline,
|
|
6143
|
-
yes: options.yes,
|
|
6144
|
-
trustPatterns: options.trust ?? [],
|
|
6145
|
-
isTTY: process.stdin.isTTY === true,
|
|
6146
|
-
warn: (message) => console.warn(message)
|
|
6147
|
-
});
|
|
6148
|
-
for (const result of results) {
|
|
6149
|
-
console.log(`Profile ${options.profile} / ${result.runtime} / ${result.packageName} at ${result.targetRoot} (${result.transport}):`);
|
|
6150
|
-
console.log(formatPlan(result.plan));
|
|
6151
|
-
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
6152
|
-
}
|
|
6153
|
-
if (!options.dryRun) console.log("Applied.");
|
|
6154
|
-
return;
|
|
6155
|
-
}
|
|
6156
|
-
const targets = await resolveCliTargets(options);
|
|
6157
|
-
if (!source) {
|
|
6158
|
-
for (const target of targets) {
|
|
6159
|
-
await runConfiguredGraphPackages(target, options, { useUpdateDecision: false });
|
|
6160
|
-
}
|
|
6161
|
-
return;
|
|
6162
|
-
}
|
|
6163
|
-
for (const target of targets) {
|
|
6164
|
-
if (options.noDeps && options.onlySource) {
|
|
6165
|
-
const { plan, bundle } = await buildPlan(source, target, options);
|
|
6166
|
-
console.log(formatPlan(plan));
|
|
6167
|
-
if (!options.dryRun) {
|
|
6168
|
-
await applyInstallPlan(plan, bundle.sourceLock, { executePlugins: options.executePlugins, transport: transportForTarget(target) });
|
|
6169
|
-
console.log(`Applied ${target.adapter} at ${target.targetRoot}.`);
|
|
6170
|
-
}
|
|
6171
|
-
await rm9(bundle.root, { recursive: true, force: true });
|
|
6172
|
-
if (plan.hasBlockingChanges) process.exitCode = 1;
|
|
6173
|
-
continue;
|
|
6174
|
-
}
|
|
6175
|
-
for (const result of await buildGraphPlansForTarget(target, source, options, { useUpdateDecision: false })) {
|
|
6176
|
-
console.log(formatGraphPlan(result));
|
|
6177
|
-
if (!options.dryRun) {
|
|
6178
|
-
await applyCombinedInstallPlan(result.plan, {
|
|
6179
|
-
executePlugins: options.executePlugins,
|
|
6180
|
-
transport: transportForTarget(target),
|
|
6181
|
-
graphLockDigest: result.graphLockDigest,
|
|
6182
|
-
graphLock: { path: result.graphLockPath, lock: result.bundle.graphLock }
|
|
6183
|
-
});
|
|
6184
|
-
console.log(`Applied ${result.plan.adapter} at ${result.plan.targetRoot}.`);
|
|
6185
|
-
}
|
|
6186
|
-
await rm9(result.bundle.root, { recursive: true, force: true });
|
|
6187
|
-
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
6188
|
-
}
|
|
6189
|
-
}
|
|
6171
|
+
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("--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) => {
|
|
6172
|
+
await runInstallCommand(source, options, { apply: !options.dryRun });
|
|
6190
6173
|
});
|
|
6191
|
-
program.command("
|
|
6174
|
+
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("--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) => {
|
|
6175
|
+
console.error("warning: 'agentwheel sync' is deprecated and will be removed in 0.10. Use 'agentwheel install'.");
|
|
6176
|
+
await runInstallCommand(source, options, { apply: !options.dryRun });
|
|
6177
|
+
});
|
|
6178
|
+
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) => {
|
|
6192
6179
|
const targets = await resolveCliTargets(options);
|
|
6193
6180
|
for (const target of targets) {
|
|
6194
|
-
await runConfiguredGraphPackages(target, options, {
|
|
6181
|
+
await runConfiguredGraphPackages(target, { ...options, scope: name }, { mode: "update" });
|
|
6195
6182
|
}
|
|
6196
6183
|
});
|
|
6197
6184
|
program.command("deps").description("inspect the OpenPack dependency graph").addCommand(
|
|
6198
|
-
new Command("tree").argument("[source]", "optional source
|
|
6185
|
+
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) => {
|
|
6199
6186
|
const targets = await resolveCliTargets(options);
|
|
6200
6187
|
for (const target of targets) {
|
|
6201
6188
|
if (source) {
|
|
6202
|
-
for (const result of await buildGraphPlansForTarget(target, source, options, {
|
|
6189
|
+
for (const result of await buildGraphPlansForTarget(target, source, options, { mode: "install" })) {
|
|
6203
6190
|
console.log(formatDependencyTree(result.graph).join("\n"));
|
|
6204
6191
|
for (const decision of result.bundle.graphLock.canonical.namespacing) {
|
|
6205
6192
|
console.log(`NAMESPACE ${decision.graphNodeId}:${decision.type}/${decision.name} -> ${decision.type}/${decision.installName} (${decision.reason})`);
|
|
@@ -6213,7 +6200,7 @@ program.command("deps").description("inspect the OpenPack dependency graph").add
|
|
|
6213
6200
|
}
|
|
6214
6201
|
})
|
|
6215
6202
|
).addCommand(
|
|
6216
|
-
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) => {
|
|
6203
|
+
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) => {
|
|
6217
6204
|
const targets = await resolveCliTargets(options);
|
|
6218
6205
|
for (const target of targets) {
|
|
6219
6206
|
const { lock, adapter } = await readTargetGraphLock(target, options);
|
|
@@ -6240,7 +6227,7 @@ program.command("registry").description("manage optional registry indexes").addC
|
|
|
6240
6227
|
})
|
|
6241
6228
|
);
|
|
6242
6229
|
program.command("trust").description("manage persisted source trust decisions").addCommand(
|
|
6243
|
-
new Command("forget").argument("<pattern>", "trusted source glob to revoke").option("--target-root <path>", "workspace root", process.cwd()).action(async (pattern, options) => {
|
|
6230
|
+
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) => {
|
|
6244
6231
|
const removed = await forgetTrustedSources(normalizeTargetRoot(options.targetRoot), pattern);
|
|
6245
6232
|
if (removed.length === 0) {
|
|
6246
6233
|
console.log(`No persisted trust matched ${pattern}.`);
|
|
@@ -6267,23 +6254,31 @@ program.command("package").description("validate and migrate OpenPack packages")
|
|
|
6267
6254
|
console.log(result.message);
|
|
6268
6255
|
})
|
|
6269
6256
|
);
|
|
6270
|
-
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) => {
|
|
6257
|
+
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) => {
|
|
6271
6258
|
const targetRoot = normalizeTargetRoot(options.targetRoot);
|
|
6272
6259
|
const result = await remember(targetRoot, options.runtime, text);
|
|
6273
|
-
console.log(`Remembered in ${result.overlayPath}
|
|
6260
|
+
console.log(`Remembered in ${result.overlayPath}.`);
|
|
6261
|
+
console.log(nextInstallNudge());
|
|
6274
6262
|
});
|
|
6275
|
-
program.command("eject").argument("<item>", "package/type/name").option("--target-root <path>", "workspace root", process.cwd()).action(async (item, options) => {
|
|
6263
|
+
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) => {
|
|
6276
6264
|
const targetRoot = normalizeTargetRoot(options.targetRoot);
|
|
6277
6265
|
const result = await ejectArtifact(targetRoot, item);
|
|
6278
6266
|
console.log(`Ejected ${item} to ${result.ejectedPath}.`);
|
|
6267
|
+
console.log(nextInstallNudge());
|
|
6279
6268
|
});
|
|
6280
|
-
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) => {
|
|
6269
|
+
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) => {
|
|
6270
|
+
if (options.keepFiles && options.force) {
|
|
6271
|
+
throw new Error("--keep-files cannot be combined with --force.");
|
|
6272
|
+
}
|
|
6281
6273
|
const targets = await resolveCliTargets(options);
|
|
6282
6274
|
for (const target of targets) {
|
|
6283
6275
|
if (packageName) {
|
|
6284
6276
|
await uninstallConfiguredPackage(target, packageName, options);
|
|
6285
6277
|
continue;
|
|
6286
6278
|
}
|
|
6279
|
+
if (options.keepFiles) {
|
|
6280
|
+
throw new Error("--keep-files requires a configured package name or source.");
|
|
6281
|
+
}
|
|
6287
6282
|
const adapter = await resolveAdapterForTarget(target, options);
|
|
6288
6283
|
const transport = transportForTarget(target);
|
|
6289
6284
|
const manifest = await readInstallManifest(target.targetRoot, adapter.name, transport);
|
|
@@ -6304,23 +6299,138 @@ program.command("uninstall").argument("[package]", "configured package name or s
|
|
|
6304
6299
|
if (plan.hasBlockingChanges) process.exitCode = 1;
|
|
6305
6300
|
}
|
|
6306
6301
|
});
|
|
6307
|
-
|
|
6308
|
-
const
|
|
6309
|
-
const
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6302
|
+
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) => {
|
|
6303
|
+
const targets = await resolveCliTargets(options);
|
|
6304
|
+
for (const target of targets) {
|
|
6305
|
+
await printStatus(target, options);
|
|
6306
|
+
}
|
|
6307
|
+
});
|
|
6308
|
+
async function runInstallCommand(nameOrSource, options, behavior) {
|
|
6309
|
+
if (options.profile) {
|
|
6310
|
+
const target = await resolveRuntimeTarget({ targetRoot: options.targetRoot, adapter: options.adapter, agent: options.agent });
|
|
6311
|
+
const results = await syncProfile({
|
|
6312
|
+
workspaceRoot: target.workspaceRoot,
|
|
6313
|
+
profile: options.profile,
|
|
6314
|
+
source: nameOrSource,
|
|
6315
|
+
driver: options.driver,
|
|
6316
|
+
mode: options.mode,
|
|
6317
|
+
select: selectedArtifactsFromOptions(options),
|
|
6318
|
+
dryRun: !behavior.apply,
|
|
6319
|
+
executePlugins: options.executePlugins,
|
|
6320
|
+
allowAdapterCode: options.allowAdapterCode,
|
|
6321
|
+
noDeps: noDepsFromOptions(options),
|
|
6322
|
+
lockedResolution: true,
|
|
6323
|
+
frozenLock: options.frozenLock,
|
|
6324
|
+
offline: options.offline,
|
|
6325
|
+
yes: options.yes,
|
|
6326
|
+
trustPatterns: options.trust ?? [],
|
|
6327
|
+
readOnly: !behavior.apply,
|
|
6328
|
+
isTTY: process.stdin.isTTY === true,
|
|
6329
|
+
warn: (message) => console.warn(message)
|
|
6330
|
+
});
|
|
6331
|
+
for (const result of results) {
|
|
6332
|
+
console.log(`Profile ${options.profile} / ${result.runtime} / ${result.packageName} at ${result.targetRoot} (${result.transport}):`);
|
|
6333
|
+
console.log(formatPlan(result.plan));
|
|
6334
|
+
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
6335
|
+
}
|
|
6336
|
+
if (behavior.apply) console.log("Applied.");
|
|
6337
|
+
return;
|
|
6338
|
+
}
|
|
6339
|
+
const targets = await resolveCliTargets(options);
|
|
6340
|
+
for (const target of targets) {
|
|
6341
|
+
const config = await readMergedWorkspaceConfig(target.workspaceRoot);
|
|
6342
|
+
const configured = nameOrSource ? findConfiguredPackage(config.packages, nameOrSource) : void 0;
|
|
6343
|
+
let source;
|
|
6344
|
+
let scope = configured?.name;
|
|
6345
|
+
let extraPackage;
|
|
6346
|
+
if (nameOrSource && !configured) {
|
|
6347
|
+
try {
|
|
6348
|
+
const entry = await packageEntryFromSource(nameOrSource, target.workspaceRoot, options);
|
|
6349
|
+
scope = entry.name;
|
|
6350
|
+
if (behavior.apply) {
|
|
6351
|
+
await writeWorkspaceConfig(target.workspaceRoot, upsertPackage(await readWorkspaceConfig(target.workspaceRoot), entry));
|
|
6352
|
+
} else {
|
|
6353
|
+
source = nameOrSource;
|
|
6354
|
+
extraPackage = entry;
|
|
6355
|
+
}
|
|
6356
|
+
} catch (error) {
|
|
6357
|
+
throw teachingInstallError(nameOrSource, error);
|
|
6358
|
+
}
|
|
6359
|
+
}
|
|
6360
|
+
for (const result of await buildGraphPlansForTarget(target, source, { ...options, scope, extraPackage }, { mode: "install" })) {
|
|
6361
|
+
console.log(formatGraphPlan(result));
|
|
6362
|
+
if (behavior.apply) {
|
|
6363
|
+
await applyCombinedInstallPlan(result.plan, {
|
|
6364
|
+
executePlugins: options.executePlugins,
|
|
6365
|
+
transport: transportForTarget(target),
|
|
6366
|
+
graphLockDigest: result.graphLockDigest,
|
|
6367
|
+
graphLock: { path: result.graphLockPath, lock: result.bundle.graphLock }
|
|
6368
|
+
});
|
|
6369
|
+
console.log(`Applied ${result.plan.adapter} at ${result.plan.targetRoot}.`);
|
|
6370
|
+
}
|
|
6371
|
+
await rm9(result.bundle.root, { recursive: true, force: true });
|
|
6372
|
+
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
6373
|
+
}
|
|
6374
|
+
}
|
|
6375
|
+
}
|
|
6376
|
+
async function packageEntryFromSource(source, targetRoot, options) {
|
|
6377
|
+
const selectedArtifacts = selectedArtifactsFromOptions(options);
|
|
6378
|
+
const lockMode = options.frozenLock === true || options.offline === true;
|
|
6379
|
+
const resolvedInput = await resolvePackageSource(source, targetRoot, { offline: lockMode });
|
|
6380
|
+
const resolvedSource = resolvedInput.source;
|
|
6381
|
+
const driverName = options.driver ?? inferSourceDriverName(resolvedSource);
|
|
6382
|
+
const driver = getSourceDriver(driverName);
|
|
6383
|
+
const adapter = await resolveAdapter({
|
|
6384
|
+
adapter: options.adapter ?? "openclaw",
|
|
6385
|
+
adapterConfig: options.adapterConfig,
|
|
6386
|
+
adapterModule: options.adapterModule,
|
|
6387
|
+
allowAdapterCode: options.allowAdapterCode,
|
|
6388
|
+
baseDir: targetRoot,
|
|
6389
|
+
warn: (message) => console.warn(message)
|
|
6390
|
+
});
|
|
6391
|
+
const bundle = await stageSource(driver, resolvedSource, {
|
|
6392
|
+
workspaceRoot: targetRoot,
|
|
6314
6393
|
adapter,
|
|
6315
|
-
|
|
6394
|
+
cacheRoot: join28(targetRoot, ".agentwheel", "cache"),
|
|
6316
6395
|
mode: options.mode,
|
|
6317
|
-
|
|
6318
|
-
|
|
6319
|
-
offline: options.offline,
|
|
6320
|
-
warn: (message) => console.warn(message),
|
|
6321
|
-
transport
|
|
6396
|
+
frozenLock: lockMode,
|
|
6397
|
+
select: selectedArtifacts
|
|
6322
6398
|
});
|
|
6323
|
-
|
|
6399
|
+
try {
|
|
6400
|
+
return {
|
|
6401
|
+
name: options.name ?? resolvedInput.registryEntry?.name ?? bundle.source.packageName ?? source,
|
|
6402
|
+
source: resolvedSource,
|
|
6403
|
+
driver: driverName,
|
|
6404
|
+
adapter: adapter.name,
|
|
6405
|
+
adapterConfig: options.adapterConfig,
|
|
6406
|
+
adapterModule: options.adapterModule,
|
|
6407
|
+
adapterCodeHash: adapter.programmatic?.hash,
|
|
6408
|
+
mode: options.mode ?? "pinned",
|
|
6409
|
+
requestedRef: bundle.source.requestedRef,
|
|
6410
|
+
select: selectedArtifacts
|
|
6411
|
+
};
|
|
6412
|
+
} finally {
|
|
6413
|
+
await rm9(bundle.root, { recursive: true, force: true });
|
|
6414
|
+
}
|
|
6415
|
+
}
|
|
6416
|
+
function findConfiguredPackage(packages, value) {
|
|
6417
|
+
return packages.find((pkg) => pkg.name === value || pkg.source === value);
|
|
6418
|
+
}
|
|
6419
|
+
function noDepsFromOptions(options) {
|
|
6420
|
+
return options.noDeps === true || options.deps === false;
|
|
6421
|
+
}
|
|
6422
|
+
function teachingInstallError(input, cause) {
|
|
6423
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
6424
|
+
return new Error(
|
|
6425
|
+
`'${input}' is not a configured package and could not be resolved as a source.
|
|
6426
|
+
To add and install a new package: agentwheel install <source> (e.g. github:org/pack)
|
|
6427
|
+
To see what's configured: agentwheel status
|
|
6428
|
+
|
|
6429
|
+
Resolver error: ${message}`
|
|
6430
|
+
);
|
|
6431
|
+
}
|
|
6432
|
+
function nextInstallNudge() {
|
|
6433
|
+
return "Preview: agentwheel plan - Apply: agentwheel install";
|
|
6324
6434
|
}
|
|
6325
6435
|
async function resolveCliTargets(options) {
|
|
6326
6436
|
if (options.all) {
|
|
@@ -6341,7 +6451,7 @@ async function resolveAdapterForTarget(target, options) {
|
|
|
6341
6451
|
async function runConfiguredGraphPackages(target, options, behavior) {
|
|
6342
6452
|
const results = await buildGraphPlansForTarget(target, void 0, options, behavior);
|
|
6343
6453
|
for (const result of results) {
|
|
6344
|
-
console.log(`${behavior.
|
|
6454
|
+
console.log(`${behavior.mode === "update" ? "Update" : "Install"} ${result.plan.adapter} at ${result.plan.targetRoot}:`);
|
|
6345
6455
|
console.log(formatGraphPlan(result));
|
|
6346
6456
|
if (!options.dryRun) {
|
|
6347
6457
|
await applyCombinedInstallPlan(result.plan, {
|
|
@@ -6356,10 +6466,13 @@ async function runConfiguredGraphPackages(target, options, behavior) {
|
|
|
6356
6466
|
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
6357
6467
|
}
|
|
6358
6468
|
}
|
|
6359
|
-
async function buildGraphPlansForTarget(target, source, options,
|
|
6469
|
+
async function buildGraphPlansForTarget(target, source, options, behavior) {
|
|
6360
6470
|
const config = await readMergedWorkspaceConfig(target.workspaceRoot);
|
|
6361
6471
|
const groups = /* @__PURE__ */ new Map();
|
|
6362
6472
|
const selectedArtifacts = selectedArtifactsFromOptions(options);
|
|
6473
|
+
const scopedPackage = options.scope ? findConfiguredPackage(config.packages, options.scope) : void 0;
|
|
6474
|
+
const scopedRootId = scopedPackage?.name ?? (source ? options.scope : void 0);
|
|
6475
|
+
if (options.scope && !scopedPackage && !source) throw new Error(`Configured package not found: ${options.scope}`);
|
|
6363
6476
|
if (!source || !options.onlySource) {
|
|
6364
6477
|
for (const pkg of config.packages) {
|
|
6365
6478
|
const group = graphGroupForPackage(groups, target, pkg, options);
|
|
@@ -6381,14 +6494,11 @@ async function buildGraphPlansForTarget(target, source, options, _behavior) {
|
|
|
6381
6494
|
allowAdapterCode: options.allowAdapterCode
|
|
6382
6495
|
},
|
|
6383
6496
|
packages: [],
|
|
6384
|
-
extraRoots: []
|
|
6497
|
+
extraRoots: [],
|
|
6498
|
+
extraPackages: []
|
|
6385
6499
|
};
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
source,
|
|
6389
|
-
mode: options.mode ?? "pinned",
|
|
6390
|
-
select: selectedArtifacts
|
|
6391
|
-
});
|
|
6500
|
+
const entry = options.extraPackage ?? await packageEntryFromSource(source, target.workspaceRoot, options);
|
|
6501
|
+
group.extraPackages.push(entry);
|
|
6392
6502
|
groups.set(key, group);
|
|
6393
6503
|
}
|
|
6394
6504
|
if (groups.size === 0) {
|
|
@@ -6398,36 +6508,160 @@ async function buildGraphPlansForTarget(target, source, options, _behavior) {
|
|
|
6398
6508
|
const results = [];
|
|
6399
6509
|
for (const group of groups.values()) {
|
|
6400
6510
|
const adapter = await resolveAdapterForTarget(group.target, group.adapterOptions);
|
|
6511
|
+
const transport = transportForTarget(group.target);
|
|
6512
|
+
const allPackages = [...group.packages, ...group.extraPackages];
|
|
6513
|
+
const groupHasScope = !scopedRootId || allPackages.some((pkg) => pkg.name === scopedRootId || pkg.source === options.scope);
|
|
6514
|
+
if (behavior.mode === "install" && scopedRootId && !groupHasScope) continue;
|
|
6515
|
+
const updateScope = behavior.mode === "update" ? scopedPackage ? /* @__PURE__ */ new Set([scopedPackage.name]) : void 0 : void 0;
|
|
6401
6516
|
const roots = [
|
|
6402
|
-
...
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6517
|
+
...allPackages.map((pkg) => {
|
|
6518
|
+
const updateThisPackage = behavior.mode === "update" && pkg.mode === "tracking" && (!updateScope || updateScope.has(pkg.name) || updateScope.has(pkg.source));
|
|
6519
|
+
return {
|
|
6520
|
+
rootId: pkg.name,
|
|
6521
|
+
source: pkg.source,
|
|
6522
|
+
mode: pkg.mode,
|
|
6523
|
+
ref: pkg.requestedRef,
|
|
6524
|
+
select: selectedArtifacts ?? normalizeArtifactSelectors(pkg.select, pkg.skills),
|
|
6525
|
+
aliases: pkg.aliases,
|
|
6526
|
+
useLock: behavior.mode === "install" ? true : !updateThisPackage
|
|
6527
|
+
};
|
|
6528
|
+
}),
|
|
6410
6529
|
...group.extraRoots
|
|
6411
6530
|
];
|
|
6531
|
+
if (behavior.mode === "update") {
|
|
6532
|
+
const changed = roots.filter((root) => root.useLock === false);
|
|
6533
|
+
if (changed.length === 0) {
|
|
6534
|
+
const label = options.scope ? ` ${options.scope}` : "";
|
|
6535
|
+
console.log(`No tracking packages to update${label}.`);
|
|
6536
|
+
continue;
|
|
6537
|
+
}
|
|
6538
|
+
}
|
|
6412
6539
|
if (roots.length === 0) continue;
|
|
6413
|
-
|
|
6540
|
+
const result = await createGraphSourcePlan({
|
|
6414
6541
|
roots,
|
|
6415
6542
|
targetRoot: group.target.targetRoot,
|
|
6416
6543
|
workspaceRoot: group.target.workspaceRoot,
|
|
6417
6544
|
adapter,
|
|
6418
|
-
transport
|
|
6545
|
+
transport,
|
|
6419
6546
|
targetKey: group.target.agentName ?? group.target.source,
|
|
6420
6547
|
targetFingerprintParts: targetFingerprintParts(group.target, adapter, group.adapterOptions),
|
|
6421
|
-
noDeps: options
|
|
6548
|
+
noDeps: noDepsFromOptions(options),
|
|
6549
|
+
lockedResolution: behavior.mode === "install",
|
|
6422
6550
|
frozenLock: options.frozenLock,
|
|
6423
6551
|
offline: options.offline,
|
|
6424
6552
|
yes: options.yes,
|
|
6425
6553
|
trustPatterns: options.trust ?? [],
|
|
6554
|
+
readOnly: options.dryRun === true,
|
|
6426
6555
|
isTTY: process.stdin.isTTY === true
|
|
6427
|
-
})
|
|
6556
|
+
});
|
|
6557
|
+
if (behavior.mode === "install" && scopedRootId) {
|
|
6558
|
+
const manifest = await readInstallManifest(group.target.targetRoot, adapter.name, transport);
|
|
6559
|
+
results.push(scopeInstallPlanToRoot(result, scopedRootId, manifest));
|
|
6560
|
+
} else {
|
|
6561
|
+
results.push(result);
|
|
6562
|
+
}
|
|
6428
6563
|
}
|
|
6429
6564
|
return results;
|
|
6430
6565
|
}
|
|
6566
|
+
function scopeInstallPlanToRoot(result, rootId, manifest) {
|
|
6567
|
+
const scopedOwners = scopedGraphOwnerKeys(result, rootId);
|
|
6568
|
+
const manifestByPath = new Map((manifest?.entries ?? []).map((entry) => [entry.path, entry]));
|
|
6569
|
+
const preservedPaths = /* @__PURE__ */ new Set();
|
|
6570
|
+
const plannedPaths = /* @__PURE__ */ new Set();
|
|
6571
|
+
const operations = [];
|
|
6572
|
+
for (const operation of result.plan.operations) {
|
|
6573
|
+
plannedPaths.add(operation.relativeDestPath);
|
|
6574
|
+
if (operationBelongsToScopedRoot(operation, scopedOwners)) {
|
|
6575
|
+
operations.push(operation);
|
|
6576
|
+
continue;
|
|
6577
|
+
}
|
|
6578
|
+
const entry = manifestByPath.get(operation.relativeDestPath);
|
|
6579
|
+
const transformed = transformOutOfScopeOperation(operation, entry, result.plan.targetRoot, rootId);
|
|
6580
|
+
for (const scopedOperation of transformed) {
|
|
6581
|
+
if (preservedPaths.has(scopedOperation.relativeDestPath)) continue;
|
|
6582
|
+
preservedPaths.add(scopedOperation.relativeDestPath);
|
|
6583
|
+
operations.push(scopedOperation);
|
|
6584
|
+
}
|
|
6585
|
+
}
|
|
6586
|
+
for (const entry of manifest?.entries ?? []) {
|
|
6587
|
+
if (plannedPaths.has(entry.path) || preservedPaths.has(entry.path) || entryBelongsToScopedRoot(entry, scopedOwners)) continue;
|
|
6588
|
+
preservedPaths.add(entry.path);
|
|
6589
|
+
operations.push(keepManifestEntryOperation(entry, result.plan.targetRoot, rootId));
|
|
6590
|
+
}
|
|
6591
|
+
return {
|
|
6592
|
+
...result,
|
|
6593
|
+
plan: {
|
|
6594
|
+
...result.plan,
|
|
6595
|
+
operations,
|
|
6596
|
+
hasBlockingChanges: operations.some((operation) => operation.action === "drift" || operation.action === "conflict")
|
|
6597
|
+
}
|
|
6598
|
+
};
|
|
6599
|
+
}
|
|
6600
|
+
function transformOutOfScopeOperation(operation, entry, targetRoot, rootId) {
|
|
6601
|
+
if (operation.action === "skip") return [operation];
|
|
6602
|
+
if (operation.action === "update" || operation.action === "drift") {
|
|
6603
|
+
return entry ? [keepManifestEntryOperation(entry, targetRoot, rootId, operation, { freshMetadata: true })] : [];
|
|
6604
|
+
}
|
|
6605
|
+
if (operation.action === "remove") {
|
|
6606
|
+
return entry ? [keepManifestEntryOperation(entry, targetRoot, rootId)] : [];
|
|
6607
|
+
}
|
|
6608
|
+
if (operation.action === "plugin" || operation.action === "program") {
|
|
6609
|
+
return entry ? [keepManifestEntryOperation(entry, targetRoot, rootId)] : [];
|
|
6610
|
+
}
|
|
6611
|
+
return [];
|
|
6612
|
+
}
|
|
6613
|
+
function scopedGraphOwnerKeys(result, rootId) {
|
|
6614
|
+
const root = result.graph.roots.find((candidate) => candidate.rootId === rootId);
|
|
6615
|
+
if (!root) throw new Error(`Resolved graph root not found for scoped install: ${rootId}`);
|
|
6616
|
+
const keys = /* @__PURE__ */ new Set([`workspace:${rootId}`]);
|
|
6617
|
+
const queue = [root.graphNodeId];
|
|
6618
|
+
while (queue.length > 0) {
|
|
6619
|
+
const nodeId = queue.shift();
|
|
6620
|
+
if (keys.has(nodeId)) continue;
|
|
6621
|
+
keys.add(nodeId);
|
|
6622
|
+
for (const edge of result.graph.edges) {
|
|
6623
|
+
if (edge.from === nodeId) queue.push(edge.to);
|
|
6624
|
+
}
|
|
6625
|
+
}
|
|
6626
|
+
return keys;
|
|
6627
|
+
}
|
|
6628
|
+
function operationBelongsToScopedRoot(operation, scopedOwners) {
|
|
6629
|
+
if (operation.graphNodeId && scopedOwners.has(operation.graphNodeId)) return true;
|
|
6630
|
+
return operation.owners?.some((owner) => scopedOwners.has(owner)) === true;
|
|
6631
|
+
}
|
|
6632
|
+
function entryBelongsToScopedRoot(entry, scopedOwners) {
|
|
6633
|
+
if ("graphNodeId" in entry && entry.graphNodeId && scopedOwners.has(entry.graphNodeId)) return true;
|
|
6634
|
+
const owners = "owners" in entry ? entry.owners : [entry.packageName ?? "legacy"];
|
|
6635
|
+
return owners.some((owner) => scopedOwners.has(owner));
|
|
6636
|
+
}
|
|
6637
|
+
function keepManifestEntryOperation(entry, targetRoot, rootId, operation, options = {}) {
|
|
6638
|
+
const owners = "owners" in entry ? entry.owners : [entry.packageName ?? "legacy"];
|
|
6639
|
+
const fresh = options.freshMetadata ? operation : void 0;
|
|
6640
|
+
return {
|
|
6641
|
+
action: "keep",
|
|
6642
|
+
artifactType: entry.artifactType,
|
|
6643
|
+
artifactName: entry.artifactName,
|
|
6644
|
+
kind: entry.kind,
|
|
6645
|
+
destPath: operation?.destPath ?? join28(targetRoot, entry.path),
|
|
6646
|
+
relativeDestPath: entry.path,
|
|
6647
|
+
desiredHash: entry.sourceHash,
|
|
6648
|
+
currentHash: operation?.currentHash ?? entry.hash,
|
|
6649
|
+
manifestHash: entry.hash,
|
|
6650
|
+
reason: `preserved outside scoped install ${rootId}`,
|
|
6651
|
+
channel: entry.channel,
|
|
6652
|
+
packageName: entry.packageName,
|
|
6653
|
+
semanticCommand: entry.semanticCommand,
|
|
6654
|
+
execute: entry.executed,
|
|
6655
|
+
mergeStrategy: entry.mergeStrategy,
|
|
6656
|
+
composedFrom: entry.composedFrom,
|
|
6657
|
+
installName: fresh?.installName ?? ("installName" in entry ? entry.installName : entry.artifactName),
|
|
6658
|
+
logicalSelector: fresh?.logicalSelector ?? ("logicalSelector" in entry ? entry.logicalSelector : `${entry.artifactType}/${entry.artifactName}`),
|
|
6659
|
+
graphNodeId: fresh?.graphNodeId ?? ("graphNodeId" in entry ? entry.graphNodeId : void 0),
|
|
6660
|
+
dependencyRole: fresh?.dependencyRole ?? ("dependencyRole" in entry ? entry.dependencyRole : "root"),
|
|
6661
|
+
owners: fresh?.owners ?? owners,
|
|
6662
|
+
graphLockDigest: fresh ? void 0 : "graphLockDigest" in entry ? entry.graphLockDigest : void 0
|
|
6663
|
+
};
|
|
6664
|
+
}
|
|
6431
6665
|
async function uninstallConfiguredPackage(target, packageName, options) {
|
|
6432
6666
|
const config = await readMergedWorkspaceConfig(target.workspaceRoot);
|
|
6433
6667
|
const removed = config.packages.filter((pkg) => pkg.name === packageName || pkg.source === packageName);
|
|
@@ -6476,10 +6710,12 @@ async function uninstallConfiguredPackage(target, packageName, options) {
|
|
|
6476
6710
|
transport,
|
|
6477
6711
|
targetKey: remainingGroup.target.agentName ?? remainingGroup.target.source,
|
|
6478
6712
|
targetFingerprintParts: targetFingerprintParts(remainingGroup.target, remainingAdapter, remainingGroup.adapterOptions),
|
|
6713
|
+
lockedResolution: true,
|
|
6479
6714
|
frozenLock: options.frozenLock,
|
|
6480
6715
|
offline: options.offline,
|
|
6481
6716
|
yes: options.yes,
|
|
6482
6717
|
trustPatterns: options.trust ?? [],
|
|
6718
|
+
readOnly: options.dryRun === true,
|
|
6483
6719
|
isTTY: process.stdin.isTTY === true
|
|
6484
6720
|
});
|
|
6485
6721
|
remainingGraphPlan = result2;
|
|
@@ -6500,6 +6736,7 @@ async function uninstallConfiguredPackage(target, packageName, options) {
|
|
|
6500
6736
|
const result = await uninstall(plan, {
|
|
6501
6737
|
dryRun: options.dryRun,
|
|
6502
6738
|
force: options.force,
|
|
6739
|
+
keepFiles: options.keepFiles,
|
|
6503
6740
|
transport,
|
|
6504
6741
|
...graphLockFinalState,
|
|
6505
6742
|
workspaceConfig: {
|
|
@@ -6528,7 +6765,8 @@ function graphGroupForPackage(groups, target, pkg, options) {
|
|
|
6528
6765
|
target: packageTarget,
|
|
6529
6766
|
adapterOptions,
|
|
6530
6767
|
packages: [],
|
|
6531
|
-
extraRoots: []
|
|
6768
|
+
extraRoots: [],
|
|
6769
|
+
extraPackages: []
|
|
6532
6770
|
};
|
|
6533
6771
|
groups.set(key, created);
|
|
6534
6772
|
return created;
|
|
@@ -6571,6 +6809,53 @@ async function readTargetGraphLock(target, options) {
|
|
|
6571
6809
|
}
|
|
6572
6810
|
return { adapter, path, lock: await readGraphLock(path) };
|
|
6573
6811
|
}
|
|
6812
|
+
async function printStatus(target, options) {
|
|
6813
|
+
const config = await readMergedWorkspaceConfig(target.workspaceRoot);
|
|
6814
|
+
const adapter = await resolveAdapterForTarget(target, options);
|
|
6815
|
+
const transport = transportForTarget(target);
|
|
6816
|
+
console.log(`Status for ${adapter.name} at ${target.targetRoot}`);
|
|
6817
|
+
if (config.packages.length === 0) {
|
|
6818
|
+
console.log(`Configured packages: none at ${target.workspaceRoot}`);
|
|
6819
|
+
return;
|
|
6820
|
+
}
|
|
6821
|
+
console.log("Configured packages:");
|
|
6822
|
+
for (const pkg of config.packages) {
|
|
6823
|
+
console.log(`- ${pkg.name} (${pkg.mode}) ${pkg.source}`);
|
|
6824
|
+
}
|
|
6825
|
+
const manifest = await readInstallManifest(target.targetRoot, adapter.name, transport);
|
|
6826
|
+
console.log(manifest ? `Install manifest: ${manifest.entries.length} entries, revision ${manifest.revision}` : "Install manifest: missing");
|
|
6827
|
+
try {
|
|
6828
|
+
const { path, lock } = await readTargetGraphLock(target, options);
|
|
6829
|
+
console.log(`Graph lock: ${path}`);
|
|
6830
|
+
console.log(`Locked graph: ${lock.canonical.roots.length} roots, ${lock.canonical.nodes.length} nodes, ${lock.canonical.artifacts.length} artifacts`);
|
|
6831
|
+
} catch {
|
|
6832
|
+
console.log("Graph lock: missing");
|
|
6833
|
+
}
|
|
6834
|
+
await printPendingInstallWork(target, options);
|
|
6835
|
+
}
|
|
6836
|
+
async function printPendingInstallWork(target, options) {
|
|
6837
|
+
let results = [];
|
|
6838
|
+
try {
|
|
6839
|
+
results = await buildGraphPlansForTarget(target, void 0, { ...options, dryRun: true }, { mode: "install" });
|
|
6840
|
+
const operations = results.flatMap((result) => result.plan.operations);
|
|
6841
|
+
const pending = operations.filter((operation) => operation.action !== "skip");
|
|
6842
|
+
if (pending.length === 0) {
|
|
6843
|
+
console.log("Pending install work: none");
|
|
6844
|
+
return;
|
|
6845
|
+
}
|
|
6846
|
+
const counts = [...pending.reduce((map, operation) => {
|
|
6847
|
+
map.set(operation.action, (map.get(operation.action) ?? 0) + 1);
|
|
6848
|
+
return map;
|
|
6849
|
+
}, /* @__PURE__ */ new Map())].map(([action, count]) => `${action}=${count}`).join(", ");
|
|
6850
|
+
const blocking = pending.filter((operation) => operation.action === "conflict" || operation.action === "drift").length;
|
|
6851
|
+
console.log(`Pending install work: ${pending.length} operations (${counts}${blocking ? `; blocking=${blocking}` : ""})`);
|
|
6852
|
+
} catch (error) {
|
|
6853
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6854
|
+
console.log(`Pending install work: unavailable (${message})`);
|
|
6855
|
+
} finally {
|
|
6856
|
+
await Promise.all(results.map((result) => rm9(result.bundle.root, { recursive: true, force: true })));
|
|
6857
|
+
}
|
|
6858
|
+
}
|
|
6574
6859
|
function collectSelectOption(value, previous) {
|
|
6575
6860
|
return [...previous, ...splitSelectorList(value)];
|
|
6576
6861
|
}
|
|
@@ -6608,10 +6893,10 @@ function filterUninstallPlanBySelection(plan, selected) {
|
|
|
6608
6893
|
};
|
|
6609
6894
|
}
|
|
6610
6895
|
async function initPackage(root) {
|
|
6611
|
-
await mkdir14(
|
|
6612
|
-
await mkdir14(
|
|
6613
|
-
await mkdir14(
|
|
6614
|
-
const manifestPath =
|
|
6896
|
+
await mkdir14(join28(root, "instructions"), { recursive: true });
|
|
6897
|
+
await mkdir14(join28(root, "rules"), { recursive: true });
|
|
6898
|
+
await mkdir14(join28(root, "skills"), { recursive: true });
|
|
6899
|
+
const manifestPath = join28(root, "openpack.json");
|
|
6615
6900
|
const manifest = {
|
|
6616
6901
|
schemaVersion: 2,
|
|
6617
6902
|
name: "example/agentwheel-package",
|
|
@@ -6624,10 +6909,10 @@ async function initPackage(root) {
|
|
|
6624
6909
|
};
|
|
6625
6910
|
await writeFile12(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
6626
6911
|
`, "utf8");
|
|
6627
|
-
await writeFile12(
|
|
6912
|
+
await writeFile12(join28(root, "instructions", "AGENTS.md"), "# Agent Instructions\n", "utf8");
|
|
6628
6913
|
}
|
|
6629
6914
|
async function defaultBootstrapPackage(_root) {
|
|
6630
|
-
const packageRoot = await findAgentwheelPackageRoot(
|
|
6915
|
+
const packageRoot = await findAgentwheelPackageRoot(dirname21(fileURLToPath3(import.meta.url)));
|
|
6631
6916
|
if (!packageRoot) return void 0;
|
|
6632
6917
|
return {
|
|
6633
6918
|
name: "agentwheel",
|
|
@@ -6673,7 +6958,7 @@ async function findAgentwheelPackageRoot(start) {
|
|
|
6673
6958
|
let current = resolve17(start);
|
|
6674
6959
|
while (true) {
|
|
6675
6960
|
if (await findPackageManifestPath(current, { warnLegacy: false })) return current;
|
|
6676
|
-
const parent =
|
|
6961
|
+
const parent = dirname21(current);
|
|
6677
6962
|
if (parent === current) return void 0;
|
|
6678
6963
|
current = parent;
|
|
6679
6964
|
}
|
|
@@ -6696,7 +6981,7 @@ function printRegistryEntries(entries) {
|
|
|
6696
6981
|
}
|
|
6697
6982
|
async function main() {
|
|
6698
6983
|
await maybeCheckForUpdate({
|
|
6699
|
-
currentVersion:
|
|
6984
|
+
currentVersion: CLI_VERSION,
|
|
6700
6985
|
argv: process.argv,
|
|
6701
6986
|
env: process.env,
|
|
6702
6987
|
isTTY: process.stderr.isTTY === true
|