agentwheel 0.14.4 → 0.14.6
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/AGENT.md +95 -0
- package/README.md +113 -7
- package/dist/{chunk-B3FMBTWC.js → chunk-QJTTISLY.js} +3 -1
- package/dist/{identify-TXIDGMNL.js → identify-T4RE5RBD.js} +1 -1
- package/dist/index.js +1242 -343
- package/install.md +113 -0
- package/llms.txt +27 -0
- package/openpack.json +1 -1
- package/package.json +4 -1
- package/skills/agentwheel/SKILL.md +13 -0
package/dist/index.js
CHANGED
|
@@ -6,12 +6,13 @@ import {
|
|
|
6
6
|
isIgnoredGeneratedEntry,
|
|
7
7
|
pathExists,
|
|
8
8
|
writeJsonAtomic
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-QJTTISLY.js";
|
|
10
10
|
|
|
11
11
|
// src/cli/index.ts
|
|
12
|
-
import {
|
|
12
|
+
import { existsSync } from "fs";
|
|
13
|
+
import { mkdir as mkdir20, rm as rm11, writeFile as writeFile19 } from "fs/promises";
|
|
13
14
|
import { homedir as homedir9 } from "os";
|
|
14
|
-
import { dirname as
|
|
15
|
+
import { dirname as dirname28, join as join40, resolve as resolve20 } from "path";
|
|
15
16
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
16
17
|
import { Command } from "commander";
|
|
17
18
|
|
|
@@ -60,6 +61,18 @@ var packageItemRequireSchema = z.union([
|
|
|
60
61
|
z.string().min(1),
|
|
61
62
|
packageItemRequireObjectSchema
|
|
62
63
|
]);
|
|
64
|
+
var packageItemSuggestObjectSchema = z.object({
|
|
65
|
+
alias: z.string().min(1),
|
|
66
|
+
select: z.array(z.string().min(1)).optional(),
|
|
67
|
+
optional: z.boolean().optional(),
|
|
68
|
+
runtimes: z.array(z.string().min(1)).optional(),
|
|
69
|
+
reason: z.string().min(1).optional(),
|
|
70
|
+
when: z.string().min(1).optional()
|
|
71
|
+
}).passthrough();
|
|
72
|
+
var packageItemSuggestSchema = z.union([
|
|
73
|
+
z.string().min(1),
|
|
74
|
+
packageItemSuggestObjectSchema
|
|
75
|
+
]);
|
|
63
76
|
var composedFromEntrySchema = z.object({
|
|
64
77
|
selector: z.string().min(1),
|
|
65
78
|
hash: z.string().min(16)
|
|
@@ -78,6 +91,7 @@ var artifactSchema = z.object({
|
|
|
78
91
|
assets: z.array(packageAssetSchema).optional(),
|
|
79
92
|
required: z.boolean().optional(),
|
|
80
93
|
requires: z.array(packageItemRequireSchema).optional(),
|
|
94
|
+
suggests: z.array(packageItemSuggestSchema).optional(),
|
|
81
95
|
compose: z.array(packageComposeEntrySchema).optional(),
|
|
82
96
|
runtimes: z.array(z.string().min(1)).optional(),
|
|
83
97
|
composedFrom: z.array(composedFromEntrySchema).optional()
|
|
@@ -102,7 +116,7 @@ var targetMappingSchema = z2.object({
|
|
|
102
116
|
"copilot-prompt",
|
|
103
117
|
"copilot-agent"
|
|
104
118
|
]).optional(),
|
|
105
|
-
merge: z2.enum(["json-deep", "yaml-deep", "codex-toml-mcp"]).optional(),
|
|
119
|
+
merge: z2.enum(["json-deep", "openclaw-json-deep", "yaml-deep", "codex-toml-mcp"]).optional(),
|
|
106
120
|
mode: z2.enum(["managed-block"]).optional()
|
|
107
121
|
});
|
|
108
122
|
var targetRegistrySchema = z2.record(installationTypeSchema, targetMappingSchema);
|
|
@@ -363,13 +377,13 @@ var openClawAdapter = {
|
|
|
363
377
|
user: { enabled: true, root: "home", dest: ".openclaw/skills" }
|
|
364
378
|
},
|
|
365
379
|
mcp: {
|
|
366
|
-
user: { enabled: true, root: "home", dest: ".openclaw/openclaw.json", merge: "json-deep" }
|
|
380
|
+
user: { enabled: true, root: "home", dest: ".openclaw/openclaw.json", merge: "openclaw-json-deep" }
|
|
367
381
|
},
|
|
368
382
|
settings: {
|
|
369
|
-
user: { enabled: true, root: "home", dest: ".openclaw/openclaw.json", merge: "json-deep" }
|
|
383
|
+
user: { enabled: true, root: "home", dest: ".openclaw/openclaw.json", merge: "openclaw-json-deep" }
|
|
370
384
|
},
|
|
371
385
|
plugins: {
|
|
372
|
-
local: { enabled: true, dest: ".openclaw/plugins", formats: ["openclaw-plugin"], semantic: "openclaw-plugin" }
|
|
386
|
+
local: { enabled: true, dest: ".openclaw/plugins", formats: ["openclaw-plugin", "openclaw-clawhub-plugin"], semantic: "openclaw-plugin" }
|
|
373
387
|
}
|
|
374
388
|
}
|
|
375
389
|
};
|
|
@@ -453,7 +467,7 @@ async function resolveAdapter(options) {
|
|
|
453
467
|
|
|
454
468
|
// src/install/apply.ts
|
|
455
469
|
import { execFile as execFile3 } from "child_process";
|
|
456
|
-
import { mkdtemp as mkdtemp2, rm as rm4, writeFile as
|
|
470
|
+
import { mkdtemp as mkdtemp2, rm as rm4, writeFile as writeFile9 } from "fs/promises";
|
|
457
471
|
import { tmpdir as tmpdir3 } from "os";
|
|
458
472
|
import { basename as basename3, join as join5 } from "path";
|
|
459
473
|
import { promisify as promisify3 } from "util";
|
|
@@ -759,13 +773,13 @@ async function spawnWithInput(command, args, input) {
|
|
|
759
773
|
if (result.stderr) throw new Error(result.stderr);
|
|
760
774
|
}
|
|
761
775
|
function waitForProcess(child, label) {
|
|
762
|
-
return new Promise((
|
|
776
|
+
return new Promise((resolve21, reject) => {
|
|
763
777
|
const stderr = [];
|
|
764
778
|
child.stderr?.on("data", (chunk) => stderr.push(chunk));
|
|
765
779
|
child.on("error", reject);
|
|
766
780
|
child.on("close", (code) => {
|
|
767
781
|
const message = Buffer.concat(stderr).toString("utf8");
|
|
768
|
-
if (code === 0)
|
|
782
|
+
if (code === 0) resolve21({ stderr: "" });
|
|
769
783
|
else reject(new Error(`${label} exited ${code}${message ? `: ${message}` : ""}`));
|
|
770
784
|
});
|
|
771
785
|
});
|
|
@@ -869,6 +883,58 @@ function dedupeArray(values) {
|
|
|
869
883
|
return out;
|
|
870
884
|
}
|
|
871
885
|
|
|
886
|
+
// src/install/openclaw-json-merge.ts
|
|
887
|
+
import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
|
|
888
|
+
import { dirname as dirname5 } from "path";
|
|
889
|
+
async function mergeOpenClawJsonFile(sourcePath, destPath) {
|
|
890
|
+
const source = expandEnvPlaceholders(
|
|
891
|
+
normalizeOpenClawConfig(JSON.parse(await readFile6(sourcePath, "utf8"))),
|
|
892
|
+
sourcePath
|
|
893
|
+
);
|
|
894
|
+
const current = await pathExists(destPath) ? JSON.parse(await readFile6(destPath, "utf8")) : {};
|
|
895
|
+
const merged = deepMerge(current, source);
|
|
896
|
+
await mkdir4(dirname5(destPath), { recursive: true });
|
|
897
|
+
await writeFile5(destPath, `${JSON.stringify(merged, null, 2)}
|
|
898
|
+
`, "utf8");
|
|
899
|
+
}
|
|
900
|
+
function expandEnvPlaceholders(value, sourcePath) {
|
|
901
|
+
if (typeof value === "string") {
|
|
902
|
+
return value.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_match, name) => {
|
|
903
|
+
const replacement = process.env[name];
|
|
904
|
+
if (replacement === void 0) {
|
|
905
|
+
throw new Error(`Missing environment variable ${name} while rendering OpenClaw JSON merge artifact ${sourcePath}`);
|
|
906
|
+
}
|
|
907
|
+
return replacement;
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
if (Array.isArray(value)) return value.map((item) => expandEnvPlaceholders(item, sourcePath));
|
|
911
|
+
if (!isRecord(value)) return value;
|
|
912
|
+
return Object.fromEntries(
|
|
913
|
+
Object.entries(value).map(([key, child]) => [key, expandEnvPlaceholders(child, sourcePath)])
|
|
914
|
+
);
|
|
915
|
+
}
|
|
916
|
+
function normalizeOpenClawConfig(value) {
|
|
917
|
+
if (!isRecord(value)) return value;
|
|
918
|
+
const rootMcpServers = isRecord(value.mcpServers) ? value.mcpServers : void 0;
|
|
919
|
+
if (!rootMcpServers) return value;
|
|
920
|
+
const out = { ...value };
|
|
921
|
+
const normalizedServers = {};
|
|
922
|
+
for (const [name, server] of Object.entries(rootMcpServers)) {
|
|
923
|
+
if (isRecord(server)) normalizedServers[name] = normalizeOpenClawMcpServer(server);
|
|
924
|
+
}
|
|
925
|
+
const mcp = isRecord(out.mcp) ? out.mcp : {};
|
|
926
|
+
out.mcp = deepMerge(mcp, { servers: normalizedServers });
|
|
927
|
+
delete out.mcpServers;
|
|
928
|
+
return out;
|
|
929
|
+
}
|
|
930
|
+
function normalizeOpenClawMcpServer(server) {
|
|
931
|
+
const out = { ...server };
|
|
932
|
+
const type = typeof out.type === "string" ? out.type : void 0;
|
|
933
|
+
if (type && typeof out.transport !== "string") out.transport = type;
|
|
934
|
+
delete out.type;
|
|
935
|
+
return out;
|
|
936
|
+
}
|
|
937
|
+
|
|
872
938
|
// src/install/manifest.ts
|
|
873
939
|
import { createHash as createHash2 } from "crypto";
|
|
874
940
|
import { resolve as resolve3 } from "path";
|
|
@@ -900,7 +966,7 @@ var manifestEntryV1Schema = z4.object({
|
|
|
900
966
|
semanticCommand: z4.array(z4.string()).optional(),
|
|
901
967
|
semanticPlugin: semanticPluginSpecSchema.optional(),
|
|
902
968
|
executed: z4.boolean().optional(),
|
|
903
|
-
mergeStrategy: z4.enum(["json-deep", "yaml-deep", "codex-toml-mcp"]).optional(),
|
|
969
|
+
mergeStrategy: z4.enum(["json-deep", "openclaw-json-deep", "yaml-deep", "codex-toml-mcp"]).optional(),
|
|
904
970
|
mode: z4.enum(["managed-block"]).optional(),
|
|
905
971
|
composedFrom: z4.array(composedFromEntrySchema).optional()
|
|
906
972
|
});
|
|
@@ -1092,8 +1158,8 @@ function assertOperationContained(operation, targetRoot) {
|
|
|
1092
1158
|
}
|
|
1093
1159
|
|
|
1094
1160
|
// src/install/transaction.ts
|
|
1095
|
-
import { cp, mkdir as
|
|
1096
|
-
import { dirname as
|
|
1161
|
+
import { cp, mkdir as mkdir5, rm as rm2, stat as stat2 } from "fs/promises";
|
|
1162
|
+
import { dirname as dirname6, join as join3 } from "path";
|
|
1097
1163
|
function applyLockPath(targetRoot, adapter, scope = {}) {
|
|
1098
1164
|
return join3(metadataDir(targetRoot), `${stateKeyFor(adapter, scope)}.apply-lock`);
|
|
1099
1165
|
}
|
|
@@ -1158,7 +1224,7 @@ async function recordBackup(operation, index, targetRoot, adapter, transport = l
|
|
|
1158
1224
|
}
|
|
1159
1225
|
const backupPath = join3(applyBackupDir(targetRoot, adapter, scope), String(index));
|
|
1160
1226
|
await rm2(backupPath, { recursive: true, force: true });
|
|
1161
|
-
await
|
|
1227
|
+
await mkdir5(dirname6(backupPath), { recursive: true });
|
|
1162
1228
|
await cp(operation.destPath, backupPath, { recursive: operation.kind === "dir", dereference: true });
|
|
1163
1229
|
return {
|
|
1164
1230
|
index,
|
|
@@ -1212,16 +1278,16 @@ async function localPathExists(path) {
|
|
|
1212
1278
|
}
|
|
1213
1279
|
|
|
1214
1280
|
// src/install/toml-merge.ts
|
|
1215
|
-
import { mkdir as
|
|
1216
|
-
import { dirname as
|
|
1281
|
+
import { mkdir as mkdir6, readFile as readFile7, writeFile as writeFile6 } from "fs/promises";
|
|
1282
|
+
import { dirname as dirname7 } from "path";
|
|
1217
1283
|
async function mergeCodexTomlMcp(sourcePath, destPath) {
|
|
1218
|
-
const source = JSON.parse(await
|
|
1284
|
+
const source = JSON.parse(await readFile7(sourcePath, "utf8"));
|
|
1219
1285
|
const servers = extractMcpServers(source);
|
|
1220
|
-
const current = await pathExists(destPath) ? await
|
|
1286
|
+
const current = await pathExists(destPath) ? await readFile7(destPath, "utf8") : "";
|
|
1221
1287
|
const withoutManaged = removeManagedMcpSections(current, Object.keys(servers));
|
|
1222
1288
|
const merged = appendMcpServers(withoutManaged, servers);
|
|
1223
|
-
await
|
|
1224
|
-
await
|
|
1289
|
+
await mkdir6(dirname7(destPath), { recursive: true });
|
|
1290
|
+
await writeFile6(destPath, merged, "utf8");
|
|
1225
1291
|
}
|
|
1226
1292
|
function extractMcpServers(source) {
|
|
1227
1293
|
const raw = isRecord2(source.mcpServers) ? source.mcpServers : source;
|
|
@@ -1300,15 +1366,15 @@ function isRecord2(value) {
|
|
|
1300
1366
|
}
|
|
1301
1367
|
|
|
1302
1368
|
// src/install/yaml-merge.ts
|
|
1303
|
-
import { mkdir as
|
|
1304
|
-
import { dirname as
|
|
1369
|
+
import { mkdir as mkdir7, readFile as readFile8, writeFile as writeFile7 } from "fs/promises";
|
|
1370
|
+
import { dirname as dirname8 } from "path";
|
|
1305
1371
|
import { parse as parse2, stringify } from "yaml";
|
|
1306
1372
|
async function mergeYamlFile(sourcePath, destPath) {
|
|
1307
|
-
const source = parseYamlValue(await
|
|
1308
|
-
const current = await pathExists(destPath) ? parseYamlValue(await
|
|
1373
|
+
const source = parseYamlValue(await readFile8(sourcePath, "utf8"));
|
|
1374
|
+
const current = await pathExists(destPath) ? parseYamlValue(await readFile8(destPath, "utf8")) : {};
|
|
1309
1375
|
const merged = deepMerge2(current, source);
|
|
1310
|
-
await
|
|
1311
|
-
await
|
|
1376
|
+
await mkdir7(dirname8(destPath), { recursive: true });
|
|
1377
|
+
await writeFile7(destPath, stringify(merged), "utf8");
|
|
1312
1378
|
}
|
|
1313
1379
|
function parseYamlValue(content) {
|
|
1314
1380
|
return normalizeYamlValue(parse2(content));
|
|
@@ -1369,13 +1435,13 @@ function normalizeOwners(owners) {
|
|
|
1369
1435
|
|
|
1370
1436
|
// src/install/instructions-block.ts
|
|
1371
1437
|
import { createHash as createHash3 } from "crypto";
|
|
1372
|
-
import { mkdtemp, readFile as
|
|
1438
|
+
import { mkdtemp, readFile as readFile9, realpath, rm as rm3, writeFile as writeFile8 } from "fs/promises";
|
|
1373
1439
|
import { tmpdir as tmpdir2 } from "os";
|
|
1374
|
-
import { basename as basename2, dirname as
|
|
1440
|
+
import { basename as basename2, dirname as dirname9, join as join4, relative as relative2 } from "path";
|
|
1375
1441
|
var managedInstructionBlockMode = "managed-block";
|
|
1376
1442
|
var managedInstructionBanner = "<!-- agentwheel-managed: edit fragments, not this block -->";
|
|
1377
1443
|
async function desiredManagedInstructionBlockHash(sourcePath) {
|
|
1378
|
-
const source = await
|
|
1444
|
+
const source = await readFile9(sourcePath, "utf8");
|
|
1379
1445
|
return hashText(managedBlockBody(source));
|
|
1380
1446
|
}
|
|
1381
1447
|
async function readManagedInstructionBlockState(destPath, selector, transport) {
|
|
@@ -1394,18 +1460,18 @@ async function readManagedInstructionBlockState(destPath, selector, transport) {
|
|
|
1394
1460
|
drifted: hash !== block.markerHash
|
|
1395
1461
|
};
|
|
1396
1462
|
}
|
|
1397
|
-
async function writeManagedInstructionBlock(sourcePath, destPath, selector, transport,
|
|
1398
|
-
const source = await
|
|
1463
|
+
async function writeManagedInstructionBlock(sourcePath, destPath, selector, transport, options = {}) {
|
|
1464
|
+
const source = await readFile9(sourcePath, "utf8");
|
|
1399
1465
|
const desired = renderManagedInstructionBlock(selector, source);
|
|
1400
1466
|
const existing = await readOptionalText(destPath, transport);
|
|
1401
|
-
const merged = upsertManagedInstructionBlock(existing ?? "", selector, desired.block,
|
|
1467
|
+
const merged = upsertManagedInstructionBlock(existing ?? "", selector, desired.block, options);
|
|
1402
1468
|
await writeTextWithTransport(destPath, merged, transport);
|
|
1403
1469
|
return desired.hash;
|
|
1404
1470
|
}
|
|
1405
|
-
async function removeManagedInstructionBlock(destPath, selector, transport,
|
|
1471
|
+
async function removeManagedInstructionBlock(destPath, selector, transport, options = {}) {
|
|
1406
1472
|
if (!await transport.pathExists(destPath)) return;
|
|
1407
1473
|
const existing = await transport.readFile(destPath);
|
|
1408
|
-
const updated = removeManagedBlockFromContent(existing, selector,
|
|
1474
|
+
const updated = removeManagedBlockFromContent(existing, selector, options);
|
|
1409
1475
|
await writeTextWithTransport(destPath, updated, transport);
|
|
1410
1476
|
}
|
|
1411
1477
|
async function managedInstructionBlockLanded(destPath, selector, expectedHash, transport) {
|
|
@@ -1438,24 +1504,30 @@ ${body}<!-- END openpack:include ${selector} -->
|
|
|
1438
1504
|
hash
|
|
1439
1505
|
};
|
|
1440
1506
|
}
|
|
1441
|
-
function upsertManagedInstructionBlock(content, selector, block,
|
|
1507
|
+
function upsertManagedInstructionBlock(content, selector, block, options) {
|
|
1508
|
+
const { expectedHash, allowDrift = false } = options;
|
|
1442
1509
|
const existing = findManagedInstructionBlock(content, selector);
|
|
1443
1510
|
if (!existing) {
|
|
1444
1511
|
if (expectedHash) throw new Error(`Managed instruction block missing for ${selector}`);
|
|
1445
1512
|
return appendManagedInstructionBlock(content, block);
|
|
1446
1513
|
}
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1514
|
+
if (!allowDrift) {
|
|
1515
|
+
assertCleanBlock(existing, selector);
|
|
1516
|
+
if (expectedHash && hashText(existing.body) !== expectedHash) {
|
|
1517
|
+
throw new Error(`Managed instruction block drift detected for ${selector}`);
|
|
1518
|
+
}
|
|
1450
1519
|
}
|
|
1451
1520
|
return `${content.slice(0, existing.start)}${block}${content.slice(existing.end)}`;
|
|
1452
1521
|
}
|
|
1453
|
-
function removeManagedBlockFromContent(content, selector,
|
|
1522
|
+
function removeManagedBlockFromContent(content, selector, options) {
|
|
1523
|
+
const { expectedHash, allowDrift = false } = options;
|
|
1454
1524
|
const existing = findManagedInstructionBlock(content, selector);
|
|
1455
1525
|
if (!existing) return content;
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1526
|
+
if (!allowDrift) {
|
|
1527
|
+
assertCleanBlock(existing, selector);
|
|
1528
|
+
if (expectedHash && hashText(existing.body) !== expectedHash) {
|
|
1529
|
+
throw new Error(`Managed instruction block drift detected for ${selector}`);
|
|
1530
|
+
}
|
|
1459
1531
|
}
|
|
1460
1532
|
return `${content.slice(0, existing.start)}${content.slice(existing.end)}`;
|
|
1461
1533
|
}
|
|
@@ -1501,7 +1573,7 @@ function assertCleanBlock(block, selector) {
|
|
|
1501
1573
|
}
|
|
1502
1574
|
}
|
|
1503
1575
|
function referencesAgentsMd(content, claudePath, agentsPath) {
|
|
1504
|
-
const claudeDir =
|
|
1576
|
+
const claudeDir = dirname9(claudePath);
|
|
1505
1577
|
for (const rawLine of content.split(/\r?\n/)) {
|
|
1506
1578
|
const line = rawLine.trim();
|
|
1507
1579
|
const atImport = /^@import\s+(.+)$/i.exec(line);
|
|
@@ -1511,7 +1583,7 @@ function referencesAgentsMd(content, claudePath, agentsPath) {
|
|
|
1511
1583
|
const cleaned = referenced.trim().replace(/^["']|["']$/g, "");
|
|
1512
1584
|
if (!/AGENTS\.md$/i.test(cleaned)) continue;
|
|
1513
1585
|
const candidate = cleaned.startsWith("/") ? cleaned : join4(claudeDir, cleaned);
|
|
1514
|
-
if (relative2(
|
|
1586
|
+
if (relative2(dirname9(agentsPath), candidate).replaceAll("\\", "/") === "AGENTS.md") return true;
|
|
1515
1587
|
if (candidate === agentsPath) return true;
|
|
1516
1588
|
}
|
|
1517
1589
|
return false;
|
|
@@ -1533,7 +1605,7 @@ async function writeTextWithTransport(path, content, transport) {
|
|
|
1533
1605
|
const tempRoot = await mkdtemp(join4(tmpdir2(), "agentwheel-instructions-"));
|
|
1534
1606
|
const localPath = join4(tempRoot, basename2(path) || "instructions.md");
|
|
1535
1607
|
try {
|
|
1536
|
-
await
|
|
1608
|
+
await writeFile8(localPath, content, "utf8");
|
|
1537
1609
|
await transport.atomicCopy(localPath, path, "file");
|
|
1538
1610
|
} finally {
|
|
1539
1611
|
await rm3(tempRoot, { recursive: true, force: true });
|
|
@@ -1677,7 +1749,7 @@ async function uninstall(plan, options = {}) {
|
|
|
1677
1749
|
const blockers = plan.operations.filter((operation) => operation.action === "conflict");
|
|
1678
1750
|
throw new Error(`Refusing to uninstall with blocking changes: ${blockers.map((item) => item.relativeDestPath).join(", ")}`);
|
|
1679
1751
|
}
|
|
1680
|
-
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);
|
|
1752
|
+
const removable = plan.operations.filter((operation) => operation.action === "remove" || resolvedOptions.force && isForceRemovableKeep(operation)).map((operation) => operation.action === "keep" ? { ...operation, action: "remove", overrideDrift: true, reason: `${operation.reason}; force removing drifted managed file` } : operation);
|
|
1681
1753
|
const kept = plan.operations.filter((operation) => operation.action === "keep" && (!resolvedOptions.force || !isForceRemovableKeep(operation)));
|
|
1682
1754
|
const skipped = plan.operations.filter((operation) => operation.action === "skip");
|
|
1683
1755
|
const removedDrifted = resolvedOptions.force ? plan.operations.filter((operation) => operation.action === "keep" && isForceRemovableKeep(operation)).length : 0;
|
|
@@ -1822,7 +1894,7 @@ async function applyOperation(operation, context) {
|
|
|
1822
1894
|
}
|
|
1823
1895
|
if (operation.mode === managedInstructionBlockMode) {
|
|
1824
1896
|
const selector = managedInstructionSelector(operation.logicalSelector, operation.artifactType, operation.artifactName);
|
|
1825
|
-
const hash2 = await writeManagedInstructionBlock(operation.sourcePath, operation.destPath, selector, transport, operation
|
|
1897
|
+
const hash2 = await writeManagedInstructionBlock(operation.sourcePath, operation.destPath, selector, transport, managedBlockMutationOptions(operation));
|
|
1826
1898
|
if (hash2 !== operation.desiredHash) {
|
|
1827
1899
|
throw new Error(`Managed block hash verification failed for ${operation.relativeDestPath}: expected ${operation.desiredHash}, got ${hash2}`);
|
|
1828
1900
|
}
|
|
@@ -1835,6 +1907,8 @@ async function applyOperation(operation, context) {
|
|
|
1835
1907
|
}
|
|
1836
1908
|
if (operation.mergeStrategy === "json-deep") {
|
|
1837
1909
|
await mergeWithTransport(operation.sourcePath, operation.destPath, transport, mergeJsonFile);
|
|
1910
|
+
} else if (operation.mergeStrategy === "openclaw-json-deep") {
|
|
1911
|
+
await mergeWithTransport(operation.sourcePath, operation.destPath, transport, mergeOpenClawJsonFile);
|
|
1838
1912
|
} else if (operation.mergeStrategy === "yaml-deep") {
|
|
1839
1913
|
await mergeWithTransport(operation.sourcePath, operation.destPath, transport, mergeYamlFile);
|
|
1840
1914
|
} else if (operation.mergeStrategy === "codex-toml-mcp") {
|
|
@@ -1884,7 +1958,7 @@ async function applyOperation(operation, context) {
|
|
|
1884
1958
|
}
|
|
1885
1959
|
if (operation.mode === managedInstructionBlockMode) {
|
|
1886
1960
|
const selector = managedInstructionSelector(operation.logicalSelector, operation.artifactType, operation.artifactName);
|
|
1887
|
-
await removeManagedInstructionBlock(operation.destPath, selector, transport, operation
|
|
1961
|
+
await removeManagedInstructionBlock(operation.destPath, selector, transport, managedBlockMutationOptions(operation));
|
|
1888
1962
|
} else {
|
|
1889
1963
|
await transport.rm(operation.destPath);
|
|
1890
1964
|
}
|
|
@@ -2057,6 +2131,12 @@ function semanticInstallCommands(operation) {
|
|
|
2057
2131
|
if (operation.semanticPlugin) return operation.semanticPlugin.installCommands;
|
|
2058
2132
|
return operation.semanticCommand ? [operation.semanticCommand] : [];
|
|
2059
2133
|
}
|
|
2134
|
+
function managedBlockMutationOptions(operation) {
|
|
2135
|
+
return {
|
|
2136
|
+
expectedHash: operation.overrideDrift ? void 0 : operation.manifestHash,
|
|
2137
|
+
allowDrift: operation.overrideDrift === true
|
|
2138
|
+
};
|
|
2139
|
+
}
|
|
2060
2140
|
async function entryForCompletedOperation(operation, transport, now, graphLockDigest) {
|
|
2061
2141
|
if (operation.action === "remove") return void 0;
|
|
2062
2142
|
if (operation.action === "create" || operation.action === "update") {
|
|
@@ -2170,7 +2250,7 @@ async function mergeWithTransport(sourcePath, destPath, transport, merge) {
|
|
|
2170
2250
|
const localDest = join5(tempRoot, basename3(destPath) || "merged");
|
|
2171
2251
|
try {
|
|
2172
2252
|
if (await transport.pathExists(destPath)) {
|
|
2173
|
-
await
|
|
2253
|
+
await writeFile9(localDest, await transport.readFile(destPath), "utf8");
|
|
2174
2254
|
}
|
|
2175
2255
|
await merge(sourcePath, localDest);
|
|
2176
2256
|
await transport.atomicCopy(localDest, destPath, "file");
|
|
@@ -2183,8 +2263,8 @@ async function mergeWithTransport(sourcePath, destPath, transport, merge) {
|
|
|
2183
2263
|
import { basename as basename7, join as join15, relative as relative3 } from "path";
|
|
2184
2264
|
|
|
2185
2265
|
// src/staging/codex-subagents.ts
|
|
2186
|
-
import { mkdir as
|
|
2187
|
-
import { basename as basename4, dirname as
|
|
2266
|
+
import { mkdir as mkdir8, readFile as readFile10, writeFile as writeFile10 } from "fs/promises";
|
|
2267
|
+
import { basename as basename4, dirname as dirname10, join as join6 } from "path";
|
|
2188
2268
|
var requiredCodexAgentFields = ["name", "description", "developer_instructions"];
|
|
2189
2269
|
async function renderCodexSubagents(artifacts, stageRoot, adapter) {
|
|
2190
2270
|
if (adapter?.name !== "codex") return artifacts;
|
|
@@ -2210,7 +2290,7 @@ async function renderCodexSubagent(artifact, stageRoot) {
|
|
|
2210
2290
|
const renderedPath = join6(stageRoot, ".agentwheel-rendered", "codex-subagents", `${agentName}.toml`);
|
|
2211
2291
|
const lowerSourcePath = sourcePath.toLowerCase();
|
|
2212
2292
|
if (artifact.kind === "file" && (artifact.name.toLowerCase().endsWith(".toml") || lowerSourcePath.endsWith(".toml"))) {
|
|
2213
|
-
const content = await
|
|
2293
|
+
const content = await readFile10(sourcePath, "utf8");
|
|
2214
2294
|
validateCodexAgentToml(content, sourcePath);
|
|
2215
2295
|
return {
|
|
2216
2296
|
...artifact,
|
|
@@ -2227,10 +2307,10 @@ async function renderCodexSubagent(artifact, stageRoot) {
|
|
|
2227
2307
|
if (artifact.kind === "file" && !artifact.name.toLowerCase().endsWith(".md") && !lowerSourcePath.endsWith(".md")) {
|
|
2228
2308
|
throw new Error(`Codex subagent ${artifact.relativePath} must be a .toml file, .md file, or directory containing AGENTS.md.`);
|
|
2229
2309
|
}
|
|
2230
|
-
const markdown = await
|
|
2310
|
+
const markdown = await readFile10(markdownPath, "utf8");
|
|
2231
2311
|
const toml = markdownToCodexAgentToml(agentName, markdown);
|
|
2232
|
-
await
|
|
2233
|
-
await
|
|
2312
|
+
await mkdir8(dirname10(renderedPath), { recursive: true });
|
|
2313
|
+
await writeFile10(renderedPath, toml, "utf8");
|
|
2234
2314
|
return {
|
|
2235
2315
|
...artifact,
|
|
2236
2316
|
name: agentName,
|
|
@@ -2298,8 +2378,8 @@ function escapeRegExp(value) {
|
|
|
2298
2378
|
}
|
|
2299
2379
|
|
|
2300
2380
|
// src/staging/copilot-artifacts.ts
|
|
2301
|
-
import { mkdir as
|
|
2302
|
-
import { basename as basename5, dirname as
|
|
2381
|
+
import { mkdir as mkdir9, readFile as readFile11, writeFile as writeFile11 } from "fs/promises";
|
|
2382
|
+
import { basename as basename5, dirname as dirname11, join as join7 } from "path";
|
|
2303
2383
|
async function renderCopilotArtifacts(artifacts, stageRoot, adapter) {
|
|
2304
2384
|
if (adapter?.name !== "copilot") return artifacts;
|
|
2305
2385
|
const names = /* @__PURE__ */ new Set();
|
|
@@ -2329,9 +2409,9 @@ async function renderCopilotSubagent(artifact, stageRoot) {
|
|
|
2329
2409
|
if (artifact.kind === "file" && !artifact.name.toLowerCase().endsWith(".md")) {
|
|
2330
2410
|
throw new Error(`Copilot subagent ${artifact.relativePath} must be a .md file or directory containing AGENTS.md.`);
|
|
2331
2411
|
}
|
|
2332
|
-
const markdown = await
|
|
2333
|
-
await
|
|
2334
|
-
await
|
|
2412
|
+
const markdown = await readFile11(markdownPath, "utf8");
|
|
2413
|
+
await mkdir9(dirname11(renderedPath), { recursive: true });
|
|
2414
|
+
await writeFile11(renderedPath, ensureCopilotAgentDescription(agentName, markdown), "utf8");
|
|
2335
2415
|
return {
|
|
2336
2416
|
...artifact,
|
|
2337
2417
|
name: `${agentName}.agent.md`,
|
|
@@ -2387,7 +2467,7 @@ function yamlString(value) {
|
|
|
2387
2467
|
import { join as join9 } from "path";
|
|
2388
2468
|
|
|
2389
2469
|
// src/targets/plugins/common.ts
|
|
2390
|
-
import { readFile as
|
|
2470
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
2391
2471
|
import { join as join8 } from "path";
|
|
2392
2472
|
import { parseDocument } from "yaml";
|
|
2393
2473
|
function pluginStateRoot(request) {
|
|
@@ -2411,14 +2491,14 @@ function safeNameSegment(value) {
|
|
|
2411
2491
|
async function jsonPluginName(root, relativeManifestPath, fallback) {
|
|
2412
2492
|
const manifestPath = join8(root, relativeManifestPath);
|
|
2413
2493
|
if (!await pathExists(manifestPath)) return fallback;
|
|
2414
|
-
const parsed = JSON.parse(await
|
|
2494
|
+
const parsed = JSON.parse(await readFile12(manifestPath, "utf8"));
|
|
2415
2495
|
return typeof parsed.name === "string" && parsed.name.trim().length > 0 ? parsed.name.trim() : fallback;
|
|
2416
2496
|
}
|
|
2417
2497
|
async function yamlPluginName(root, relativeManifestPaths, fallback) {
|
|
2418
2498
|
for (const relativeManifestPath of relativeManifestPaths) {
|
|
2419
2499
|
const manifestPath = join8(root, relativeManifestPath);
|
|
2420
2500
|
if (!await pathExists(manifestPath)) continue;
|
|
2421
|
-
const document = parseDocument(await
|
|
2501
|
+
const document = parseDocument(await readFile12(manifestPath, "utf8"));
|
|
2422
2502
|
const parsed = document.toJSON();
|
|
2423
2503
|
if (!isRecord4(parsed)) continue;
|
|
2424
2504
|
for (const key of ["name", "module", "package"]) {
|
|
@@ -2550,7 +2630,7 @@ async function hermesPluginSpec(request) {
|
|
|
2550
2630
|
}
|
|
2551
2631
|
|
|
2552
2632
|
// src/targets/plugins/openclaw.ts
|
|
2553
|
-
import { readFile as
|
|
2633
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
2554
2634
|
import { join as join13 } from "path";
|
|
2555
2635
|
function openClawPluginInstallCommand(request) {
|
|
2556
2636
|
return ["openclaw", "plugins", "install", "--force", request.path];
|
|
@@ -2559,6 +2639,15 @@ function openClawPluginUninstallCommand(pluginName) {
|
|
|
2559
2639
|
return ["openclaw", "plugins", "uninstall", pluginName, "--force"];
|
|
2560
2640
|
}
|
|
2561
2641
|
async function openClawPluginSpec(request) {
|
|
2642
|
+
if (request.format === "openclaw-clawhub-plugin") {
|
|
2643
|
+
const metadata = await openClawClawHubPluginMetadata(request.path, request.fallbackPluginName);
|
|
2644
|
+
return {
|
|
2645
|
+
runtime: "openclaw",
|
|
2646
|
+
pluginName: metadata.pluginName,
|
|
2647
|
+
installCommands: [openClawPluginInstallCommand({ path: metadata.installSpec, dryRun: true })],
|
|
2648
|
+
uninstallCommands: [openClawPluginUninstallCommand(metadata.pluginName)]
|
|
2649
|
+
};
|
|
2650
|
+
}
|
|
2562
2651
|
const pluginName = await openClawPluginName(request.path, request.fallbackPluginName);
|
|
2563
2652
|
return {
|
|
2564
2653
|
runtime: "openclaw",
|
|
@@ -2571,17 +2660,37 @@ async function openClawPluginName(root, fallback) {
|
|
|
2571
2660
|
for (const manifestName of ["plugin.json", "openclaw.plugin.json"]) {
|
|
2572
2661
|
const manifestPath = join13(root, manifestName);
|
|
2573
2662
|
if (!await pathExists(manifestPath)) continue;
|
|
2574
|
-
const parsed = JSON.parse(await
|
|
2663
|
+
const parsed = JSON.parse(await readFile13(manifestPath, "utf8"));
|
|
2575
2664
|
if (typeof parsed.name === "string" && parsed.name.trim().length > 0) return parsed.name.trim();
|
|
2576
2665
|
}
|
|
2577
2666
|
return fallback;
|
|
2578
2667
|
}
|
|
2668
|
+
async function openClawClawHubPluginMetadata(root, fallback) {
|
|
2669
|
+
const metadataPath = join13(root, "clawhub.json");
|
|
2670
|
+
if (!await pathExists(metadataPath)) {
|
|
2671
|
+
throw new Error("OpenClaw ClawHub plugins must contain clawhub.json");
|
|
2672
|
+
}
|
|
2673
|
+
const parsed = JSON.parse(await readFile13(metadataPath, "utf8"));
|
|
2674
|
+
const installSpec = stringField(parsed.installSpec);
|
|
2675
|
+
if (!installSpec?.startsWith("clawhub:")) {
|
|
2676
|
+
throw new Error("OpenClaw ClawHub plugin metadata must declare installSpec starting with clawhub:");
|
|
2677
|
+
}
|
|
2678
|
+
const pluginName = stringField(parsed.runtimeId) ?? installNameFor(stringField(parsed.name) ?? fallback);
|
|
2679
|
+
return { installSpec, pluginName };
|
|
2680
|
+
}
|
|
2681
|
+
function stringField(value) {
|
|
2682
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
2683
|
+
}
|
|
2684
|
+
function installNameFor(value) {
|
|
2685
|
+
return value.split("/").at(-1).replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "clawhub-plugin";
|
|
2686
|
+
}
|
|
2579
2687
|
|
|
2580
2688
|
// src/targets/plugins/index.ts
|
|
2581
2689
|
async function semanticPluginSpecForArtifact(request) {
|
|
2582
2690
|
if (request.semantic === "openclaw-plugin") {
|
|
2583
2691
|
return openClawPluginSpec({
|
|
2584
2692
|
path: request.sourcePath,
|
|
2693
|
+
format: request.artifact.format,
|
|
2585
2694
|
fallbackPluginName: request.installName
|
|
2586
2695
|
});
|
|
2587
2696
|
}
|
|
@@ -2601,7 +2710,7 @@ async function semanticPluginSpecForArtifact(request) {
|
|
|
2601
2710
|
}
|
|
2602
2711
|
|
|
2603
2712
|
// src/validation/artifacts.ts
|
|
2604
|
-
import { readFile as
|
|
2713
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
2605
2714
|
import { basename as basename6, join as join14 } from "path";
|
|
2606
2715
|
import { parseDocument as parseDocument2 } from "yaml";
|
|
2607
2716
|
|
|
@@ -2657,7 +2766,7 @@ function subagentBaseName(name) {
|
|
|
2657
2766
|
|
|
2658
2767
|
// src/validation/artifacts.ts
|
|
2659
2768
|
var behavioralRuleFormats = ["markdown-rule", "claude-markdown-rule", "copilot-instruction-rule"];
|
|
2660
|
-
var pluginFormats = ["claude-plugin", "codex-plugin", "hermes-plugin", "copilot-plugin", "openclaw-plugin"];
|
|
2769
|
+
var pluginFormats = ["claude-plugin", "codex-plugin", "hermes-plugin", "copilot-plugin", "openclaw-plugin", "openclaw-clawhub-plugin"];
|
|
2661
2770
|
async function filterArtifactsByInstallFormat(artifacts, adapter, installationType, options = {}) {
|
|
2662
2771
|
const selectedSet = new Set(normalizeArtifactSelectors(options.selected ?? []) ?? []);
|
|
2663
2772
|
const kept = [];
|
|
@@ -2766,6 +2875,7 @@ async function inferArtifactFormat(artifact, target) {
|
|
|
2766
2875
|
return void 0;
|
|
2767
2876
|
}
|
|
2768
2877
|
if (artifact.type === "plugins" && target.semantic === "openclaw-plugin") {
|
|
2878
|
+
if (artifact.kind === "dir" && await pathExists(join14(artifactPath(artifact), "clawhub.json"))) return "openclaw-clawhub-plugin";
|
|
2769
2879
|
if (artifact.kind === "dir" && (await openClawPluginManifestPaths(artifact)).length > 0) return "openclaw-plugin";
|
|
2770
2880
|
}
|
|
2771
2881
|
return void 0;
|
|
@@ -2781,9 +2891,9 @@ function expectedFormats(artifact, target) {
|
|
|
2781
2891
|
return behavioralRuleFormats;
|
|
2782
2892
|
}
|
|
2783
2893
|
if (artifact.type === "plugins") {
|
|
2784
|
-
if (isPluginFormat(target.semantic)) return [target.semantic];
|
|
2785
2894
|
const declared = target.formats?.filter((format) => pluginFormats.includes(format));
|
|
2786
2895
|
if (target.formats?.length) return declared?.length ? declared : pluginFormats;
|
|
2896
|
+
if (isPluginFormat(target.semantic)) return [target.semantic];
|
|
2787
2897
|
}
|
|
2788
2898
|
return target.formats;
|
|
2789
2899
|
}
|
|
@@ -2792,7 +2902,10 @@ async function validateKnownFormat(artifact, format, target) {
|
|
|
2792
2902
|
if (format === "markdown-rule" || format === "claude-markdown-rule" || format === "copilot-instruction-rule") {
|
|
2793
2903
|
return validateMarkdownRule(artifact, format);
|
|
2794
2904
|
}
|
|
2795
|
-
if (format === "openclaw-
|
|
2905
|
+
if (format === "openclaw-clawhub-plugin") {
|
|
2906
|
+
return validateOpenClawClawHubPlugin(artifact);
|
|
2907
|
+
}
|
|
2908
|
+
if (format === "openclaw-plugin") {
|
|
2796
2909
|
return validateOpenClawPlugin(artifact);
|
|
2797
2910
|
}
|
|
2798
2911
|
if (format === "claude-plugin" || target.semantic === "claude-plugin") {
|
|
@@ -2847,7 +2960,7 @@ async function validateGenericStructure(artifact, target) {
|
|
|
2847
2960
|
async function validateSkillFrontmatter(artifact, skillMdPath) {
|
|
2848
2961
|
let content;
|
|
2849
2962
|
try {
|
|
2850
|
-
content = await
|
|
2963
|
+
content = await readFile14(skillMdPath, "utf8");
|
|
2851
2964
|
} catch (error) {
|
|
2852
2965
|
return [{ artifact, message: `could not read SKILL.md: ${errorMessage(error)}` }];
|
|
2853
2966
|
}
|
|
@@ -2917,7 +3030,7 @@ async function validateHermesPlugin(artifact) {
|
|
|
2917
3030
|
return [{ artifact, message: "Hermes plugins must contain plugin.yaml or plugin.yml" }];
|
|
2918
3031
|
}
|
|
2919
3032
|
try {
|
|
2920
|
-
const document = parseDocument2(await
|
|
3033
|
+
const document = parseDocument2(await readFile14(manifestPath, "utf8"));
|
|
2921
3034
|
if (document.errors.length > 0) {
|
|
2922
3035
|
return [{ artifact, message: `Hermes ${basename6(manifestPath)} must be valid YAML: ${document.errors[0]?.message ?? "parse error"}` }];
|
|
2923
3036
|
}
|
|
@@ -2941,7 +3054,7 @@ async function firstExistingPath(paths) {
|
|
|
2941
3054
|
}
|
|
2942
3055
|
async function parseJsonPluginManifest(manifestPath, label) {
|
|
2943
3056
|
try {
|
|
2944
|
-
const parsed = JSON.parse(await
|
|
3057
|
+
const parsed = JSON.parse(await readFile14(manifestPath, "utf8"));
|
|
2945
3058
|
if (!isRecord5(parsed)) {
|
|
2946
3059
|
return { ok: false, message: `${label} ${basename6(manifestPath)} must be a JSON object` };
|
|
2947
3060
|
}
|
|
@@ -2976,6 +3089,33 @@ async function validateOpenClawPlugin(artifact) {
|
|
|
2976
3089
|
}
|
|
2977
3090
|
return issues;
|
|
2978
3091
|
}
|
|
3092
|
+
async function validateOpenClawClawHubPlugin(artifact) {
|
|
3093
|
+
if (artifact.type !== "plugins") {
|
|
3094
|
+
return [{ artifact, message: "openclaw-clawhub-plugin format is only valid for plugins artifacts" }];
|
|
3095
|
+
}
|
|
3096
|
+
if (artifact.kind !== "dir") {
|
|
3097
|
+
return [{ artifact, message: "OpenClaw ClawHub plugins must be directory artifacts" }];
|
|
3098
|
+
}
|
|
3099
|
+
const metadataPath = join14(artifactPath(artifact), "clawhub.json");
|
|
3100
|
+
if (!await pathExists(metadataPath)) {
|
|
3101
|
+
return [{ artifact, message: "OpenClaw ClawHub plugins must contain clawhub.json" }];
|
|
3102
|
+
}
|
|
3103
|
+
try {
|
|
3104
|
+
const parsed = JSON.parse(await readFile14(metadataPath, "utf8"));
|
|
3105
|
+
if (!isRecord5(parsed)) {
|
|
3106
|
+
return [{ artifact, message: "OpenClaw ClawHub clawhub.json must be a JSON object" }];
|
|
3107
|
+
}
|
|
3108
|
+
if (typeof parsed.installSpec !== "string" || !parsed.installSpec.trim().startsWith("clawhub:")) {
|
|
3109
|
+
return [{ artifact, message: "OpenClaw ClawHub clawhub.json must declare installSpec starting with clawhub:" }];
|
|
3110
|
+
}
|
|
3111
|
+
if (typeof parsed.name !== "string" || parsed.name.trim().length === 0) {
|
|
3112
|
+
return [{ artifact, message: "OpenClaw ClawHub clawhub.json must declare a non-empty name" }];
|
|
3113
|
+
}
|
|
3114
|
+
return [];
|
|
3115
|
+
} catch (error) {
|
|
3116
|
+
return [{ artifact, message: `OpenClaw ClawHub clawhub.json must be valid JSON: ${errorMessage(error)}` }];
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
2979
3119
|
async function openClawPluginManifestPaths(artifact) {
|
|
2980
3120
|
const root = artifactPath(artifact);
|
|
2981
3121
|
const candidates = [join14(root, "plugin.json"), join14(root, "openclaw.plugin.json")];
|
|
@@ -2985,7 +3125,7 @@ async function openClawPluginManifestPaths(artifact) {
|
|
|
2985
3125
|
async function parseOpenClawPluginManifest(manifestPath) {
|
|
2986
3126
|
const manifestName = basename6(manifestPath);
|
|
2987
3127
|
try {
|
|
2988
|
-
const parsed = JSON.parse(await
|
|
3128
|
+
const parsed = JSON.parse(await readFile14(manifestPath, "utf8"));
|
|
2989
3129
|
if (!isRecord5(parsed)) {
|
|
2990
3130
|
return { ok: false, path: manifestPath, message: `OpenClaw ${manifestName} must be a JSON object` };
|
|
2991
3131
|
}
|
|
@@ -3002,7 +3142,7 @@ async function parseJsonObjectArtifact(artifact) {
|
|
|
3002
3142
|
return { ok: false, message: "merge artifacts must be JSON files" };
|
|
3003
3143
|
}
|
|
3004
3144
|
try {
|
|
3005
|
-
const parsed = JSON.parse(await
|
|
3145
|
+
const parsed = JSON.parse(await readFile14(artifactPath(artifact), "utf8"));
|
|
3006
3146
|
if (!isRecord5(parsed)) return { ok: false, message: "merge artifacts must contain a JSON object" };
|
|
3007
3147
|
return { ok: true, value: parsed };
|
|
3008
3148
|
} catch (error) {
|
|
@@ -3014,7 +3154,7 @@ async function parseYamlObjectArtifact(artifact) {
|
|
|
3014
3154
|
return { ok: false, message: "merge artifacts must be YAML files" };
|
|
3015
3155
|
}
|
|
3016
3156
|
try {
|
|
3017
|
-
const document = parseDocument2(await
|
|
3157
|
+
const document = parseDocument2(await readFile14(artifactPath(artifact), "utf8"));
|
|
3018
3158
|
if (document.errors.length > 0) {
|
|
3019
3159
|
return { ok: false, message: `merge artifact must be valid YAML: ${document.errors[0]?.message ?? "parse error"}` };
|
|
3020
3160
|
}
|
|
@@ -3139,7 +3279,7 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
|
|
|
3139
3279
|
}
|
|
3140
3280
|
const currentHash2 = await transport.hashPath(op.destPath);
|
|
3141
3281
|
if (existing2 && existing2.sourceHash === op.desiredHash) {
|
|
3142
|
-
operations.push({ ...op, action: "skip", currentHash: currentHash2, manifestHash: existing2.hash, reason: "merged source already up to date" });
|
|
3282
|
+
operations.push(options.forceDrift ? { ...op, action: "update", currentHash: currentHash2, manifestHash: existing2.hash, reason: "force refreshing managed merge destination" } : { ...op, action: "skip", currentHash: currentHash2, manifestHash: existing2.hash, reason: "merged source already up to date" });
|
|
3143
3283
|
} else {
|
|
3144
3284
|
operations.push({ ...op, action: "update", currentHash: currentHash2, manifestHash: existing2?.hash, reason: existing2 ? "merge source changed" : "merge into existing destination" });
|
|
3145
3285
|
}
|
|
@@ -3186,6 +3326,7 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
|
|
|
3186
3326
|
action: "update",
|
|
3187
3327
|
currentHash,
|
|
3188
3328
|
manifestHash: existing.hash,
|
|
3329
|
+
overrideDrift: true,
|
|
3189
3330
|
reason: reasonWithComposedDiff("force replacing drifted managed destination", op.composedFrom, existing.composedFrom),
|
|
3190
3331
|
composedFromDiff
|
|
3191
3332
|
});
|
|
@@ -3245,7 +3386,7 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
|
|
|
3245
3386
|
mode: entry.mode,
|
|
3246
3387
|
composedFrom: entry.composedFrom,
|
|
3247
3388
|
...operationMetadataFromEntry(entry),
|
|
3248
|
-
...options.forceDrift ? { action: "remove", reason: "force removing drifted stale managed destination" } : {}
|
|
3389
|
+
...options.forceDrift ? { action: "remove", reason: "force removing drifted stale managed destination", overrideDrift: true } : {}
|
|
3249
3390
|
});
|
|
3250
3391
|
} else {
|
|
3251
3392
|
operations.push({
|
|
@@ -3422,6 +3563,7 @@ async function planManagedBlockOperation(op, manifestByPath, transport, options,
|
|
|
3422
3563
|
action: "update",
|
|
3423
3564
|
currentHash: state.hash,
|
|
3424
3565
|
manifestHash: existing.hash,
|
|
3566
|
+
overrideDrift: true,
|
|
3425
3567
|
reason: reasonWithComposedDiff("force replacing drifted managed instruction block", op.composedFrom, existing.composedFrom),
|
|
3426
3568
|
composedFromDiff
|
|
3427
3569
|
}];
|
|
@@ -3452,7 +3594,7 @@ async function currentEntryHash(entry, destPath, transport) {
|
|
|
3452
3594
|
if (entry.mode !== managedInstructionBlockMode) return transport.hashPath(destPath);
|
|
3453
3595
|
const selector = managedInstructionSelector("logicalSelector" in entry ? entry.logicalSelector : void 0, entry.artifactType, entry.artifactName);
|
|
3454
3596
|
const state = await readManagedInstructionBlockState(destPath, selector, transport);
|
|
3455
|
-
return state.
|
|
3597
|
+
return state.hash;
|
|
3456
3598
|
}
|
|
3457
3599
|
async function canStrictlyAdoptLegacyEntry(entry, op, targetRoot, transport) {
|
|
3458
3600
|
if (entry.artifactType !== op.artifactType || entry.artifactName !== op.artifactName) return false;
|
|
@@ -3924,7 +4066,7 @@ async function currentEntryHash2(entry, destPath, transport) {
|
|
|
3924
4066
|
if (entry.mode !== managedInstructionBlockMode) return transport.hashPath(destPath);
|
|
3925
4067
|
const selector = managedInstructionSelector("logicalSelector" in entry ? entry.logicalSelector : void 0, entry.artifactType, entry.artifactName);
|
|
3926
4068
|
const state = await readManagedInstructionBlockState(destPath, selector, transport);
|
|
3927
|
-
return state.
|
|
4069
|
+
return state.hash;
|
|
3928
4070
|
}
|
|
3929
4071
|
function operationMetadataFromEntry2(entry, ownersOverride) {
|
|
3930
4072
|
if ("owners" in entry) {
|
|
@@ -4148,15 +4290,17 @@ function ownerChains(lock, nodeId) {
|
|
|
4148
4290
|
return incoming.flatMap((edge) => ownerChains(lock, edge.from).map((chain) => [...chain, `${edge.alias}:${nodeId}`]));
|
|
4149
4291
|
}
|
|
4150
4292
|
|
|
4151
|
-
// src/source/
|
|
4152
|
-
import {
|
|
4153
|
-
import {
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
import {
|
|
4293
|
+
// src/source/clawhub.ts
|
|
4294
|
+
import { mkdir as mkdir10, rm as rm5, writeFile as writeFile12 } from "fs/promises";
|
|
4295
|
+
import { basename as basename9, dirname as dirname12, join as join19, resolve as resolve6 } from "path";
|
|
4296
|
+
|
|
4297
|
+
// src/source/local.ts
|
|
4298
|
+
import { createHash as createHash4 } from "crypto";
|
|
4299
|
+
import { readdir, stat as stat3 } from "fs/promises";
|
|
4300
|
+
import { basename as basename8, join as join18, relative as relative4, resolve as resolve5 } from "path";
|
|
4157
4301
|
|
|
4158
4302
|
// src/model/package.ts
|
|
4159
|
-
import { readFile as
|
|
4303
|
+
import { readFile as readFile15 } from "fs/promises";
|
|
4160
4304
|
import { join as join17 } from "path";
|
|
4161
4305
|
import { parse as parse3, printParseErrorCode as printParseErrorCode2 } from "jsonc-parser";
|
|
4162
4306
|
import { z as z5 } from "zod";
|
|
@@ -4181,6 +4325,7 @@ var packageProvideBaseSchema = z5.object({
|
|
|
4181
4325
|
var packageItemSchema = z5.object({
|
|
4182
4326
|
format: artifactFormatSchema.optional(),
|
|
4183
4327
|
requires: z5.array(packageItemRequireSchema).optional(),
|
|
4328
|
+
suggests: z5.array(packageItemSuggestSchema).optional(),
|
|
4184
4329
|
compose: z5.array(packageComposeEntrySchema).optional(),
|
|
4185
4330
|
runtimes: runtimeListSchema.optional()
|
|
4186
4331
|
});
|
|
@@ -4194,6 +4339,10 @@ var packageDependencySchema = z5.object({
|
|
|
4194
4339
|
integrity: z5.string().min(1).optional(),
|
|
4195
4340
|
runtimes: runtimeListSchema.optional()
|
|
4196
4341
|
});
|
|
4342
|
+
var packageSuggestionSchema = packageDependencySchema.extend({
|
|
4343
|
+
reason: z5.string().min(1).optional(),
|
|
4344
|
+
when: z5.string().min(1).optional()
|
|
4345
|
+
});
|
|
4197
4346
|
var packageProvideV1Schema = packageProvideBaseSchema.extend({
|
|
4198
4347
|
type: legacyArtifactTypeSchema
|
|
4199
4348
|
});
|
|
@@ -4214,6 +4363,7 @@ var packageManifestV2Schema = z5.object({
|
|
|
4214
4363
|
version: z5.string().min(1),
|
|
4215
4364
|
runtimes: runtimeListSchema.optional(),
|
|
4216
4365
|
requires: z5.record(z5.string().min(1), packageDependencySchema).optional(),
|
|
4366
|
+
suggests: z5.record(z5.string().min(1), packageSuggestionSchema).optional(),
|
|
4217
4367
|
compose: z5.array(packageComposeEntrySchema).optional(),
|
|
4218
4368
|
provides: z5.array(packageProvideSchema).default([])
|
|
4219
4369
|
}).superRefine((manifest, ctx) => {
|
|
@@ -4247,7 +4397,7 @@ async function findPackageManifestPath(root, options = {}) {
|
|
|
4247
4397
|
async function readPackageManifest(root) {
|
|
4248
4398
|
const path = await findPackageManifestPath(root);
|
|
4249
4399
|
if (!path) return void 0;
|
|
4250
|
-
const content = await
|
|
4400
|
+
const content = await readFile15(path, "utf8");
|
|
4251
4401
|
const errors = [];
|
|
4252
4402
|
const parsed = parse3(content, errors, { allowTrailingComma: true, disallowComments: false });
|
|
4253
4403
|
if (errors.length > 0) {
|
|
@@ -4301,9 +4451,6 @@ function isRecord6(value) {
|
|
|
4301
4451
|
}
|
|
4302
4452
|
|
|
4303
4453
|
// src/source/local.ts
|
|
4304
|
-
import { createHash as createHash4 } from "crypto";
|
|
4305
|
-
import { readdir, stat as stat3 } from "fs/promises";
|
|
4306
|
-
import { basename as basename8, join as join18, relative as relative4, resolve as resolve5 } from "path";
|
|
4307
4454
|
var LocalSourceDriver = class {
|
|
4308
4455
|
name = "local";
|
|
4309
4456
|
async resolve(source) {
|
|
@@ -4527,6 +4674,7 @@ async function artifactForFile(type, name, sourcePath, relativePath, packageName
|
|
|
4527
4674
|
assets: provide?.assets,
|
|
4528
4675
|
required: provide?.required,
|
|
4529
4676
|
requires: item.requires,
|
|
4677
|
+
suggests: item.suggests,
|
|
4530
4678
|
compose: item.compose,
|
|
4531
4679
|
runtimes: item.runtimes ?? provideRuntimes(provide) ?? manifestRuntimes(manifest)
|
|
4532
4680
|
};
|
|
@@ -4546,6 +4694,7 @@ async function artifactForDir(type, name, sourcePath, relativePath, packageName,
|
|
|
4546
4694
|
assets: provide?.assets,
|
|
4547
4695
|
required: provide?.required,
|
|
4548
4696
|
requires: item.requires,
|
|
4697
|
+
suggests: item.suggests,
|
|
4549
4698
|
compose: item.compose,
|
|
4550
4699
|
runtimes: item.runtimes ?? provideRuntimes(provide) ?? manifestRuntimes(manifest)
|
|
4551
4700
|
};
|
|
@@ -4554,7 +4703,7 @@ function itemMetadata(provide, itemName) {
|
|
|
4554
4703
|
if (!provide || !("items" in provide) || !provide.items || !itemName) return {};
|
|
4555
4704
|
const item = provide.items[itemName];
|
|
4556
4705
|
if (!item) return {};
|
|
4557
|
-
return { format: item.format, requires: item.requires, compose: item.compose, runtimes: item.runtimes };
|
|
4706
|
+
return { format: item.format, requires: item.requires, suggests: item.suggests, compose: item.compose, runtimes: item.runtimes };
|
|
4558
4707
|
}
|
|
4559
4708
|
function provideRuntimes(provide) {
|
|
4560
4709
|
return provide && "runtimes" in provide ? provide.runtimes : void 0;
|
|
@@ -4563,7 +4712,154 @@ function manifestRuntimes(manifest) {
|
|
|
4563
4712
|
return manifest && manifest.schemaVersion === 2 ? manifest.runtimes : void 0;
|
|
4564
4713
|
}
|
|
4565
4714
|
|
|
4715
|
+
// src/source/clawhub.ts
|
|
4716
|
+
var clawHubBaseUrl = "https://clawhub.ai/api/v1";
|
|
4717
|
+
var sourcePrefix = "clawhub:";
|
|
4718
|
+
var transientStatuses = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
|
|
4719
|
+
var ClawHubSourceDriver = class {
|
|
4720
|
+
constructor(fetchImpl = fetch) {
|
|
4721
|
+
this.fetchImpl = fetchImpl;
|
|
4722
|
+
}
|
|
4723
|
+
fetchImpl;
|
|
4724
|
+
name = "clawhub";
|
|
4725
|
+
local = new LocalSourceDriver();
|
|
4726
|
+
async resolve(source, options = {}) {
|
|
4727
|
+
const packageName = parseClawHubSource(source);
|
|
4728
|
+
return {
|
|
4729
|
+
driver: this.name,
|
|
4730
|
+
source,
|
|
4731
|
+
resolvedPath: cachePathFor(packageName, options.cacheRoot),
|
|
4732
|
+
packageName: `clawhub/${packageName}`,
|
|
4733
|
+
mode: options.mode ?? "tracking",
|
|
4734
|
+
requestedRef: options.ref ?? "latest",
|
|
4735
|
+
frozenLock: options.frozenLock,
|
|
4736
|
+
cacheLockTimeoutMs: options.cacheLockTimeoutMs
|
|
4737
|
+
};
|
|
4738
|
+
}
|
|
4739
|
+
async fetch(resolved) {
|
|
4740
|
+
if (resolved.frozenLock) {
|
|
4741
|
+
if (!await pathExists(resolved.resolvedPath)) {
|
|
4742
|
+
throw new Error(`Frozen lock requires cached ClawHub source at ${resolved.resolvedPath}`);
|
|
4743
|
+
}
|
|
4744
|
+
return {
|
|
4745
|
+
...resolved,
|
|
4746
|
+
sourceHash: await hashPath(resolved.resolvedPath)
|
|
4747
|
+
};
|
|
4748
|
+
}
|
|
4749
|
+
const requestedName = parseClawHubSource(resolved.source);
|
|
4750
|
+
const response = await fetchClawHubPackage(this.fetchImpl, requestedName);
|
|
4751
|
+
if (!response.ok) {
|
|
4752
|
+
throw new Error(`ClawHub lookup failed for ${requestedName}: HTTP ${response.status}`);
|
|
4753
|
+
}
|
|
4754
|
+
const payload = await response.json();
|
|
4755
|
+
const packageInfo = payload.package;
|
|
4756
|
+
if (!packageInfo?.name) {
|
|
4757
|
+
throw new Error(`ClawHub response missing package metadata for ${requestedName}`);
|
|
4758
|
+
}
|
|
4759
|
+
if (!isInstallableOpenClawPackage(packageInfo.family)) {
|
|
4760
|
+
throw new Error(`ClawHub package is not an OpenClaw plugin or hook package: ${packageInfo.name}`);
|
|
4761
|
+
}
|
|
4762
|
+
await writeGeneratedPackage(resolved.resolvedPath, packageInfo);
|
|
4763
|
+
return {
|
|
4764
|
+
...resolved,
|
|
4765
|
+
packageName: `clawhub/${packageInfo.name}`,
|
|
4766
|
+
packageVersion: packageInfo.latestVersion ?? "latest",
|
|
4767
|
+
sourceHash: await hashPath(resolved.resolvedPath)
|
|
4768
|
+
};
|
|
4769
|
+
}
|
|
4770
|
+
async list(resolved) {
|
|
4771
|
+
return this.local.list({ ...resolved, driver: "local" });
|
|
4772
|
+
}
|
|
4773
|
+
async scan(resolved) {
|
|
4774
|
+
if (!await pathExists(join19(resolved.resolvedPath, "plugins"))) {
|
|
4775
|
+
return { ok: false, findings: [{ level: "error", message: "ClawHub source has no generated plugin artifact" }] };
|
|
4776
|
+
}
|
|
4777
|
+
return { ok: true, findings: [] };
|
|
4778
|
+
}
|
|
4779
|
+
async translate(resolved) {
|
|
4780
|
+
return resolved;
|
|
4781
|
+
}
|
|
4782
|
+
async export(resolved) {
|
|
4783
|
+
return resolved;
|
|
4784
|
+
}
|
|
4785
|
+
};
|
|
4786
|
+
async function fetchClawHubPackage(fetchImpl, packageName) {
|
|
4787
|
+
const url = `${clawHubBaseUrl}/packages/${encodeURIComponent(packageName)}`;
|
|
4788
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
4789
|
+
const response = await fetchImpl(url, { headers: { Accept: "application/json" } });
|
|
4790
|
+
if (!transientStatuses.has(response.status) || attempt === 2) return response;
|
|
4791
|
+
await delay((attempt + 1) * 250);
|
|
4792
|
+
}
|
|
4793
|
+
throw new Error(`ClawHub lookup failed for ${packageName}`);
|
|
4794
|
+
}
|
|
4795
|
+
async function delay(ms) {
|
|
4796
|
+
await new Promise((resolve21) => setTimeout(resolve21, ms));
|
|
4797
|
+
}
|
|
4798
|
+
function parseClawHubSource(source) {
|
|
4799
|
+
if (!source.startsWith(sourcePrefix)) {
|
|
4800
|
+
throw new Error(`Invalid ClawHub source: ${source}`);
|
|
4801
|
+
}
|
|
4802
|
+
const packageName = source.slice(sourcePrefix.length).trim();
|
|
4803
|
+
if (!packageName) throw new Error(`Invalid ClawHub source: ${source}`);
|
|
4804
|
+
return packageName;
|
|
4805
|
+
}
|
|
4806
|
+
function isInstallableOpenClawPackage(family) {
|
|
4807
|
+
if (!family) return true;
|
|
4808
|
+
return family.endsWith("-plugin") || family === "hook-pack";
|
|
4809
|
+
}
|
|
4810
|
+
async function writeGeneratedPackage(root, packageInfo) {
|
|
4811
|
+
const name = packageInfo.name?.trim();
|
|
4812
|
+
if (!name) throw new Error("ClawHub package metadata must include a name");
|
|
4813
|
+
const pluginId = installNameFor2(packageInfo.runtimeId ?? name);
|
|
4814
|
+
const plugin = {
|
|
4815
|
+
name,
|
|
4816
|
+
installSpec: `${sourcePrefix}${name}`,
|
|
4817
|
+
pluginId,
|
|
4818
|
+
displayName: packageInfo.displayName,
|
|
4819
|
+
runtimeId: packageInfo.runtimeId,
|
|
4820
|
+
latestVersion: packageInfo.latestVersion,
|
|
4821
|
+
family: packageInfo.family,
|
|
4822
|
+
summary: packageInfo.summary ?? packageInfo.description,
|
|
4823
|
+
artifact: packageInfo.artifact,
|
|
4824
|
+
verification: packageInfo.verification
|
|
4825
|
+
};
|
|
4826
|
+
const pluginPath = join19(root, "plugins", pluginId, "clawhub.json");
|
|
4827
|
+
await rm5(root, { recursive: true, force: true });
|
|
4828
|
+
await mkdir10(dirname12(pluginPath), { recursive: true });
|
|
4829
|
+
await writeFile12(join19(root, "openpack.json"), `${JSON.stringify({
|
|
4830
|
+
schemaVersion: 2,
|
|
4831
|
+
name: `clawhub/${name}`,
|
|
4832
|
+
version: packageInfo.latestVersion ?? "latest",
|
|
4833
|
+
runtimes: ["openclaw"],
|
|
4834
|
+
provides: [
|
|
4835
|
+
{
|
|
4836
|
+
type: "plugins",
|
|
4837
|
+
path: "plugins",
|
|
4838
|
+
format: "openclaw-clawhub-plugin",
|
|
4839
|
+
runtimes: ["openclaw"],
|
|
4840
|
+
required: true
|
|
4841
|
+
}
|
|
4842
|
+
]
|
|
4843
|
+
}, null, 2)}
|
|
4844
|
+
`, "utf8");
|
|
4845
|
+
await writeFile12(pluginPath, `${JSON.stringify(plugin, null, 2)}
|
|
4846
|
+
`, "utf8");
|
|
4847
|
+
}
|
|
4848
|
+
function installNameFor2(value) {
|
|
4849
|
+
return basename9(value).replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "clawhub-plugin";
|
|
4850
|
+
}
|
|
4851
|
+
function cachePathFor(packageName, cacheRoot) {
|
|
4852
|
+
const root = cacheRoot ? resolve6(cacheRoot) : join19(process.env.HOME ?? ".", ".agentwheel", "cache");
|
|
4853
|
+
const slug2 = `clawhub-${packageName}`.replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "");
|
|
4854
|
+
return join19(root, slug2 || "clawhub-package");
|
|
4855
|
+
}
|
|
4856
|
+
|
|
4566
4857
|
// src/source/git.ts
|
|
4858
|
+
import { execFile as execFile4 } from "child_process";
|
|
4859
|
+
import { cp as cp2, mkdir as mkdir11, rename as rename3, rm as rm6, writeFile as writeFile13 } from "fs/promises";
|
|
4860
|
+
import { homedir as homedir2 } from "os";
|
|
4861
|
+
import { basename as basename10, dirname as dirname13, join as join20, resolve as resolve7 } from "path";
|
|
4862
|
+
import { promisify as promisify4 } from "util";
|
|
4567
4863
|
var execFileAsync4 = promisify4(execFile4);
|
|
4568
4864
|
var GitSourceDriver = class {
|
|
4569
4865
|
name = "git";
|
|
@@ -4575,7 +4871,7 @@ var GitSourceDriver = class {
|
|
|
4575
4871
|
return {
|
|
4576
4872
|
driver: this.name,
|
|
4577
4873
|
source,
|
|
4578
|
-
resolvedPath:
|
|
4874
|
+
resolvedPath: cachePathFor2(parsed.url, options.cacheRoot),
|
|
4579
4875
|
mode,
|
|
4580
4876
|
requestedRef,
|
|
4581
4877
|
frozenLock: options.frozenLock,
|
|
@@ -4585,12 +4881,12 @@ var GitSourceDriver = class {
|
|
|
4585
4881
|
async fetch(resolved) {
|
|
4586
4882
|
return withFilesystemLock(`${resolved.resolvedPath}.lock`, resolved.cacheLockTimeoutMs ?? 3e4, async () => {
|
|
4587
4883
|
const parsed = parseGitSource(resolved.source);
|
|
4588
|
-
await
|
|
4589
|
-
if (!await pathExists(
|
|
4884
|
+
await mkdir11(resolve7(resolved.resolvedPath, ".."), { recursive: true });
|
|
4885
|
+
if (!await pathExists(join20(resolved.resolvedPath, ".git"))) {
|
|
4590
4886
|
if (resolved.frozenLock) {
|
|
4591
4887
|
throw new Error(`Frozen lock requires cached git checkout at ${resolved.resolvedPath}`);
|
|
4592
4888
|
}
|
|
4593
|
-
await
|
|
4889
|
+
await rm6(resolved.resolvedPath, { recursive: true, force: true });
|
|
4594
4890
|
await git(["clone", "--no-tags", parsed.url, resolved.resolvedPath]);
|
|
4595
4891
|
} else if (!resolved.frozenLock) {
|
|
4596
4892
|
await git(["-C", resolved.resolvedPath, "fetch", "--prune", "origin"]);
|
|
@@ -4652,65 +4948,201 @@ function parseGitSource(source) {
|
|
|
4652
4948
|
}
|
|
4653
4949
|
throw new Error(`Invalid git source: ${source}`);
|
|
4654
4950
|
}
|
|
4655
|
-
function
|
|
4656
|
-
const root = cacheRoot ?
|
|
4951
|
+
function cachePathFor2(url, cacheRoot) {
|
|
4952
|
+
const root = cacheRoot ? resolve7(cacheRoot) : join20(homedir2(), ".agentwheel", "cache");
|
|
4657
4953
|
const slug2 = url.replace(/^[a-z]+:\/\//i, "").replace(/\.git$/i, "").replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "");
|
|
4658
|
-
return
|
|
4954
|
+
return join20(root, slug2 || basename10(url));
|
|
4659
4955
|
}
|
|
4660
4956
|
async function git(args) {
|
|
4661
4957
|
return execFileAsync4("git", args, { maxBuffer: 1024 * 1024 * 10 });
|
|
4662
4958
|
}
|
|
4663
4959
|
async function snapshotCheckout(checkoutPath, commit) {
|
|
4664
|
-
const snapshotPath =
|
|
4960
|
+
const snapshotPath = join20(dirname13(checkoutPath), `${basename10(checkoutPath)}-${commit.slice(0, 12)}`);
|
|
4665
4961
|
if (await pathExists(snapshotPath)) return snapshotPath;
|
|
4666
|
-
const tempPath =
|
|
4667
|
-
await
|
|
4962
|
+
const tempPath = join20(dirname13(checkoutPath), `${basename10(snapshotPath)}.tmp-${process.pid}-${Date.now()}`);
|
|
4963
|
+
await rm6(tempPath, { recursive: true, force: true });
|
|
4668
4964
|
await cp2(checkoutPath, tempPath, { recursive: true, dereference: true });
|
|
4669
|
-
await
|
|
4965
|
+
await rm6(join20(tempPath, ".git"), { recursive: true, force: true });
|
|
4670
4966
|
try {
|
|
4671
4967
|
await rename3(tempPath, snapshotPath);
|
|
4672
4968
|
} catch (error) {
|
|
4673
4969
|
if (!isAlreadyExists2(error)) throw error;
|
|
4674
|
-
await
|
|
4970
|
+
await rm6(tempPath, { recursive: true, force: true });
|
|
4675
4971
|
return snapshotPath;
|
|
4676
4972
|
}
|
|
4677
4973
|
return snapshotPath;
|
|
4678
4974
|
}
|
|
4679
4975
|
async function withFilesystemLock(lockPath, timeoutMs, fn) {
|
|
4680
|
-
await
|
|
4976
|
+
await mkdir11(dirname13(lockPath), { recursive: true });
|
|
4681
4977
|
const started = Date.now();
|
|
4682
4978
|
while (true) {
|
|
4683
4979
|
try {
|
|
4684
|
-
await
|
|
4685
|
-
await
|
|
4980
|
+
await mkdir11(lockPath);
|
|
4981
|
+
await writeFile13(join20(lockPath, "owner.json"), JSON.stringify({ pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf8");
|
|
4686
4982
|
break;
|
|
4687
4983
|
} catch (error) {
|
|
4688
4984
|
if (!isAlreadyExists2(error)) throw error;
|
|
4689
4985
|
if (Date.now() - started > timeoutMs) {
|
|
4690
4986
|
throw new Error(`Timed out waiting for git cache lock at ${lockPath}`);
|
|
4691
4987
|
}
|
|
4692
|
-
await new Promise((
|
|
4988
|
+
await new Promise((resolve21) => setTimeout(resolve21, 50));
|
|
4693
4989
|
}
|
|
4694
4990
|
}
|
|
4695
4991
|
try {
|
|
4696
4992
|
return await fn();
|
|
4697
4993
|
} finally {
|
|
4698
|
-
await
|
|
4994
|
+
await rm6(lockPath, { recursive: true, force: true });
|
|
4699
4995
|
}
|
|
4700
4996
|
}
|
|
4701
4997
|
function isAlreadyExists2(error) {
|
|
4702
4998
|
return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
|
|
4703
4999
|
}
|
|
4704
5000
|
|
|
5001
|
+
// src/source/mcp-registry.ts
|
|
5002
|
+
import { mkdir as mkdir12, writeFile as writeFile14 } from "fs/promises";
|
|
5003
|
+
import { basename as basename11, dirname as dirname14, join as join21, resolve as resolve8 } from "path";
|
|
5004
|
+
var registryBaseUrl = "https://registry.modelcontextprotocol.io/v0.1";
|
|
5005
|
+
var sourcePrefix2 = "mcp-registry:";
|
|
5006
|
+
var McpRegistrySourceDriver = class {
|
|
5007
|
+
constructor(fetchImpl = fetch) {
|
|
5008
|
+
this.fetchImpl = fetchImpl;
|
|
5009
|
+
}
|
|
5010
|
+
fetchImpl;
|
|
5011
|
+
name = "mcp-registry";
|
|
5012
|
+
local = new LocalSourceDriver();
|
|
5013
|
+
async resolve(source, options = {}) {
|
|
5014
|
+
const serverName = parseMcpRegistrySource(source);
|
|
5015
|
+
return {
|
|
5016
|
+
driver: this.name,
|
|
5017
|
+
source,
|
|
5018
|
+
resolvedPath: cachePathFor3(serverName, options.cacheRoot),
|
|
5019
|
+
packageName: `mcp-registry/${serverName}`,
|
|
5020
|
+
mode: options.mode ?? "tracking",
|
|
5021
|
+
requestedRef: options.ref ?? "latest",
|
|
5022
|
+
frozenLock: options.frozenLock
|
|
5023
|
+
};
|
|
5024
|
+
}
|
|
5025
|
+
async fetch(resolved) {
|
|
5026
|
+
if (resolved.frozenLock) {
|
|
5027
|
+
if (!await pathExists(resolved.resolvedPath)) {
|
|
5028
|
+
throw new Error(`Frozen lock requires cached MCP registry source at ${resolved.resolvedPath}`);
|
|
5029
|
+
}
|
|
5030
|
+
return {
|
|
5031
|
+
...resolved,
|
|
5032
|
+
sourceHash: await hashPath(resolved.resolvedPath)
|
|
5033
|
+
};
|
|
5034
|
+
}
|
|
5035
|
+
const serverName = parseMcpRegistrySource(resolved.source);
|
|
5036
|
+
const response = await this.fetchImpl(`${registryBaseUrl}/servers/${encodeURIComponent(serverName)}/versions/latest`, {
|
|
5037
|
+
headers: { Accept: "application/json" }
|
|
5038
|
+
});
|
|
5039
|
+
if (!response.ok) {
|
|
5040
|
+
throw new Error(`MCP registry lookup failed for ${serverName}: HTTP ${response.status}`);
|
|
5041
|
+
}
|
|
5042
|
+
const payload = await response.json();
|
|
5043
|
+
const server = payload.server;
|
|
5044
|
+
if (!server?.name) {
|
|
5045
|
+
throw new Error(`MCP registry response missing server metadata for ${serverName}`);
|
|
5046
|
+
}
|
|
5047
|
+
const remote = supportedRemote(server.remotes ?? []);
|
|
5048
|
+
if (!remote) {
|
|
5049
|
+
throw new Error(`MCP registry server is discovery-only for Agentwheel: ${serverName}`);
|
|
5050
|
+
}
|
|
5051
|
+
await writeGeneratedPackage2(resolved.resolvedPath, {
|
|
5052
|
+
serverName: server.name,
|
|
5053
|
+
title: server.title,
|
|
5054
|
+
description: server.description,
|
|
5055
|
+
version: server.version,
|
|
5056
|
+
url: remote.url
|
|
5057
|
+
});
|
|
5058
|
+
return {
|
|
5059
|
+
...resolved,
|
|
5060
|
+
packageName: `mcp-registry/${server.name}`,
|
|
5061
|
+
packageVersion: server.version,
|
|
5062
|
+
sourceHash: await hashPath(resolved.resolvedPath)
|
|
5063
|
+
};
|
|
5064
|
+
}
|
|
5065
|
+
async list(resolved) {
|
|
5066
|
+
return this.local.list({ ...resolved, driver: "local" });
|
|
5067
|
+
}
|
|
5068
|
+
async scan(resolved) {
|
|
5069
|
+
if (!await pathExists(join21(resolved.resolvedPath, "mcp"))) {
|
|
5070
|
+
return { ok: false, findings: [{ level: "error", message: "MCP registry source has no generated mcp artifact" }] };
|
|
5071
|
+
}
|
|
5072
|
+
return { ok: true, findings: [] };
|
|
5073
|
+
}
|
|
5074
|
+
async translate(resolved) {
|
|
5075
|
+
return resolved;
|
|
5076
|
+
}
|
|
5077
|
+
async export(resolved) {
|
|
5078
|
+
return resolved;
|
|
5079
|
+
}
|
|
5080
|
+
};
|
|
5081
|
+
function parseMcpRegistrySource(source) {
|
|
5082
|
+
if (!source.startsWith(sourcePrefix2)) {
|
|
5083
|
+
throw new Error(`Invalid MCP registry source: ${source}`);
|
|
5084
|
+
}
|
|
5085
|
+
const serverName = source.slice(sourcePrefix2.length).trim();
|
|
5086
|
+
if (!serverName) throw new Error(`Invalid MCP registry source: ${source}`);
|
|
5087
|
+
return serverName;
|
|
5088
|
+
}
|
|
5089
|
+
function supportedRemote(remotes) {
|
|
5090
|
+
for (const remote of remotes ?? []) {
|
|
5091
|
+
if (remote?.type !== "streamable-http") continue;
|
|
5092
|
+
if (!isSafeHttpUrl(remote.url)) continue;
|
|
5093
|
+
if ((remote.headers ?? []).some((header) => header.isRequired && header.isSecret)) continue;
|
|
5094
|
+
return { url: remote.url };
|
|
5095
|
+
}
|
|
5096
|
+
return void 0;
|
|
5097
|
+
}
|
|
5098
|
+
function isSafeHttpUrl(value) {
|
|
5099
|
+
if (typeof value !== "string") return false;
|
|
5100
|
+
try {
|
|
5101
|
+
const url = new URL(value);
|
|
5102
|
+
return url.protocol === "https:" || url.protocol === "http:";
|
|
5103
|
+
} catch {
|
|
5104
|
+
return false;
|
|
5105
|
+
}
|
|
5106
|
+
}
|
|
5107
|
+
async function writeGeneratedPackage2(root, server) {
|
|
5108
|
+
const serverId = installNameFor3(server.serverName);
|
|
5109
|
+
const mcpPath = join21(root, "mcp", `${serverId}.json`);
|
|
5110
|
+
await mkdir12(dirname14(mcpPath), { recursive: true });
|
|
5111
|
+
await writeFile14(join21(root, "openpack.json"), `${JSON.stringify({
|
|
5112
|
+
schemaVersion: 2,
|
|
5113
|
+
name: `mcp-registry/${server.serverName}`,
|
|
5114
|
+
version: server.version ?? "latest",
|
|
5115
|
+
provides: [{ type: "mcp", path: "mcp" }]
|
|
5116
|
+
}, null, 2)}
|
|
5117
|
+
`, "utf8");
|
|
5118
|
+
await writeFile14(mcpPath, `${JSON.stringify({
|
|
5119
|
+
mcpServers: {
|
|
5120
|
+
[serverId]: {
|
|
5121
|
+
type: "streamable-http",
|
|
5122
|
+
url: server.url
|
|
5123
|
+
}
|
|
5124
|
+
}
|
|
5125
|
+
}, null, 2)}
|
|
5126
|
+
`, "utf8");
|
|
5127
|
+
}
|
|
5128
|
+
function installNameFor3(serverName) {
|
|
5129
|
+
return basename11(serverName).replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "mcp-server";
|
|
5130
|
+
}
|
|
5131
|
+
function cachePathFor3(serverName, cacheRoot) {
|
|
5132
|
+
const root = cacheRoot ? resolve8(cacheRoot) : join21(process.env.HOME ?? ".", ".agentwheel", "cache");
|
|
5133
|
+
const slug2 = `mcp-registry-${serverName}`.replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "");
|
|
5134
|
+
return join21(root, slug2 || "mcp-registry-server");
|
|
5135
|
+
}
|
|
5136
|
+
|
|
4705
5137
|
// src/source/skillkit.ts
|
|
4706
|
-
import { cp as cp3, mkdir as
|
|
5138
|
+
import { cp as cp3, mkdir as mkdir13, readFile as readFile16, rm as rm7 } from "fs/promises";
|
|
4707
5139
|
import { homedir as homedir3 } from "os";
|
|
4708
|
-
import { basename as
|
|
5140
|
+
import { basename as basename13, dirname as dirname16, join as join23, resolve as resolve9 } from "path";
|
|
4709
5141
|
import * as defaultSkillKit from "@skillkit/core";
|
|
4710
5142
|
|
|
4711
5143
|
// src/source/skill-artifacts.ts
|
|
4712
5144
|
import { readdir as readdir2, stat as stat4 } from "fs/promises";
|
|
4713
|
-
import { basename as
|
|
5145
|
+
import { basename as basename12, dirname as dirname15, extname as extname2, join as join22 } from "path";
|
|
4714
5146
|
async function artifactsFromSkillPaths(paths, packageName) {
|
|
4715
5147
|
const artifacts = [];
|
|
4716
5148
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -4732,28 +5164,28 @@ async function discoverSkillPaths(root) {
|
|
|
4732
5164
|
async function artifactFromSkillPath(item, packageName) {
|
|
4733
5165
|
const stats = await stat4(item.path);
|
|
4734
5166
|
if (stats.isDirectory()) {
|
|
4735
|
-
const skillMd =
|
|
5167
|
+
const skillMd = join22(item.path, "SKILL.md");
|
|
4736
5168
|
if (!await pathExists(skillMd)) return void 0;
|
|
4737
|
-
const name = sanitizeSkillName(item.name ??
|
|
5169
|
+
const name = sanitizeSkillName(item.name ?? basename12(item.path));
|
|
4738
5170
|
return {
|
|
4739
5171
|
type: "skills",
|
|
4740
5172
|
name,
|
|
4741
5173
|
sourcePath: item.path,
|
|
4742
|
-
relativePath:
|
|
5174
|
+
relativePath: join22("skills", name),
|
|
4743
5175
|
kind: "dir",
|
|
4744
5176
|
hash: await hashPath(item.path),
|
|
4745
5177
|
packageName,
|
|
4746
5178
|
channel: "managed"
|
|
4747
5179
|
};
|
|
4748
5180
|
}
|
|
4749
|
-
if (stats.isFile() &&
|
|
4750
|
-
const dir =
|
|
4751
|
-
const name = sanitizeSkillName(item.name ??
|
|
5181
|
+
if (stats.isFile() && basename12(item.path).toLowerCase() === "skill.md") {
|
|
5182
|
+
const dir = dirname15(item.path);
|
|
5183
|
+
const name = sanitizeSkillName(item.name ?? basename12(dir));
|
|
4752
5184
|
return {
|
|
4753
5185
|
type: "skills",
|
|
4754
5186
|
name,
|
|
4755
5187
|
sourcePath: dir,
|
|
4756
|
-
relativePath:
|
|
5188
|
+
relativePath: join22("skills", name),
|
|
4757
5189
|
kind: "dir",
|
|
4758
5190
|
hash: await hashPath(dir),
|
|
4759
5191
|
packageName,
|
|
@@ -4761,12 +5193,12 @@ async function artifactFromSkillPath(item, packageName) {
|
|
|
4761
5193
|
};
|
|
4762
5194
|
}
|
|
4763
5195
|
if (stats.isFile() && extname2(item.path).toLowerCase() === ".md") {
|
|
4764
|
-
const name = sanitizeSkillName(item.name ??
|
|
5196
|
+
const name = sanitizeSkillName(item.name ?? basename12(item.path, ".md"));
|
|
4765
5197
|
return {
|
|
4766
5198
|
type: "skills",
|
|
4767
5199
|
name,
|
|
4768
5200
|
sourcePath: item.path,
|
|
4769
|
-
relativePath:
|
|
5201
|
+
relativePath: join22("skills", `${name}.md`),
|
|
4770
5202
|
kind: "file",
|
|
4771
5203
|
hash: await hashPath(item.path),
|
|
4772
5204
|
packageName,
|
|
@@ -4784,7 +5216,7 @@ async function walk(dir, paths) {
|
|
|
4784
5216
|
}
|
|
4785
5217
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
4786
5218
|
if (!entry.isDirectory() || entry.name === ".git" || entry.name === "node_modules") continue;
|
|
4787
|
-
await walk(
|
|
5219
|
+
await walk(join22(dir, entry.name), paths);
|
|
4788
5220
|
}
|
|
4789
5221
|
}
|
|
4790
5222
|
function sanitizeSkillName(name) {
|
|
@@ -4801,12 +5233,12 @@ var SkillKitSourceDriver = class {
|
|
|
4801
5233
|
async resolve(source, options = {}) {
|
|
4802
5234
|
const spec = parseSkillKitSource(source);
|
|
4803
5235
|
if (await pathExists(spec)) {
|
|
4804
|
-
const resolvedPath =
|
|
5236
|
+
const resolvedPath = resolve9(spec);
|
|
4805
5237
|
return {
|
|
4806
5238
|
driver: this.name,
|
|
4807
5239
|
source,
|
|
4808
5240
|
resolvedPath,
|
|
4809
|
-
packageName: `skillkit/${
|
|
5241
|
+
packageName: `skillkit/${basename13(resolvedPath)}`,
|
|
4810
5242
|
mode: options.mode ?? "pinned",
|
|
4811
5243
|
sourceHash: await hashPath(resolvedPath)
|
|
4812
5244
|
};
|
|
@@ -4814,7 +5246,7 @@ var SkillKitSourceDriver = class {
|
|
|
4814
5246
|
return {
|
|
4815
5247
|
driver: this.name,
|
|
4816
5248
|
source,
|
|
4817
|
-
resolvedPath:
|
|
5249
|
+
resolvedPath: cachePathFor4(spec, options.cacheRoot),
|
|
4818
5250
|
packageName: `skillkit/${packageSlug(spec)}`,
|
|
4819
5251
|
mode: options.mode ?? "tracking",
|
|
4820
5252
|
requestedRef: options.ref,
|
|
@@ -4840,17 +5272,17 @@ var SkillKitSourceDriver = class {
|
|
|
4840
5272
|
if (!provider?.clone) {
|
|
4841
5273
|
throw new Error("SkillKit provider API unavailable or cannot resolve source. Expected @skillkit/core detectProvider().clone().");
|
|
4842
5274
|
}
|
|
4843
|
-
await
|
|
5275
|
+
await mkdir13(dirname16(resolved.resolvedPath), { recursive: true });
|
|
4844
5276
|
const result = await provider.clone(providerSpec, resolved.resolvedPath, {});
|
|
4845
5277
|
if (!result.success || !result.path) {
|
|
4846
5278
|
throw new Error(`SkillKit provider failed to fetch ${spec}: ${result.error ?? "unknown error"}`);
|
|
4847
5279
|
}
|
|
4848
|
-
if (
|
|
4849
|
-
await
|
|
5280
|
+
if (resolve9(result.path) !== resolve9(resolved.resolvedPath)) {
|
|
5281
|
+
await rm7(resolved.resolvedPath, { recursive: true, force: true });
|
|
4850
5282
|
await cp3(result.path, resolved.resolvedPath, { recursive: true, dereference: true });
|
|
4851
5283
|
}
|
|
4852
5284
|
if (result.tempRoot) {
|
|
4853
|
-
await
|
|
5285
|
+
await rm7(result.tempRoot, { recursive: true, force: true });
|
|
4854
5286
|
}
|
|
4855
5287
|
return {
|
|
4856
5288
|
...resolved,
|
|
@@ -4884,9 +5316,9 @@ var SkillKitSourceDriver = class {
|
|
|
4884
5316
|
throw new Error("SkillKit translateSkill API unavailable");
|
|
4885
5317
|
}
|
|
4886
5318
|
for (const skill of this.discover(resolved.resolvedPath)) {
|
|
4887
|
-
const skillMd =
|
|
5319
|
+
const skillMd = join23(skill.path, "SKILL.md");
|
|
4888
5320
|
if (await pathExists(skillMd)) {
|
|
4889
|
-
this.core.translateSkill(await
|
|
5321
|
+
this.core.translateSkill(await readFile16(skillMd, "utf8"), "openclaw", { sourceFilename: "SKILL.md" });
|
|
4890
5322
|
}
|
|
4891
5323
|
}
|
|
4892
5324
|
return resolved;
|
|
@@ -4914,9 +5346,9 @@ function normalizeProviderSource(spec) {
|
|
|
4914
5346
|
if (spec.startsWith("git:https://github.com/")) return spec.slice("git:".length);
|
|
4915
5347
|
return spec;
|
|
4916
5348
|
}
|
|
4917
|
-
function
|
|
4918
|
-
const root = cacheRoot ?
|
|
4919
|
-
return
|
|
5349
|
+
function cachePathFor4(spec, cacheRoot) {
|
|
5350
|
+
const root = cacheRoot ? resolve9(cacheRoot) : join23(homedir3(), ".agentwheel", "cache");
|
|
5351
|
+
return join23(root, "skillkit", packageSlug(spec));
|
|
4920
5352
|
}
|
|
4921
5353
|
function packageSlug(spec) {
|
|
4922
5354
|
return spec.replace(/^[a-z]+:\/\//i, "").replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "source";
|
|
@@ -4929,14 +5361,14 @@ function mapSeverity(severity) {
|
|
|
4929
5361
|
|
|
4930
5362
|
// src/source/vercel-skills.ts
|
|
4931
5363
|
import { stat as stat5 } from "fs/promises";
|
|
4932
|
-
import { basename as
|
|
5364
|
+
import { basename as basename14, join as join24, relative as relative5, resolve as resolve10 } from "path";
|
|
4933
5365
|
var VercelSkillsSourceDriver = class {
|
|
4934
5366
|
name = "vercel-skills";
|
|
4935
5367
|
git = new GitSourceDriver();
|
|
4936
5368
|
async resolve(source, options = {}) {
|
|
4937
5369
|
const parsed = parseVercelSource(source);
|
|
4938
5370
|
if (parsed.kind === "local") {
|
|
4939
|
-
const resolvedPath =
|
|
5371
|
+
const resolvedPath = resolve10(parsed.path);
|
|
4940
5372
|
if (!await pathExists(resolvedPath) || !(await stat5(resolvedPath)).isDirectory()) {
|
|
4941
5373
|
throw new Error(`Vercel skills local source not found: ${resolvedPath}`);
|
|
4942
5374
|
}
|
|
@@ -4944,7 +5376,7 @@ var VercelSkillsSourceDriver = class {
|
|
|
4944
5376
|
driver: this.name,
|
|
4945
5377
|
source,
|
|
4946
5378
|
resolvedPath,
|
|
4947
|
-
packageName: `vercel/${
|
|
5379
|
+
packageName: `vercel/${basename14(resolvedPath)}`,
|
|
4948
5380
|
mode: options.mode ?? "pinned",
|
|
4949
5381
|
sourceHash: await hashPath(resolvedPath)
|
|
4950
5382
|
};
|
|
@@ -4965,10 +5397,7 @@ var VercelSkillsSourceDriver = class {
|
|
|
4965
5397
|
driver: "git",
|
|
4966
5398
|
source: parsed.gitSource
|
|
4967
5399
|
});
|
|
4968
|
-
const resolvedPath =
|
|
4969
|
-
if (!await pathExists(resolvedPath)) {
|
|
4970
|
-
throw new Error(`Vercel skills subpath not found: ${parsed.subpath}`);
|
|
4971
|
-
}
|
|
5400
|
+
const resolvedPath = await resolveVercelSkillSubpath(fetched.resolvedPath, parsed.subpath);
|
|
4972
5401
|
return {
|
|
4973
5402
|
...resolved,
|
|
4974
5403
|
resolvedPath,
|
|
@@ -4993,6 +5422,15 @@ var VercelSkillsSourceDriver = class {
|
|
|
4993
5422
|
return resolved;
|
|
4994
5423
|
}
|
|
4995
5424
|
};
|
|
5425
|
+
async function resolveVercelSkillSubpath(root, subpath) {
|
|
5426
|
+
if (!subpath) return root;
|
|
5427
|
+
const candidates = [join24(root, subpath), join24(root, "skills", subpath)];
|
|
5428
|
+
for (const candidate of candidates) {
|
|
5429
|
+
if (await pathExists(candidate)) return candidate;
|
|
5430
|
+
}
|
|
5431
|
+
const tried = candidates.map((candidate) => relative5(root, candidate)).join(", ");
|
|
5432
|
+
throw new Error(`Vercel skills subpath not found: ${subpath} (tried ${tried})`);
|
|
5433
|
+
}
|
|
4996
5434
|
function parseVercelSource(source) {
|
|
4997
5435
|
if (!source.startsWith("vercel:")) {
|
|
4998
5436
|
throw new Error(`Invalid Vercel skills source: ${source}`);
|
|
@@ -5043,7 +5481,9 @@ var drivers = [
|
|
|
5043
5481
|
new LocalSourceDriver(),
|
|
5044
5482
|
new GitSourceDriver(),
|
|
5045
5483
|
new SkillKitSourceDriver(),
|
|
5046
|
-
new VercelSkillsSourceDriver()
|
|
5484
|
+
new VercelSkillsSourceDriver(),
|
|
5485
|
+
new McpRegistrySourceDriver(),
|
|
5486
|
+
new ClawHubSourceDriver()
|
|
5047
5487
|
];
|
|
5048
5488
|
function getSourceDriver(name = "local") {
|
|
5049
5489
|
const driver = drivers.find((candidate) => candidate.name === name);
|
|
@@ -5054,14 +5494,14 @@ function getSourceDriver(name = "local") {
|
|
|
5054
5494
|
}
|
|
5055
5495
|
|
|
5056
5496
|
// src/staging/staging.ts
|
|
5057
|
-
import { chmod, cp as cp5, mkdir as
|
|
5058
|
-
import { basename as
|
|
5497
|
+
import { chmod, cp as cp5, mkdir as mkdir15, mkdtemp as mkdtemp3, readdir as readdir5, stat as stat8 } from "fs/promises";
|
|
5498
|
+
import { basename as basename17, dirname as dirname19, join as join27, relative as relative7, resolve as resolve12, sep as sep2 } from "path";
|
|
5059
5499
|
import { tmpdir as tmpdir4 } from "os";
|
|
5060
5500
|
|
|
5061
5501
|
// src/compose/markdown.ts
|
|
5062
5502
|
import { createHash as createHash5 } from "crypto";
|
|
5063
|
-
import { readdir as readdir3, readFile as
|
|
5064
|
-
import { basename as
|
|
5503
|
+
import { readdir as readdir3, readFile as readFile17, stat as stat6, writeFile as writeFile15 } from "fs/promises";
|
|
5504
|
+
import { basename as basename15, dirname as dirname17, extname as extname3, join as join25, relative as relative6, resolve as resolve11, sep } from "path";
|
|
5065
5505
|
var includePattern = /<!--\s*openpack:include(\?)?\s+([^>]+?)\s*-->/g;
|
|
5066
5506
|
var escapedIncludePattern = /<!--\s*openpack\\:include(\?)?\s+([^>]+?)\s*-->/g;
|
|
5067
5507
|
var generatedPattern = /<!--\s*(?:BEGIN|END)\s+openpack:include\b/;
|
|
@@ -5076,7 +5516,7 @@ async function expandMarkdownIncludes(artifacts, packageRoot, options = {}) {
|
|
|
5076
5516
|
const composedFrom = [];
|
|
5077
5517
|
for (const file of files) {
|
|
5078
5518
|
const result = await expandFile(file, packageRoot, composeEntriesForFile(artifact, file), artifactPaths, options);
|
|
5079
|
-
if (result.changed) await
|
|
5519
|
+
if (result.changed) await writeFile15(file, result.content, "utf8");
|
|
5080
5520
|
composedFrom.push(...result.composedFrom);
|
|
5081
5521
|
}
|
|
5082
5522
|
const stagedPath = artifact.stagedPath ?? artifact.sourcePath;
|
|
@@ -5098,7 +5538,7 @@ async function validateMarkdownIncludes(artifacts, packageRoot, options = {}) {
|
|
|
5098
5538
|
}
|
|
5099
5539
|
}
|
|
5100
5540
|
async function expandFile(file, packageRoot, appendEntries, artifactPaths, options) {
|
|
5101
|
-
const raw = await
|
|
5541
|
+
const raw = await readFile17(file, "utf8");
|
|
5102
5542
|
const owner = ownerSelector(packageRoot, file, options.nodeId);
|
|
5103
5543
|
const expanded = await expandContent(raw, packageRoot, [owner], artifactPaths, options);
|
|
5104
5544
|
let content = expanded.content;
|
|
@@ -5191,7 +5631,7 @@ async function expandInclude(selector, packageRoot, artifactPaths, options) {
|
|
|
5191
5631
|
if (!stats.isFile()) {
|
|
5192
5632
|
throw new Error(`OpenPack include is not a file: ${displaySelector}`);
|
|
5193
5633
|
}
|
|
5194
|
-
const raw = sourceContent ?? await
|
|
5634
|
+
const raw = sourceContent ?? await readFile17(sourcePath, "utf8");
|
|
5195
5635
|
const { optional: _optional, markers: _markers, chain: _chain, ...childOptions } = options;
|
|
5196
5636
|
const expanded = await expandContent(raw, includePackageRoot, [...options.chain, displaySelector], includeArtifactPaths, {
|
|
5197
5637
|
...childOptions,
|
|
@@ -5249,8 +5689,8 @@ function extractOpenPackIncludeSelectors(content) {
|
|
|
5249
5689
|
return selectors;
|
|
5250
5690
|
}
|
|
5251
5691
|
function resolvePackageSelector(packageRoot, selector) {
|
|
5252
|
-
const root =
|
|
5253
|
-
const resolved =
|
|
5692
|
+
const root = resolve11(packageRoot);
|
|
5693
|
+
const resolved = resolve11(root, selector);
|
|
5254
5694
|
if (resolved !== root && !resolved.startsWith(`${root}${sep}`)) {
|
|
5255
5695
|
throw new Error(`OpenPack include escapes package root: ${selector}`);
|
|
5256
5696
|
}
|
|
@@ -5267,7 +5707,7 @@ async function listMarkdownFiles(root) {
|
|
|
5267
5707
|
const out = [];
|
|
5268
5708
|
async function walk2(dir) {
|
|
5269
5709
|
for (const entry of (await readdir3(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
5270
|
-
const full =
|
|
5710
|
+
const full = join25(dir, entry.name);
|
|
5271
5711
|
if (entry.isDirectory()) {
|
|
5272
5712
|
await walk2(full);
|
|
5273
5713
|
} else if (entry.isFile() && extname3(entry.name).toLowerCase() === ".md") {
|
|
@@ -5280,8 +5720,8 @@ async function listMarkdownFiles(root) {
|
|
|
5280
5720
|
}
|
|
5281
5721
|
function composeEntriesForFile(artifact, file) {
|
|
5282
5722
|
if (!artifact.compose?.length) return [];
|
|
5283
|
-
if (artifact.kind === "file") return [
|
|
5284
|
-
return
|
|
5723
|
+
if (artifact.kind === "file") return [resolve11(artifact.stagedPath ?? artifact.sourcePath), resolve11(file)].every(Boolean) && resolve11(artifact.stagedPath ?? artifact.sourcePath) === resolve11(file) ? artifact.compose : [];
|
|
5724
|
+
return basename15(file) === "SKILL.md" && dirname17(file) === resolve11(artifact.stagedPath ?? artifact.sourcePath) ? artifact.compose : [];
|
|
5285
5725
|
}
|
|
5286
5726
|
function orderedForExpansion(artifacts) {
|
|
5287
5727
|
return [...artifacts].sort((a, b) => Number(a.type === "fragments") - Number(b.type === "fragments"));
|
|
@@ -5298,7 +5738,7 @@ function applyReplacements(content, replacements) {
|
|
|
5298
5738
|
return out + content.slice(cursor);
|
|
5299
5739
|
}
|
|
5300
5740
|
function relativeSelector(root, file) {
|
|
5301
|
-
return
|
|
5741
|
+
return relative6(root, file).replaceAll("\\", "/");
|
|
5302
5742
|
}
|
|
5303
5743
|
function ownerSelector(root, file, nodeId) {
|
|
5304
5744
|
const selector = relativeSelector(root, file);
|
|
@@ -5327,8 +5767,8 @@ function artifactPathMap(artifacts) {
|
|
|
5327
5767
|
}
|
|
5328
5768
|
|
|
5329
5769
|
// src/staging/customize.ts
|
|
5330
|
-
import { cp as cp4, mkdir as
|
|
5331
|
-
import { dirname as
|
|
5770
|
+
import { cp as cp4, mkdir as mkdir14, readdir as readdir4, readFile as readFile18, writeFile as writeFile16 } from "fs/promises";
|
|
5771
|
+
import { dirname as dirname18, join as join26 } from "path";
|
|
5332
5772
|
async function applyCustomizations(artifacts, options) {
|
|
5333
5773
|
let next = [...artifacts];
|
|
5334
5774
|
next = await applyReplacements2(next, options, "override", installableArtifactTypes());
|
|
@@ -5344,16 +5784,16 @@ async function applyFragmentCustomizations(artifacts, options) {
|
|
|
5344
5784
|
return next.sort((a, b) => `${a.type}:${a.name}:${a.channel}`.localeCompare(`${b.type}:${b.name}:${b.channel}`));
|
|
5345
5785
|
}
|
|
5346
5786
|
async function applyInstructionOverlay(artifacts, options) {
|
|
5347
|
-
const overlayPath =
|
|
5787
|
+
const overlayPath = join26(options.workspaceRoot, ".agentwheel", "overlays", options.adapter.name, "instructions.local.md");
|
|
5348
5788
|
if (!await pathExists(overlayPath)) return artifacts;
|
|
5349
5789
|
const index = artifacts.findIndex((artifact2) => artifact2.type === "instructions");
|
|
5350
5790
|
if (index < 0) return artifacts;
|
|
5351
5791
|
const artifact = artifacts[index];
|
|
5352
|
-
const managed = await
|
|
5353
|
-
const local = await
|
|
5354
|
-
const composedPath =
|
|
5355
|
-
await
|
|
5356
|
-
await
|
|
5792
|
+
const managed = await readFile18(artifact.stagedPath ?? artifact.sourcePath, "utf8");
|
|
5793
|
+
const local = await readFile18(overlayPath, "utf8");
|
|
5794
|
+
const composedPath = join26(options.stageRoot, ".agentwheel-composed", "instructions", "AGENTS.md");
|
|
5795
|
+
await mkdir14(dirname18(composedPath), { recursive: true });
|
|
5796
|
+
await writeFile16(
|
|
5357
5797
|
composedPath,
|
|
5358
5798
|
[
|
|
5359
5799
|
"<!-- BEGIN agentwheel managed: upstream -->",
|
|
@@ -5379,19 +5819,19 @@ async function applyInstructionOverlay(artifacts, options) {
|
|
|
5379
5819
|
return [...artifacts.slice(0, index), updated, ...artifacts.slice(index + 1)];
|
|
5380
5820
|
}
|
|
5381
5821
|
async function applyAdditions(artifacts, options) {
|
|
5382
|
-
const additionsRoot =
|
|
5383
|
-
const rulesRoot =
|
|
5822
|
+
const additionsRoot = join26(options.workspaceRoot, ".agentwheel", "additions");
|
|
5823
|
+
const rulesRoot = join26(additionsRoot, "rules");
|
|
5384
5824
|
if (!await pathExists(rulesRoot)) return artifacts;
|
|
5385
5825
|
const additions = [];
|
|
5386
5826
|
for (const entry of await sortedDirEntries2(rulesRoot)) {
|
|
5387
|
-
const full =
|
|
5827
|
+
const full = join26(rulesRoot, entry.name);
|
|
5388
5828
|
if (!entry.isFile()) continue;
|
|
5389
5829
|
additions.push({
|
|
5390
5830
|
type: "rules",
|
|
5391
5831
|
name: entry.name,
|
|
5392
5832
|
sourcePath: full,
|
|
5393
5833
|
stagedPath: full,
|
|
5394
|
-
relativePath:
|
|
5834
|
+
relativePath: join26("additions", "rules", entry.name),
|
|
5395
5835
|
kind: "file",
|
|
5396
5836
|
hash: await hashPath(full),
|
|
5397
5837
|
packageName: options.packageName,
|
|
@@ -5415,17 +5855,17 @@ async function applyReplacements2(artifacts, options, channel, artifactTypes) {
|
|
|
5415
5855
|
);
|
|
5416
5856
|
}
|
|
5417
5857
|
for (const type of artifactTypes) {
|
|
5418
|
-
const typeRoot =
|
|
5858
|
+
const typeRoot = join26(root, type);
|
|
5419
5859
|
if (!await pathExists(typeRoot)) continue;
|
|
5420
5860
|
for (const entry of await sortedDirEntries2(typeRoot)) {
|
|
5421
5861
|
const artifactMapKey = `${type}:${entry.name}`;
|
|
5422
5862
|
if (seen.has(artifactMapKey)) continue;
|
|
5423
5863
|
seen.add(artifactMapKey);
|
|
5424
|
-
const full =
|
|
5864
|
+
const full = join26(typeRoot, entry.name);
|
|
5425
5865
|
const artifactKind = entry.isDirectory() ? "dir" : "file";
|
|
5426
5866
|
const existing = byKey.get(artifactMapKey);
|
|
5427
|
-
const stagedPath =
|
|
5428
|
-
await
|
|
5867
|
+
const stagedPath = join26(options.stageRoot, ".agentwheel-composed", channel, type, entry.name);
|
|
5868
|
+
await mkdir14(dirname18(stagedPath), { recursive: true });
|
|
5429
5869
|
await cp4(full, stagedPath, { recursive: artifactKind === "dir", dereference: true });
|
|
5430
5870
|
byKey.set(artifactMapKey, {
|
|
5431
5871
|
...existing,
|
|
@@ -5433,7 +5873,7 @@ async function applyReplacements2(artifacts, options, channel, artifactTypes) {
|
|
|
5433
5873
|
name: entry.name,
|
|
5434
5874
|
sourcePath: full,
|
|
5435
5875
|
stagedPath,
|
|
5436
|
-
relativePath: existing?.relativePath ??
|
|
5876
|
+
relativePath: existing?.relativePath ?? join26(type, entry.name),
|
|
5437
5877
|
kind: artifactKind,
|
|
5438
5878
|
hash: await hashPath(stagedPath),
|
|
5439
5879
|
packageName,
|
|
@@ -5448,13 +5888,13 @@ function replacementRoots(options, channel) {
|
|
|
5448
5888
|
const stateDir = channel === "override" ? "overrides" : "ejected";
|
|
5449
5889
|
const roots = [];
|
|
5450
5890
|
if (options.graphNodeId) {
|
|
5451
|
-
roots.push({ root:
|
|
5891
|
+
roots.push({ root: join26(options.workspaceRoot, ".agentwheel", stateDir, ...options.graphNodeId.split("/")), kind: "node" });
|
|
5452
5892
|
}
|
|
5453
5893
|
if (options.packageName && options.packageVersion) {
|
|
5454
|
-
roots.push({ root:
|
|
5894
|
+
roots.push({ root: join26(options.workspaceRoot, ".agentwheel", stateDir, ...`${options.packageName}@${options.packageVersion}`.split("/")), kind: "version" });
|
|
5455
5895
|
}
|
|
5456
5896
|
if (options.packageName) {
|
|
5457
|
-
roots.push({ root:
|
|
5897
|
+
roots.push({ root: join26(options.workspaceRoot, ".agentwheel", stateDir, ...options.packageName.split("/")), kind: "package" });
|
|
5458
5898
|
}
|
|
5459
5899
|
return roots;
|
|
5460
5900
|
}
|
|
@@ -5481,15 +5921,15 @@ async function stageResolvedSourceRaw(driver, resolved) {
|
|
|
5481
5921
|
return stageResolvedArtifactsRaw(resolved, artifacts);
|
|
5482
5922
|
}
|
|
5483
5923
|
async function stageResolvedArtifactsRaw(resolved, artifacts) {
|
|
5484
|
-
const root = await mkdtemp3(
|
|
5924
|
+
const root = await mkdtemp3(join27(tmpdir4(), "agentwheel-stage-"));
|
|
5485
5925
|
const stagedArtifacts = [];
|
|
5486
5926
|
for (const artifact of artifacts) {
|
|
5487
|
-
const stagedPath =
|
|
5488
|
-
await
|
|
5927
|
+
const stagedPath = join27(root, artifact.relativePath);
|
|
5928
|
+
await mkdir15(dirname19(stagedPath), { recursive: true });
|
|
5489
5929
|
await cp5(artifact.sourcePath, stagedPath, {
|
|
5490
5930
|
recursive: artifact.kind === "dir",
|
|
5491
5931
|
dereference: true,
|
|
5492
|
-
filter: (path) => !isIgnoredGeneratedEntry(
|
|
5932
|
+
filter: (path) => !isIgnoredGeneratedEntry(basename17(path))
|
|
5493
5933
|
});
|
|
5494
5934
|
await composeAssets(artifact, resolved.resolvedPath, stagedPath);
|
|
5495
5935
|
stagedArtifacts.push({
|
|
@@ -5570,16 +6010,16 @@ async function composeAssets(artifact, packageRoot, stagedPath) {
|
|
|
5570
6010
|
}
|
|
5571
6011
|
for (const asset of artifact.assets) {
|
|
5572
6012
|
const source = resolvePackagePath(packageRoot, asset.from);
|
|
5573
|
-
const dest =
|
|
6013
|
+
const dest = join27(stagedPath, asset.into);
|
|
5574
6014
|
await copyAsset(asset, source, dest);
|
|
5575
6015
|
}
|
|
5576
6016
|
}
|
|
5577
6017
|
async function copyAsset(asset, source, dest) {
|
|
5578
6018
|
const sourceStats = await stat8(source);
|
|
5579
6019
|
if (sourceStats.isFile()) {
|
|
5580
|
-
if (matchesAny(
|
|
5581
|
-
await
|
|
5582
|
-
await copyAssetFile(source,
|
|
6020
|
+
if (matchesAny(basename17(source), asset.include)) {
|
|
6021
|
+
await mkdir15(dest, { recursive: true });
|
|
6022
|
+
await copyAssetFile(source, join27(dest, basename17(source)), asset);
|
|
5583
6023
|
}
|
|
5584
6024
|
return;
|
|
5585
6025
|
}
|
|
@@ -5587,25 +6027,25 @@ async function copyAsset(asset, source, dest) {
|
|
|
5587
6027
|
throw new Error(`Asset include source is not a file or directory: ${source}`);
|
|
5588
6028
|
}
|
|
5589
6029
|
if (!asset.include?.length) {
|
|
5590
|
-
await
|
|
6030
|
+
await mkdir15(dirname19(dest), { recursive: true });
|
|
5591
6031
|
await cp5(source, dest, { recursive: true, dereference: true });
|
|
5592
6032
|
if (asset.mode === "copy") await normalizeCopiedModes(dest);
|
|
5593
6033
|
return;
|
|
5594
6034
|
}
|
|
5595
6035
|
for (const file of await listFiles(source)) {
|
|
5596
|
-
const rel =
|
|
5597
|
-
if (!matchesAny(rel, asset.include) && !matchesAny(
|
|
5598
|
-
await copyAssetFile(file,
|
|
6036
|
+
const rel = relative7(source, file).replaceAll("\\", "/");
|
|
6037
|
+
if (!matchesAny(rel, asset.include) && !matchesAny(basename17(file), asset.include)) continue;
|
|
6038
|
+
await copyAssetFile(file, join27(dest, rel), asset);
|
|
5599
6039
|
}
|
|
5600
6040
|
}
|
|
5601
6041
|
async function copyAssetFile(source, dest, asset) {
|
|
5602
|
-
await
|
|
6042
|
+
await mkdir15(dirname19(dest), { recursive: true });
|
|
5603
6043
|
await cp5(source, dest, { dereference: true });
|
|
5604
6044
|
if (asset.mode === "copy") await chmod(dest, 420);
|
|
5605
6045
|
}
|
|
5606
6046
|
function resolvePackagePath(packageRoot, path) {
|
|
5607
|
-
const resolved =
|
|
5608
|
-
const root =
|
|
6047
|
+
const resolved = resolve12(packageRoot, path);
|
|
6048
|
+
const root = resolve12(packageRoot);
|
|
5609
6049
|
if (resolved !== root && !resolved.startsWith(`${root}${sep2}`)) {
|
|
5610
6050
|
throw new Error(`Asset include escapes package root: ${path}`);
|
|
5611
6051
|
}
|
|
@@ -5615,7 +6055,7 @@ async function listFiles(root) {
|
|
|
5615
6055
|
const out = [];
|
|
5616
6056
|
async function walk2(dir) {
|
|
5617
6057
|
for (const entry of (await readdir5(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
5618
|
-
const full =
|
|
6058
|
+
const full = join27(dir, entry.name);
|
|
5619
6059
|
if (entry.isDirectory()) {
|
|
5620
6060
|
await walk2(full);
|
|
5621
6061
|
} else if (entry.isFile()) {
|
|
@@ -5634,7 +6074,7 @@ async function normalizeCopiedModes(path) {
|
|
|
5634
6074
|
}
|
|
5635
6075
|
if (!stats.isDirectory()) return;
|
|
5636
6076
|
for (const entry of await readdir5(path, { withFileTypes: true })) {
|
|
5637
|
-
await normalizeCopiedModes(
|
|
6077
|
+
await normalizeCopiedModes(join27(path, entry.name));
|
|
5638
6078
|
}
|
|
5639
6079
|
}
|
|
5640
6080
|
function matchesAny(path, patterns) {
|
|
@@ -5647,14 +6087,14 @@ function matchesGlob(path, pattern) {
|
|
|
5647
6087
|
}
|
|
5648
6088
|
|
|
5649
6089
|
// src/model/workspace.ts
|
|
5650
|
-
import { readFile as
|
|
6090
|
+
import { readFile as readFile19 } from "fs/promises";
|
|
5651
6091
|
import { homedir as homedir4 } from "os";
|
|
5652
|
-
import { dirname as
|
|
6092
|
+
import { dirname as dirname20, join as join28, resolve as resolve13 } from "path";
|
|
5653
6093
|
import { z as z6 } from "zod";
|
|
5654
6094
|
var workspacePackageSchema = z6.object({
|
|
5655
6095
|
name: z6.string().min(1),
|
|
5656
6096
|
source: z6.string().min(1),
|
|
5657
|
-
driver: z6.enum(["local", "git", "skillkit", "vercel-skills"]).default("local"),
|
|
6097
|
+
driver: z6.enum(["local", "git", "skillkit", "vercel-skills", "mcp-registry", "clawhub"]).default("local"),
|
|
5658
6098
|
adapter: z6.string().min(1).default("openclaw"),
|
|
5659
6099
|
adapterConfig: z6.string().min(1).optional(),
|
|
5660
6100
|
adapterModule: z6.string().min(1).optional(),
|
|
@@ -5664,6 +6104,8 @@ var workspacePackageSchema = z6.object({
|
|
|
5664
6104
|
requestedRef: z6.string().min(1).optional(),
|
|
5665
6105
|
select: z6.array(z6.string().min(1)).optional(),
|
|
5666
6106
|
skills: z6.array(z6.string().min(1)).optional(),
|
|
6107
|
+
withSuggestions: z6.boolean().optional(),
|
|
6108
|
+
suggestions: z6.array(z6.string().min(1)).optional(),
|
|
5667
6109
|
aliases: z6.record(z6.string(), z6.string().min(1)).optional(),
|
|
5668
6110
|
overrides: z6.array(z6.string().min(1)).optional()
|
|
5669
6111
|
});
|
|
@@ -5718,12 +6160,12 @@ var workspaceConfigSchema = z6.object({
|
|
|
5718
6160
|
agents: z6.record(z6.string(), workspaceAgentSchema).default({})
|
|
5719
6161
|
});
|
|
5720
6162
|
function workspaceConfigPath(workspaceRoot) {
|
|
5721
|
-
return
|
|
6163
|
+
return join28(workspaceRoot, ".agentwheel", "config.json");
|
|
5722
6164
|
}
|
|
5723
6165
|
async function readWorkspaceConfig(workspaceRoot) {
|
|
5724
6166
|
const path = workspaceConfigPath(workspaceRoot);
|
|
5725
6167
|
if (!await pathExists(path)) return emptyWorkspaceConfig();
|
|
5726
|
-
return workspaceConfigSchema.parse(JSON.parse(await
|
|
6168
|
+
return workspaceConfigSchema.parse(JSON.parse(await readFile19(path, "utf8")));
|
|
5727
6169
|
}
|
|
5728
6170
|
async function writeWorkspaceConfig(workspaceRoot, config) {
|
|
5729
6171
|
await writeJsonAtomic(workspaceConfigPath(workspaceRoot), workspaceConfigSchema.parse(config));
|
|
@@ -5736,14 +6178,14 @@ function upsertPackage(config, entry) {
|
|
|
5736
6178
|
return { schemaVersion: 1, packages, bootstrapSkills: parsed.bootstrapSkills, registry: parsed.registry ?? {}, trust: parsed.trust ?? {}, profiles: parsed.profiles ?? {}, agents: parsed.agents ?? {} };
|
|
5737
6179
|
}
|
|
5738
6180
|
function globalWorkspaceConfigPath(globalRoot = homedir4()) {
|
|
5739
|
-
return
|
|
6181
|
+
return join28(globalRoot, ".agentwheel", "config.json");
|
|
5740
6182
|
}
|
|
5741
6183
|
async function findWorkspaceRoot(start = process.cwd()) {
|
|
5742
|
-
let current =
|
|
6184
|
+
let current = resolve13(start);
|
|
5743
6185
|
while (true) {
|
|
5744
6186
|
if (await pathExists(workspaceConfigPath(current))) return current;
|
|
5745
|
-
const parent =
|
|
5746
|
-
if (parent === current) return
|
|
6187
|
+
const parent = dirname20(current);
|
|
6188
|
+
if (parent === current) return resolve13(start);
|
|
5747
6189
|
current = parent;
|
|
5748
6190
|
}
|
|
5749
6191
|
}
|
|
@@ -5769,16 +6211,16 @@ function mergeWorkspaceConfig(global, project) {
|
|
|
5769
6211
|
});
|
|
5770
6212
|
}
|
|
5771
6213
|
function resolveConfigPath(path, baseRoot) {
|
|
5772
|
-
if (path.startsWith("~/")) return
|
|
6214
|
+
if (path.startsWith("~/")) return resolve13(homedir4(), path.slice(2));
|
|
5773
6215
|
if (path === "~") return homedir4();
|
|
5774
|
-
return path.startsWith("/") ?
|
|
6216
|
+
return path.startsWith("/") ? resolve13(path) : resolve13(baseRoot, path);
|
|
5775
6217
|
}
|
|
5776
6218
|
function emptyWorkspaceConfig() {
|
|
5777
6219
|
return { schemaVersion: 1, packages: [], registry: {}, trust: {}, profiles: {}, agents: {} };
|
|
5778
6220
|
}
|
|
5779
6221
|
async function readConfigPath(path) {
|
|
5780
6222
|
if (!await pathExists(path)) return emptyWorkspaceConfig();
|
|
5781
|
-
return workspaceConfigSchema.parse(JSON.parse(await
|
|
6223
|
+
return workspaceConfigSchema.parse(JSON.parse(await readFile19(path, "utf8")));
|
|
5782
6224
|
}
|
|
5783
6225
|
function mergeWorkspaceTrust(global, project) {
|
|
5784
6226
|
return {
|
|
@@ -5793,23 +6235,23 @@ function sortedUnique2(values) {
|
|
|
5793
6235
|
}
|
|
5794
6236
|
|
|
5795
6237
|
// src/lifecycle/customization.ts
|
|
5796
|
-
import { appendFile, cp as cp6, mkdir as
|
|
5797
|
-
import { dirname as
|
|
6238
|
+
import { appendFile, cp as cp6, mkdir as mkdir16, rm as rm9 } from "fs/promises";
|
|
6239
|
+
import { dirname as dirname22, join as join31 } from "path";
|
|
5798
6240
|
|
|
5799
6241
|
// src/resolve/graph.ts
|
|
5800
6242
|
import { createHash as createHash6 } from "crypto";
|
|
5801
|
-
import { mkdtemp as mkdtemp4, readdir as readdir6, readFile as
|
|
6243
|
+
import { mkdtemp as mkdtemp4, readdir as readdir6, readFile as readFile21, stat as stat10 } from "fs/promises";
|
|
5802
6244
|
import { tmpdir as tmpdir5 } from "os";
|
|
5803
|
-
import { basename as
|
|
6245
|
+
import { basename as basename18, extname as extname4, join as join30 } from "path";
|
|
5804
6246
|
|
|
5805
6247
|
// src/resolve/identity.ts
|
|
5806
6248
|
import { homedir as homedir6 } from "os";
|
|
5807
|
-
import { resolve as
|
|
6249
|
+
import { resolve as resolve15 } from "path";
|
|
5808
6250
|
|
|
5809
6251
|
// src/registry/client.ts
|
|
5810
|
-
import { readFile as
|
|
6252
|
+
import { readFile as readFile20, rm as rm8, stat as stat9 } from "fs/promises";
|
|
5811
6253
|
import { homedir as homedir5 } from "os";
|
|
5812
|
-
import { dirname as
|
|
6254
|
+
import { dirname as dirname21, join as join29, resolve as resolve14 } from "path";
|
|
5813
6255
|
import { fileURLToPath } from "url";
|
|
5814
6256
|
|
|
5815
6257
|
// src/model/registry.ts
|
|
@@ -5820,6 +6262,12 @@ var registryEntrySchema = z7.object({
|
|
|
5820
6262
|
type: z7.enum(["package", "skill", "plugin", "mcp", "adapter"]).default("package"),
|
|
5821
6263
|
description: z7.string().default(""),
|
|
5822
6264
|
tags: z7.array(z7.string().min(1)).default([]),
|
|
6265
|
+
select: z7.array(z7.string().min(1)).optional(),
|
|
6266
|
+
skills: z7.array(z7.string().min(1)).optional(),
|
|
6267
|
+
homepageUrl: z7.string().min(1).optional(),
|
|
6268
|
+
homepageLinkLabel: z7.string().min(1).optional(),
|
|
6269
|
+
sourceUrl: z7.string().min(1).optional(),
|
|
6270
|
+
sourceLinkLabel: z7.string().min(1).optional(),
|
|
5823
6271
|
openpack: z7.object({
|
|
5824
6272
|
schemaVersion: z7.number().int().positive().optional(),
|
|
5825
6273
|
specVersion: z7.string().min(1).optional()
|
|
@@ -5884,7 +6332,7 @@ var RegistryClient = class {
|
|
|
5884
6332
|
);
|
|
5885
6333
|
}
|
|
5886
6334
|
async clearCache() {
|
|
5887
|
-
await
|
|
6335
|
+
await rm8(this.cachePath, { force: true });
|
|
5888
6336
|
}
|
|
5889
6337
|
async getSources() {
|
|
5890
6338
|
if (this.options.sources?.length) return this.options.sources;
|
|
@@ -5907,7 +6355,7 @@ var RegistryClient = class {
|
|
|
5907
6355
|
}
|
|
5908
6356
|
async readCache() {
|
|
5909
6357
|
if (!await pathExists(this.cachePath)) return void 0;
|
|
5910
|
-
return registryCacheSchema.parse(JSON.parse(await
|
|
6358
|
+
return registryCacheSchema.parse(JSON.parse(await readFile20(this.cachePath, "utf8")));
|
|
5911
6359
|
}
|
|
5912
6360
|
isExpired(cache, ttlMs) {
|
|
5913
6361
|
return this.now().getTime() - new Date(cache.fetchedAt).getTime() > ttlMs;
|
|
@@ -5924,12 +6372,12 @@ var RegistryClient = class {
|
|
|
5924
6372
|
}
|
|
5925
6373
|
const filePath = source.startsWith("file:") ? fileURLToPath(source) : source;
|
|
5926
6374
|
if (await pathExists(filePath)) {
|
|
5927
|
-
const fullPath =
|
|
6375
|
+
const fullPath = resolve14(filePath);
|
|
5928
6376
|
const stats = await stat9(fullPath);
|
|
5929
|
-
return
|
|
6377
|
+
return readFile20(stats.isDirectory() ? join29(fullPath, "index.json") : fullPath, "utf8");
|
|
5930
6378
|
}
|
|
5931
|
-
const resolved = await this.git.fetch(await this.git.resolve(source, { cacheRoot:
|
|
5932
|
-
return
|
|
6379
|
+
const resolved = await this.git.fetch(await this.git.resolve(source, { cacheRoot: join29(dirname21(this.cachePath), "registry-repos") }));
|
|
6380
|
+
return readFile20(join29(resolved.resolvedPath, "index.json"), "utf8");
|
|
5933
6381
|
}
|
|
5934
6382
|
warnCompatibility(entries) {
|
|
5935
6383
|
for (const entry of entries) {
|
|
@@ -5943,17 +6391,20 @@ var RegistryClient = class {
|
|
|
5943
6391
|
}
|
|
5944
6392
|
};
|
|
5945
6393
|
async function resolvePackageSource(source, workspaceRoot, options = {}) {
|
|
5946
|
-
const { isExplicitSource } = await import("./identify-
|
|
6394
|
+
const { isExplicitSource } = await import("./identify-T4RE5RBD.js");
|
|
5947
6395
|
if (await isExplicitSource(source)) return { source };
|
|
5948
6396
|
const entry = await new RegistryClient({ workspaceRoot, offline: options.offline, warn: options.warn }).resolve(source);
|
|
5949
6397
|
if (!entry) {
|
|
5950
6398
|
if (options.offline) {
|
|
5951
6399
|
throw new Error(`Offline cannot refresh registry indexes (entry not found in cache: ${source}). Run without --offline first.`);
|
|
5952
6400
|
}
|
|
5953
|
-
throw new Error(`Registry entry not found: ${source}. Use an explicit path/git/skillkit/vercel source to bypass the registry.`);
|
|
6401
|
+
throw new Error(`Registry entry not found: ${source}. Use an explicit path/git/skillkit/vercel/mcp-registry/clawhub source to bypass the registry.`);
|
|
5954
6402
|
}
|
|
5955
6403
|
return { source: entry.source, registryEntry: entry };
|
|
5956
6404
|
}
|
|
6405
|
+
function selectorsFromRegistryEntry(entry) {
|
|
6406
|
+
return normalizeArtifactSelectors(entry?.select, entry?.skills);
|
|
6407
|
+
}
|
|
5957
6408
|
function mergeIndexes(indexes) {
|
|
5958
6409
|
const merged = /* @__PURE__ */ new Map();
|
|
5959
6410
|
for (const index of indexes) {
|
|
@@ -5964,7 +6415,7 @@ function mergeIndexes(indexes) {
|
|
|
5964
6415
|
return [...merged.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
5965
6416
|
}
|
|
5966
6417
|
function defaultRegistryCachePath() {
|
|
5967
|
-
return
|
|
6418
|
+
return join29(homedir5(), ".agentwheel", "registry-cache.json");
|
|
5968
6419
|
}
|
|
5969
6420
|
function sameSources(a, b) {
|
|
5970
6421
|
return a.length === b.length && a.every((source, index) => source === b[index]);
|
|
@@ -6021,7 +6472,23 @@ async function normalizeDependencySource(source, options) {
|
|
|
6021
6472
|
driver: "vercel-skills"
|
|
6022
6473
|
};
|
|
6023
6474
|
}
|
|
6024
|
-
|
|
6475
|
+
if (trimmed.startsWith("mcp-registry:")) {
|
|
6476
|
+
const spec = normalizeLiteralProviderSpec(trimmed, "mcp-registry:");
|
|
6477
|
+
return {
|
|
6478
|
+
source: spec,
|
|
6479
|
+
normalizedSource: spec,
|
|
6480
|
+
driver: "mcp-registry"
|
|
6481
|
+
};
|
|
6482
|
+
}
|
|
6483
|
+
if (trimmed.startsWith("clawhub:")) {
|
|
6484
|
+
const spec = normalizeLiteralProviderSpec(trimmed, "clawhub:");
|
|
6485
|
+
return {
|
|
6486
|
+
source: spec,
|
|
6487
|
+
normalizedSource: spec,
|
|
6488
|
+
driver: "clawhub"
|
|
6489
|
+
};
|
|
6490
|
+
}
|
|
6491
|
+
throw new Error(`Unsupported dependency source: ${source}. Use registry:<name>, ./, ../, local:, github:, git:, skillkit:, vercel:, mcp-registry:, or clawhub:.`);
|
|
6025
6492
|
}
|
|
6026
6493
|
function isBareRegistryName(source) {
|
|
6027
6494
|
return !source.includes(":") && !isLocalSource(source);
|
|
@@ -6034,16 +6501,16 @@ function localSourcePath(source) {
|
|
|
6034
6501
|
}
|
|
6035
6502
|
function resolveLocalPath(path, declaringPackageRoot) {
|
|
6036
6503
|
if (path === "~") return homedir6();
|
|
6037
|
-
if (path.startsWith("~/")) return
|
|
6038
|
-
if (path.startsWith("/")) return
|
|
6039
|
-
return
|
|
6504
|
+
if (path.startsWith("~/")) return resolve15(homedir6(), path.slice(2));
|
|
6505
|
+
if (path.startsWith("/")) return resolve15(path);
|
|
6506
|
+
return resolve15(declaringPackageRoot, path);
|
|
6040
6507
|
}
|
|
6041
6508
|
async function normalizeRegistrySource(name, options) {
|
|
6042
6509
|
const client = options.registryClient ?? new RegistryClient({ workspaceRoot: options.workspaceRoot });
|
|
6043
6510
|
const entry = await client.resolve(name);
|
|
6044
6511
|
if (!entry) {
|
|
6045
6512
|
throw new Error(
|
|
6046
|
-
`Registry entry not found: ${name}. Bare dependency names are registry-only inside package manifests; use ./, ../, local:, github:, git:, skillkit:, or
|
|
6513
|
+
`Registry entry not found: ${name}. Bare dependency names are registry-only inside package manifests; use ./, ../, local:, github:, git:, skillkit:, vercel:, mcp-registry:, or clawhub: for explicit sources.`
|
|
6047
6514
|
);
|
|
6048
6515
|
}
|
|
6049
6516
|
const resolved = await normalizeDependencySource(entry.source, {
|
|
@@ -6102,6 +6569,11 @@ function normalizeProviderSpec(spec, declaringPackageRoot) {
|
|
|
6102
6569
|
}
|
|
6103
6570
|
return spec.trim();
|
|
6104
6571
|
}
|
|
6572
|
+
function normalizeLiteralProviderSpec(source, prefix) {
|
|
6573
|
+
const spec = source.slice(prefix.length).trim();
|
|
6574
|
+
if (!spec) throw new Error(`Provider dependency source must include a spec: ${prefix}<name>`);
|
|
6575
|
+
return `${prefix}${spec}`;
|
|
6576
|
+
}
|
|
6105
6577
|
|
|
6106
6578
|
// src/resolve/semver.ts
|
|
6107
6579
|
var semverPattern = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
@@ -6193,7 +6665,7 @@ function compareSemver(a, b) {
|
|
|
6193
6665
|
var cacheLocks = /* @__PURE__ */ new Map();
|
|
6194
6666
|
async function resolveDependencyGraph(roots, options) {
|
|
6195
6667
|
if (roots.length === 0) throw new Error("At least one graph root is required.");
|
|
6196
|
-
const graphRoot = await mkdtemp4(
|
|
6668
|
+
const graphRoot = await mkdtemp4(join30(tmpdir5(), "agentwheel-graph-"));
|
|
6197
6669
|
const fetchCache = /* @__PURE__ */ new Map();
|
|
6198
6670
|
const nodesByKey = /* @__PURE__ */ new Map();
|
|
6199
6671
|
const rootResults = [];
|
|
@@ -6213,7 +6685,9 @@ async function resolveDependencyGraph(roots, options) {
|
|
|
6213
6685
|
useLock: root.useLock ?? options.lockedResolution,
|
|
6214
6686
|
depth: 0,
|
|
6215
6687
|
optional: false,
|
|
6216
|
-
chain: [`workspace:${rootId}`]
|
|
6688
|
+
chain: [`workspace:${rootId}`],
|
|
6689
|
+
includeSuggestions: root.includeSuggestions ?? options.includeSuggestions,
|
|
6690
|
+
suggestionAliases: sortedUnique3([...root.suggestionAliases ?? [], ...options.suggestionAliases ?? []])
|
|
6217
6691
|
};
|
|
6218
6692
|
});
|
|
6219
6693
|
let iterations = 0;
|
|
@@ -6341,13 +6815,18 @@ Run without ${lockLabel === "Offline" ? "--offline" : "--frozen-lock"} first.`
|
|
|
6341
6815
|
selectionReasons: /* @__PURE__ */ new Map(),
|
|
6342
6816
|
processedNeeds: /* @__PURE__ */ new Set(),
|
|
6343
6817
|
processedPackageAliases: /* @__PURE__ */ new Set(),
|
|
6818
|
+
processedSuggestions: /* @__PURE__ */ new Set(),
|
|
6344
6819
|
depth: requirement.depth,
|
|
6345
|
-
fullPackageSelected: false
|
|
6820
|
+
fullPackageSelected: false,
|
|
6821
|
+
includeSuggestions: false,
|
|
6822
|
+
suggestionAliases: /* @__PURE__ */ new Set()
|
|
6346
6823
|
};
|
|
6347
6824
|
nodesByKey.set(nodeKey, state);
|
|
6348
6825
|
}
|
|
6349
6826
|
state.depth = Math.min(state.depth, requirement.depth);
|
|
6350
6827
|
state.fullPackageSelected = state.fullPackageSelected || requirement.select === void 0;
|
|
6828
|
+
state.includeSuggestions = state.includeSuggestions || requirement.includeSuggestions === true;
|
|
6829
|
+
for (const alias of requirement.suggestionAliases ?? []) state.suggestionAliases.add(alias);
|
|
6351
6830
|
state.requiredBy.add(requirement.requiredBy);
|
|
6352
6831
|
for (const selector of selected) addSelectedSelector(state, selector, requirement.selectionReason);
|
|
6353
6832
|
refreshNode(state);
|
|
@@ -6394,10 +6873,13 @@ Dependency chain: ${requirement.chain.join(" -> ")}`);
|
|
|
6394
6873
|
async function collectDependencyNeeds(state, fetched, options, chain) {
|
|
6395
6874
|
if (fetched.manifest?.schemaVersion !== 2) return [];
|
|
6396
6875
|
const dependencies = fetched.manifest.requires ?? {};
|
|
6876
|
+
const suggestions = fetched.manifest.suggests ?? {};
|
|
6397
6877
|
const dependencyEntries = Object.entries(dependencies).sort(([a], [b]) => a.localeCompare(b));
|
|
6878
|
+
const suggestionEntries = Object.entries(suggestions).sort(([a], [b]) => a.localeCompare(b));
|
|
6879
|
+
const suggestionOptions = suggestionOptionsForState(state, options);
|
|
6398
6880
|
const requirements = [];
|
|
6399
6881
|
if (options.noDeps) {
|
|
6400
|
-
warnNoDepsOnce(state, dependencyEntries.map(([alias]) => alias), options.warn);
|
|
6882
|
+
warnNoDepsOnce(state, [...dependencyEntries.map(([alias]) => alias), ...suggestionEntries.map(([alias]) => alias)], options.warn);
|
|
6401
6883
|
} else {
|
|
6402
6884
|
for (const [alias, dependency] of dependencyEntries) {
|
|
6403
6885
|
if (state.processedPackageAliases.has(alias)) continue;
|
|
@@ -6406,6 +6888,14 @@ async function collectDependencyNeeds(state, fetched, options, chain) {
|
|
|
6406
6888
|
if (!dependencyTargetsRuntime(dependency.runtimes, options.runtime, state.node.id, alias, options.warn)) continue;
|
|
6407
6889
|
requirements.push(dependencyRequirement(state, fetched, alias, dependency, dependency.select, chain, options.lockedResolution === true));
|
|
6408
6890
|
}
|
|
6891
|
+
for (const [alias, suggestion] of suggestionEntries) {
|
|
6892
|
+
if (state.processedSuggestions.has(alias)) continue;
|
|
6893
|
+
if (!shouldIncludeSuggestionAlias(alias, suggestionOptions, state.fullPackageSelected)) continue;
|
|
6894
|
+
if (!suggestion.select?.length && !(state.fullPackageSelected && suggestion.select === void 0) && !explicitSuggestionAlias(alias, suggestionOptions)) continue;
|
|
6895
|
+
state.processedSuggestions.add(alias);
|
|
6896
|
+
if (!dependencyTargetsRuntime(suggestion.runtimes, options.runtime, state.node.id, alias, options.warn)) continue;
|
|
6897
|
+
requirements.push(suggestionRequirement(state, fetched, alias, suggestion, suggestion.select, chain, suggestionOptions));
|
|
6898
|
+
}
|
|
6409
6899
|
}
|
|
6410
6900
|
const artifactsBySelector = new Map(fetched.artifacts.map((artifact) => [artifactSelectorKey(artifact), artifact]));
|
|
6411
6901
|
const artifactsByRelativePath = new Map(fetched.artifacts.map((artifact) => [artifact.relativePath.replaceAll("\\", "/"), artifact]));
|
|
@@ -6449,6 +6939,28 @@ async function collectDependencyNeeds(state, fetched, options, chain) {
|
|
|
6449
6939
|
}
|
|
6450
6940
|
addSelectedSelector(state, parsed.selector, `required by ${parentSelector}`);
|
|
6451
6941
|
}
|
|
6942
|
+
for (const suggestion of artifact.suggests ?? []) {
|
|
6943
|
+
const parsed = parseArtifactSuggestion(suggestion);
|
|
6944
|
+
if (!shouldIncludeSuggestionAlias(parsed.alias, suggestionOptions, true)) continue;
|
|
6945
|
+
if (!requirementTargetsRuntime(parsed.runtimes, options.runtime, `${state.node.id}:${parentSelector} -> ${parsed.raw}`, options.warn)) continue;
|
|
6946
|
+
if (options.noDeps) {
|
|
6947
|
+
warnNoDepsOnce(state, [parsed.alias], options.warn);
|
|
6948
|
+
continue;
|
|
6949
|
+
}
|
|
6950
|
+
const packageSuggestion = suggestionForAlias(suggestions, state.node.id, parsed.alias);
|
|
6951
|
+
if (!dependencyTargetsRuntime(packageSuggestion.runtimes, options.runtime, state.node.id, parsed.alias, options.warn)) continue;
|
|
6952
|
+
requirements.push(suggestionRequirement(
|
|
6953
|
+
state,
|
|
6954
|
+
fetched,
|
|
6955
|
+
parsed.alias,
|
|
6956
|
+
packageSuggestion,
|
|
6957
|
+
combinedSelectors(packageSuggestion.select, parsed.select),
|
|
6958
|
+
chain,
|
|
6959
|
+
suggestionOptions,
|
|
6960
|
+
parsed.optional || shouldTreatSuggestionAsOptional(parsed.alias, packageSuggestion, suggestionOptions),
|
|
6961
|
+
`suggested by ${parentSelector}`
|
|
6962
|
+
));
|
|
6963
|
+
}
|
|
6452
6964
|
for (const include of await collectIncludeNeeds(artifact, artifactsByRelativePath)) {
|
|
6453
6965
|
if (!include.alias) continue;
|
|
6454
6966
|
if (options.noDeps) {
|
|
@@ -6490,9 +7002,24 @@ function dependencyRequirement(state, fetched, alias, dependency, select, chain,
|
|
|
6490
7002
|
chain: [...chain, `${state.node.id}:${alias}`],
|
|
6491
7003
|
version: dependency.version,
|
|
6492
7004
|
integrity: dependency.integrity,
|
|
6493
|
-
selectionReason
|
|
7005
|
+
selectionReason,
|
|
7006
|
+
includeSuggestions: state.includeSuggestions,
|
|
7007
|
+
suggestionAliases: sortedUnique3([...state.suggestionAliases])
|
|
6494
7008
|
};
|
|
6495
7009
|
}
|
|
7010
|
+
function suggestionRequirement(state, fetched, alias, suggestion, select, chain, options, optional = shouldTreatSuggestionAsOptional(alias, suggestion, options), selectionReason) {
|
|
7011
|
+
return dependencyRequirement(
|
|
7012
|
+
state,
|
|
7013
|
+
fetched,
|
|
7014
|
+
alias,
|
|
7015
|
+
suggestion,
|
|
7016
|
+
select,
|
|
7017
|
+
chain,
|
|
7018
|
+
options.lockedResolution === true,
|
|
7019
|
+
optional,
|
|
7020
|
+
selectionReason
|
|
7021
|
+
);
|
|
7022
|
+
}
|
|
6496
7023
|
function dependencyForAlias(dependencies, nodeId, alias) {
|
|
6497
7024
|
const dependency = dependencies[alias];
|
|
6498
7025
|
if (!dependency) {
|
|
@@ -6500,6 +7027,13 @@ function dependencyForAlias(dependencies, nodeId, alias) {
|
|
|
6500
7027
|
}
|
|
6501
7028
|
return dependency;
|
|
6502
7029
|
}
|
|
7030
|
+
function suggestionForAlias(suggestions, nodeId, alias) {
|
|
7031
|
+
const suggestion = suggestions[alias];
|
|
7032
|
+
if (!suggestion) {
|
|
7033
|
+
throw new Error(`Suggestion alias not found in ${nodeId}: ${alias}`);
|
|
7034
|
+
}
|
|
7035
|
+
return suggestion;
|
|
7036
|
+
}
|
|
6503
7037
|
function warnNoDepsOnce(state, aliases, warn) {
|
|
6504
7038
|
const unique = sortedUnique3(aliases.filter(Boolean));
|
|
6505
7039
|
if (unique.length === 0 || state.processedPackageAliases.has("__noDepsWarned")) return;
|
|
@@ -6517,6 +7051,45 @@ function parseArtifactRequirement(requirement) {
|
|
|
6517
7051
|
runtimes: typeof requirement === "string" ? void 0 : requirement.runtimes
|
|
6518
7052
|
};
|
|
6519
7053
|
}
|
|
7054
|
+
function parseArtifactSuggestion(suggestion) {
|
|
7055
|
+
if (typeof suggestion === "string") {
|
|
7056
|
+
return {
|
|
7057
|
+
raw: suggestion,
|
|
7058
|
+
alias: suggestion,
|
|
7059
|
+
optional: false
|
|
7060
|
+
};
|
|
7061
|
+
}
|
|
7062
|
+
return {
|
|
7063
|
+
raw: suggestion.alias,
|
|
7064
|
+
alias: suggestion.alias,
|
|
7065
|
+
select: suggestion.select,
|
|
7066
|
+
optional: suggestion.optional === true,
|
|
7067
|
+
runtimes: suggestion.runtimes
|
|
7068
|
+
};
|
|
7069
|
+
}
|
|
7070
|
+
function shouldIncludeSuggestionAlias(alias, options, includeWhenAllSuggestions) {
|
|
7071
|
+
const aliases = new Set(options.suggestionAliases ?? []);
|
|
7072
|
+
if (aliases.has(alias)) return true;
|
|
7073
|
+
return options.includeSuggestions === true && includeWhenAllSuggestions;
|
|
7074
|
+
}
|
|
7075
|
+
function suggestionOptionsForState(state, options) {
|
|
7076
|
+
return {
|
|
7077
|
+
...options,
|
|
7078
|
+
includeSuggestions: state.includeSuggestions || options.includeSuggestions === true,
|
|
7079
|
+
suggestionAliases: sortedUnique3([...options.suggestionAliases ?? [], ...state.suggestionAliases])
|
|
7080
|
+
};
|
|
7081
|
+
}
|
|
7082
|
+
function explicitSuggestionAlias(alias, options) {
|
|
7083
|
+
return (options.suggestionAliases ?? []).includes(alias);
|
|
7084
|
+
}
|
|
7085
|
+
function shouldTreatSuggestionAsOptional(alias, suggestion, options) {
|
|
7086
|
+
if (suggestion.optional === true) return true;
|
|
7087
|
+
return options.includeSuggestions === true && !(options.suggestionAliases ?? []).includes(alias);
|
|
7088
|
+
}
|
|
7089
|
+
function combinedSelectors(base, extra) {
|
|
7090
|
+
const values = [...base ?? [], ...extra ?? []];
|
|
7091
|
+
return values.length > 0 ? sortedUnique3(values) : void 0;
|
|
7092
|
+
}
|
|
6520
7093
|
function parseDependencySelector(value) {
|
|
6521
7094
|
const cleaned = value.trim();
|
|
6522
7095
|
const slash = cleaned.indexOf("/");
|
|
@@ -6555,7 +7128,7 @@ async function collectIncludeNeeds(artifact, artifactsByRelativePath) {
|
|
|
6555
7128
|
const file = stack.shift();
|
|
6556
7129
|
if (scanned.has(file)) continue;
|
|
6557
7130
|
scanned.add(file);
|
|
6558
|
-
const content = await
|
|
7131
|
+
const content = await readFile21(file, "utf8");
|
|
6559
7132
|
for (const include of extractOpenPackIncludeSelectors(content)) {
|
|
6560
7133
|
await collectIncludeSelector(include.raw, include.optional, artifactsByRelativePath, scanned, stack, needs);
|
|
6561
7134
|
}
|
|
@@ -6598,7 +7171,7 @@ async function listMarkdownFiles2(root) {
|
|
|
6598
7171
|
const out = [];
|
|
6599
7172
|
async function walk2(dir) {
|
|
6600
7173
|
for (const entry of (await readdir6(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
6601
|
-
const full =
|
|
7174
|
+
const full = join30(dir, entry.name);
|
|
6602
7175
|
if (entry.isDirectory()) {
|
|
6603
7176
|
await walk2(full);
|
|
6604
7177
|
} else if (entry.isFile() && extname4(entry.name).toLowerCase() === ".md") {
|
|
@@ -6633,7 +7206,7 @@ async function fetchPackage(normalized, mode, options, fetchCache, refOverride)
|
|
|
6633
7206
|
const promise = (async () => {
|
|
6634
7207
|
const driver = getSourceDriver(normalized.driver);
|
|
6635
7208
|
const resolved = await driver.resolve(normalized.source, {
|
|
6636
|
-
cacheRoot: options.cacheRoot ??
|
|
7209
|
+
cacheRoot: options.cacheRoot ?? join30(options.workspaceRoot, ".agentwheel", "cache"),
|
|
6637
7210
|
mode,
|
|
6638
7211
|
ref: refOverride ?? normalized.requestedRef,
|
|
6639
7212
|
frozenLock: hardLockedCheckout
|
|
@@ -6643,7 +7216,7 @@ async function fetchPackage(normalized, mode, options, fetchCache, refOverride)
|
|
|
6643
7216
|
const exported = await driver.export(translated);
|
|
6644
7217
|
const manifest = await readPackageManifest(exported.resolvedPath);
|
|
6645
7218
|
const artifacts = await driver.list(exported);
|
|
6646
|
-
const name = manifest?.name ?? exported.packageName ??
|
|
7219
|
+
const name = manifest?.name ?? exported.packageName ?? basename18(exported.resolvedPath);
|
|
6647
7220
|
const version = manifest?.version ?? exported.packageVersion ?? "0.0.0";
|
|
6648
7221
|
const sourceHash = exported.sourceHash ?? await hashPath(exported.resolvedPath);
|
|
6649
7222
|
return {
|
|
@@ -6727,7 +7300,7 @@ function shouldCheckLockedRootSource(requirement) {
|
|
|
6727
7300
|
}
|
|
6728
7301
|
function isExplicitNonRegistrySource(source) {
|
|
6729
7302
|
const trimmed = source.trim();
|
|
6730
|
-
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:");
|
|
7303
|
+
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:") || trimmed.startsWith("mcp-registry:") || trimmed.startsWith("clawhub:");
|
|
6731
7304
|
}
|
|
6732
7305
|
function lockedRootSourceDrifted(declared, locked) {
|
|
6733
7306
|
return declared.normalizedSource !== locked.normalizedSource || declared.requestedRef !== locked.requestedRef;
|
|
@@ -6750,8 +7323,8 @@ function verifyIntegrity(integrity, sourceHash, label) {
|
|
|
6750
7323
|
async function withCachePathLock(path, fn) {
|
|
6751
7324
|
const previous = cacheLocks.get(path) ?? Promise.resolve();
|
|
6752
7325
|
let release = () => void 0;
|
|
6753
|
-
const current = previous.then(() => new Promise((
|
|
6754
|
-
release =
|
|
7326
|
+
const current = previous.then(() => new Promise((resolve21) => {
|
|
7327
|
+
release = resolve21;
|
|
6755
7328
|
}));
|
|
6756
7329
|
cacheLocks.set(path, current);
|
|
6757
7330
|
await previous;
|
|
@@ -6840,8 +7413,8 @@ async function mapLimit(items, limit, fn) {
|
|
|
6840
7413
|
|
|
6841
7414
|
// src/lifecycle/customization.ts
|
|
6842
7415
|
async function remember(workspaceRoot, runtime, text) {
|
|
6843
|
-
const overlayPath =
|
|
6844
|
-
await
|
|
7416
|
+
const overlayPath = join31(workspaceRoot, ".agentwheel", "overlays", runtime, "instructions.local.md");
|
|
7417
|
+
await mkdir16(dirname22(overlayPath), { recursive: true });
|
|
6845
7418
|
await appendFile(overlayPath, `${text.trim()}
|
|
6846
7419
|
`, "utf8");
|
|
6847
7420
|
return { overlayPath };
|
|
@@ -6864,9 +7437,9 @@ async function ejectArtifact(workspaceRoot, item) {
|
|
|
6864
7437
|
throw new Error(`Artifact not found: ${item}`);
|
|
6865
7438
|
}
|
|
6866
7439
|
const ejectedIdentity = parsed.packageIdentity === parsed.packageName ? parsed.packageIdentity : candidate.nodeId === parsed.packageIdentity ? candidate.nodeId : `${candidate.packageName}@${candidate.packageVersion}`;
|
|
6867
|
-
const ejectedPath =
|
|
6868
|
-
await
|
|
6869
|
-
await
|
|
7440
|
+
const ejectedPath = join31(workspaceRoot, ".agentwheel", "ejected", ...ejectedIdentity.split("/"), parsed.type, parsed.name);
|
|
7441
|
+
await mkdir16(dirname22(ejectedPath), { recursive: true });
|
|
7442
|
+
await rm9(ejectedPath, { recursive: true, force: true });
|
|
6870
7443
|
await cp6(artifact.stagedPath ?? artifact.sourcePath, ejectedPath, { recursive: artifact.kind === "dir", dereference: true });
|
|
6871
7444
|
return {
|
|
6872
7445
|
...parsed,
|
|
@@ -6877,7 +7450,7 @@ async function ejectArtifact(workspaceRoot, item) {
|
|
|
6877
7450
|
ejectedPath
|
|
6878
7451
|
};
|
|
6879
7452
|
} finally {
|
|
6880
|
-
await Promise.all(candidates.map((candidate) =>
|
|
7453
|
+
await Promise.all(candidates.map((candidate) => rm9(candidate.bundle.root, { recursive: true, force: true })));
|
|
6881
7454
|
}
|
|
6882
7455
|
}
|
|
6883
7456
|
function parseEjectItem(item) {
|
|
@@ -6907,7 +7480,7 @@ async function stageEjectCandidate(workspaceRoot, pkg) {
|
|
|
6907
7480
|
const adapter = pkg.adapterConfig ? await loadAdapterConfig(pkg.adapterConfig) : getAdapter(pkg.adapter);
|
|
6908
7481
|
const bundle = await stageSource(driver, normalized.source, {
|
|
6909
7482
|
adapter,
|
|
6910
|
-
cacheRoot:
|
|
7483
|
+
cacheRoot: join31(workspaceRoot, ".agentwheel", "cache"),
|
|
6911
7484
|
mode: pkg.mode,
|
|
6912
7485
|
ref: normalized.requestedRef ?? pkg.requestedRef
|
|
6913
7486
|
});
|
|
@@ -6954,12 +7527,12 @@ function ejectCommands(candidates, parsed) {
|
|
|
6954
7527
|
}
|
|
6955
7528
|
|
|
6956
7529
|
// src/lifecycle/profile.ts
|
|
6957
|
-
import { rm as
|
|
7530
|
+
import { rm as rm10 } from "fs/promises";
|
|
6958
7531
|
|
|
6959
7532
|
// src/lifecycle/source-plan.ts
|
|
6960
7533
|
import { createHash as createHash8 } from "crypto";
|
|
6961
|
-
import { mkdir as
|
|
6962
|
-
import { dirname as
|
|
7534
|
+
import { mkdir as mkdir18 } from "fs/promises";
|
|
7535
|
+
import { dirname as dirname24, join as join34, resolve as resolve16 } from "path";
|
|
6963
7536
|
|
|
6964
7537
|
// src/resolve/graph-diff.ts
|
|
6965
7538
|
function diffGraphLocks(previous, next) {
|
|
@@ -7081,11 +7654,11 @@ function short(hash) {
|
|
|
7081
7654
|
|
|
7082
7655
|
// src/resolve/render.ts
|
|
7083
7656
|
import { createHash as createHash7 } from "crypto";
|
|
7084
|
-
import { readFile as
|
|
7657
|
+
import { readFile as readFile22, mkdtemp as mkdtemp5 } from "fs/promises";
|
|
7085
7658
|
import { tmpdir as tmpdir6 } from "os";
|
|
7086
|
-
import { join as
|
|
7659
|
+
import { join as join32 } from "path";
|
|
7087
7660
|
async function renderGraphForTarget(graph, targetContext = {}) {
|
|
7088
|
-
const root = await mkdtemp5(
|
|
7661
|
+
const root = await mkdtemp5(join32(tmpdir6(), "agentwheel-render-"));
|
|
7089
7662
|
const artifacts = [];
|
|
7090
7663
|
const stagedNodes = /* @__PURE__ */ new Map();
|
|
7091
7664
|
const includeEdges = /* @__PURE__ */ new Map();
|
|
@@ -7205,7 +7778,7 @@ async function artifactContentMap(artifacts) {
|
|
|
7205
7778
|
const out = /* @__PURE__ */ new Map();
|
|
7206
7779
|
for (const artifact of artifacts) {
|
|
7207
7780
|
if (artifact.kind !== "file") continue;
|
|
7208
|
-
out.set(artifact.relativePath.replaceAll("\\", "/"), await
|
|
7781
|
+
out.set(artifact.relativePath.replaceAll("\\", "/"), await readFile22(artifact.stagedPath ?? artifact.sourcePath, "utf8"));
|
|
7209
7782
|
}
|
|
7210
7783
|
return out;
|
|
7211
7784
|
}
|
|
@@ -7468,9 +8041,9 @@ function lockArtifactFor(artifact) {
|
|
|
7468
8041
|
}
|
|
7469
8042
|
|
|
7470
8043
|
// src/lifecycle/trust.ts
|
|
7471
|
-
import { mkdir as
|
|
8044
|
+
import { mkdir as mkdir17, readFile as readFile23 } from "fs/promises";
|
|
7472
8045
|
import { homedir as homedir7 } from "os";
|
|
7473
|
-
import { dirname as
|
|
8046
|
+
import { dirname as dirname23, join as join33 } from "path";
|
|
7474
8047
|
import { z as z8 } from "zod";
|
|
7475
8048
|
var trustStoreSchema = z8.object({
|
|
7476
8049
|
version: z8.literal(1),
|
|
@@ -7544,14 +8117,14 @@ function sortedUnique4(values) {
|
|
|
7544
8117
|
}
|
|
7545
8118
|
async function readTrustStore(path) {
|
|
7546
8119
|
if (!await pathExists(path)) return { version: 1, acceptedSources: [] };
|
|
7547
|
-
return trustStoreSchema.parse(JSON.parse(await
|
|
8120
|
+
return trustStoreSchema.parse(JSON.parse(await readFile23(path, "utf8")));
|
|
7548
8121
|
}
|
|
7549
8122
|
async function writeTrustStore(path, store) {
|
|
7550
|
-
await
|
|
8123
|
+
await mkdir17(dirname23(path), { recursive: true });
|
|
7551
8124
|
await writeJsonAtomic(path, trustStoreSchema.parse(store));
|
|
7552
8125
|
}
|
|
7553
8126
|
function defaultTrustStorePath() {
|
|
7554
|
-
return process.env.AGENTWHEEL_TRUST_STORE ??
|
|
8127
|
+
return process.env.AGENTWHEEL_TRUST_STORE ?? join33(homedir7(), ".agentwheel", "trust.json");
|
|
7555
8128
|
}
|
|
7556
8129
|
|
|
7557
8130
|
// src/lifecycle/source-plan.ts
|
|
@@ -7589,9 +8162,11 @@ async function createGraphSourcePlan(options) {
|
|
|
7589
8162
|
const registryClient = new RegistryClient({ workspaceRoot, offline: lockMode, offlineLabel: lockLabel, warn });
|
|
7590
8163
|
const graph = await resolveDependencyGraph(options.roots, {
|
|
7591
8164
|
workspaceRoot,
|
|
7592
|
-
cacheRoot:
|
|
8165
|
+
cacheRoot: join34(workspaceRoot, ".agentwheel", "cache"),
|
|
7593
8166
|
registryClient,
|
|
7594
8167
|
noDeps: options.noDeps,
|
|
8168
|
+
includeSuggestions: options.includeSuggestions,
|
|
8169
|
+
suggestionAliases: options.suggestionAliases,
|
|
7595
8170
|
lockedResolution: options.lockedResolution,
|
|
7596
8171
|
frozenLock: lockMode,
|
|
7597
8172
|
offline: options.offline,
|
|
@@ -7687,7 +8262,7 @@ async function readExistingGraphLock(path) {
|
|
|
7687
8262
|
return readGraphLock(path);
|
|
7688
8263
|
}
|
|
7689
8264
|
function pathForGraphLock(workspaceRoot, targetKey, adapter, targetFingerprint) {
|
|
7690
|
-
return
|
|
8265
|
+
return join34(workspaceRoot, ".agentwheel", "locks", sanitizePathSegment(targetKey), sanitizePathSegment(adapter), `${targetFingerprint}.graph-lock.json`);
|
|
7691
8266
|
}
|
|
7692
8267
|
function sanitizePathSegment(value) {
|
|
7693
8268
|
return value.replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "default";
|
|
@@ -7696,7 +8271,7 @@ function digestGraphLock(lock) {
|
|
|
7696
8271
|
return createHash8("sha256").update(canonicalGraphLockJson(lock)).digest("hex");
|
|
7697
8272
|
}
|
|
7698
8273
|
function workspaceOwnerId(workspaceRoot) {
|
|
7699
|
-
return `workspace-root:${
|
|
8274
|
+
return `workspace-root:${resolve16(workspaceRoot)}`;
|
|
7700
8275
|
}
|
|
7701
8276
|
function assertFrozenGraph(previousLock, graph, frozen, label) {
|
|
7702
8277
|
if (!frozen) return;
|
|
@@ -7751,7 +8326,7 @@ ${sources.map((source) => `- ${source}`).join("\n")}`);
|
|
|
7751
8326
|
}
|
|
7752
8327
|
|
|
7753
8328
|
// src/runtime/target.ts
|
|
7754
|
-
import { basename as
|
|
8329
|
+
import { basename as basename19, dirname as dirname25, join as join35, resolve as resolve17 } from "path";
|
|
7755
8330
|
var runtimeMarkers = [
|
|
7756
8331
|
{ adapter: "openclaw", dirs: [".openclaw", ".clawdbot", ".moltbot"] },
|
|
7757
8332
|
{ adapter: "claude", dirs: [".claude"] },
|
|
@@ -7760,9 +8335,9 @@ var runtimeMarkers = [
|
|
|
7760
8335
|
{ adapter: "copilot", dirs: [".github"] }
|
|
7761
8336
|
];
|
|
7762
8337
|
async function resolveRuntimeTarget(request = {}) {
|
|
7763
|
-
const cwd =
|
|
8338
|
+
const cwd = resolve17(request.cwd ?? process.cwd());
|
|
7764
8339
|
if (request.targetRoot) {
|
|
7765
|
-
const targetRoot =
|
|
8340
|
+
const targetRoot = resolve17(request.targetRoot);
|
|
7766
8341
|
return {
|
|
7767
8342
|
adapter: request.adapter ?? "openclaw",
|
|
7768
8343
|
installationType: request.installationType,
|
|
@@ -7793,7 +8368,7 @@ async function resolveRuntimeTarget(request = {}) {
|
|
|
7793
8368
|
async function resolveAllRuntimeTargets(request = {}) {
|
|
7794
8369
|
if (request.targetRoot) return [await resolveRuntimeTarget(request)];
|
|
7795
8370
|
if (request.agent) return [await resolveRuntimeTarget(request)];
|
|
7796
|
-
const cwd =
|
|
8371
|
+
const cwd = resolve17(request.cwd ?? process.cwd());
|
|
7797
8372
|
const workspaceRoot = await findWorkspaceRoot(cwd);
|
|
7798
8373
|
const config = await readMergedWorkspaceConfig(workspaceRoot, { globalRoot: request.globalRoot });
|
|
7799
8374
|
const targets = Object.entries(config.agents).map(([name]) => targetFromAgent(name, config, workspaceRoot, request.installationType));
|
|
@@ -7803,8 +8378,8 @@ async function resolveAllRuntimeTargets(request = {}) {
|
|
|
7803
8378
|
return targets;
|
|
7804
8379
|
}
|
|
7805
8380
|
async function resolveProfileRuntimeTargets(request) {
|
|
7806
|
-
const cwd =
|
|
7807
|
-
const workspaceRoot = request.targetRoot ?
|
|
8381
|
+
const cwd = resolve17(request.cwd ?? process.cwd());
|
|
8382
|
+
const workspaceRoot = request.targetRoot ? resolve17(request.targetRoot) : await findWorkspaceRoot(cwd);
|
|
7808
8383
|
const config = await readMergedWorkspaceConfig(workspaceRoot, { globalRoot: request.globalRoot });
|
|
7809
8384
|
const profile = config.profiles[request.profile];
|
|
7810
8385
|
if (!profile) {
|
|
@@ -7857,14 +8432,14 @@ async function detectRuntimeTarget(cwd = process.cwd(), adapterFilter) {
|
|
|
7857
8432
|
return unique[0];
|
|
7858
8433
|
}
|
|
7859
8434
|
async function detectRuntimeTargets(cwd = process.cwd(), adapterFilter) {
|
|
7860
|
-
const root =
|
|
8435
|
+
const root = resolve17(cwd);
|
|
7861
8436
|
const matches = [];
|
|
7862
8437
|
for (const marker of runtimeMarkers) {
|
|
7863
8438
|
if (adapterFilter && marker.adapter !== adapterFilter) continue;
|
|
7864
8439
|
for (const dir of marker.dirs) {
|
|
7865
|
-
if (
|
|
7866
|
-
matches.push({ adapter: marker.adapter, targetRoot:
|
|
7867
|
-
} else if (await pathExists(
|
|
8440
|
+
if (basename19(root) === dir) {
|
|
8441
|
+
matches.push({ adapter: marker.adapter, targetRoot: dirname25(root) });
|
|
8442
|
+
} else if (await pathExists(join35(root, dir))) {
|
|
7868
8443
|
matches.push({ adapter: marker.adapter, targetRoot: root });
|
|
7869
8444
|
}
|
|
7870
8445
|
}
|
|
@@ -7901,9 +8476,9 @@ function dedupeTargets(matches) {
|
|
|
7901
8476
|
return [...byKey.values()];
|
|
7902
8477
|
}
|
|
7903
8478
|
function runtimeScanRoot(request) {
|
|
7904
|
-
const root =
|
|
8479
|
+
const root = resolve17(request.targetRoot ?? request.cwd ?? process.cwd());
|
|
7905
8480
|
if (request.targetRoot) return root;
|
|
7906
|
-
return runtimeMarkers.some((marker) => marker.dirs.includes(
|
|
8481
|
+
return runtimeMarkers.some((marker) => marker.dirs.includes(basename19(root))) ? dirname25(root) : root;
|
|
7907
8482
|
}
|
|
7908
8483
|
|
|
7909
8484
|
// src/lifecycle/profile.ts
|
|
@@ -7940,7 +8515,9 @@ async function syncProfile(options) {
|
|
|
7940
8515
|
ref: pkg.requestedRef,
|
|
7941
8516
|
select: selected ?? normalizeArtifactSelectors(pkg.select, pkg.skills),
|
|
7942
8517
|
aliases: pkg.aliases,
|
|
7943
|
-
overrides: pkg.overrides
|
|
8518
|
+
overrides: pkg.overrides,
|
|
8519
|
+
includeSuggestions: options.includeSuggestions === true || pkg.withSuggestions === true,
|
|
8520
|
+
suggestionAliases: combinedSuggestionAliases(pkg.suggestions, options.suggestionAliases)
|
|
7944
8521
|
})),
|
|
7945
8522
|
targetRoot: target.targetRoot,
|
|
7946
8523
|
workspaceRoot: options.workspaceRoot,
|
|
@@ -7960,6 +8537,8 @@ async function syncProfile(options) {
|
|
|
7960
8537
|
},
|
|
7961
8538
|
installationType,
|
|
7962
8539
|
noDeps: options.noDeps,
|
|
8540
|
+
includeSuggestions: options.includeSuggestions,
|
|
8541
|
+
suggestionAliases: options.suggestionAliases,
|
|
7963
8542
|
lockedResolution: options.lockedResolution,
|
|
7964
8543
|
frozenLock: options.frozenLock,
|
|
7965
8544
|
offline: options.offline,
|
|
@@ -7989,7 +8568,7 @@ async function syncProfile(options) {
|
|
|
7989
8568
|
});
|
|
7990
8569
|
}
|
|
7991
8570
|
} finally {
|
|
7992
|
-
await
|
|
8571
|
+
await rm10(graphPlan.bundle.root, { recursive: true, force: true });
|
|
7993
8572
|
}
|
|
7994
8573
|
}
|
|
7995
8574
|
return results;
|
|
@@ -8000,6 +8579,7 @@ async function packageFromSource(source, options) {
|
|
|
8000
8579
|
warn: options.warn
|
|
8001
8580
|
});
|
|
8002
8581
|
const driver = options.driver ?? inferSourceDriverName(resolved.source);
|
|
8582
|
+
const selectedArtifacts = normalizeArtifactSelectors(options.select, options.skills) ?? selectorsFromRegistryEntry(resolved.registryEntry);
|
|
8003
8583
|
return {
|
|
8004
8584
|
name: resolved.registryEntry?.name ?? source,
|
|
8005
8585
|
source: resolved.source,
|
|
@@ -8007,15 +8587,184 @@ async function packageFromSource(source, options) {
|
|
|
8007
8587
|
adapter: "openclaw",
|
|
8008
8588
|
installationType: options.installationType,
|
|
8009
8589
|
mode: options.mode ?? "pinned",
|
|
8010
|
-
select:
|
|
8011
|
-
|
|
8590
|
+
select: selectedArtifacts,
|
|
8591
|
+
withSuggestions: options.includeSuggestions === true ? true : void 0,
|
|
8592
|
+
suggestions: options.suggestionAliases
|
|
8012
8593
|
};
|
|
8013
8594
|
}
|
|
8595
|
+
function combinedSuggestionAliases(packageAliases, optionAliases) {
|
|
8596
|
+
const aliases = [...packageAliases ?? [], ...optionAliases ?? []].map((item) => item.trim()).filter(Boolean);
|
|
8597
|
+
return aliases.length > 0 ? [...new Set(aliases)].sort((a, b) => a.localeCompare(b)) : void 0;
|
|
8598
|
+
}
|
|
8599
|
+
|
|
8600
|
+
// src/registry/publish.ts
|
|
8601
|
+
var DEFAULT_REGISTRY_SUBMISSION_URL = "https://github.com/NestDevLab/agentwheel-registry/issues/new";
|
|
8602
|
+
var registryEntryTypes = ["package", "skill", "plugin", "mcp", "adapter"];
|
|
8603
|
+
var explicitSourcePrefixes = ["github:", "git:", "skillkit:", "vercel:", "mcp-registry:", "clawhub:"];
|
|
8604
|
+
function createRegistryPublishDraft(sourceInput, options = {}) {
|
|
8605
|
+
const source = normalizeCatalogueSource(sourceInput);
|
|
8606
|
+
const entry = {
|
|
8607
|
+
name: normalizeRegistryName(options.name ?? inferRegistryName(source)),
|
|
8608
|
+
source,
|
|
8609
|
+
type: options.type ? parseRegistryEntryType(options.type) : inferRegistryEntryType(source),
|
|
8610
|
+
description: options.description?.trim() ?? "",
|
|
8611
|
+
tags: normalizeTags(options.tags ?? [])
|
|
8612
|
+
};
|
|
8613
|
+
const selectors = normalizeArtifactSelectors(options.select, options.skills);
|
|
8614
|
+
if (selectors?.length) entry.select = selectors;
|
|
8615
|
+
if (options.skills?.length) entry.skills = normalizeSkillNames(options.skills);
|
|
8616
|
+
const installCommand = installCommandForEntry(entry);
|
|
8617
|
+
return {
|
|
8618
|
+
entry,
|
|
8619
|
+
installCommand,
|
|
8620
|
+
issueUrl: registrySubmissionUrl(entry, installCommand, options.submissionUrl ?? DEFAULT_REGISTRY_SUBMISSION_URL)
|
|
8621
|
+
};
|
|
8622
|
+
}
|
|
8623
|
+
function normalizeCatalogueSource(sourceInput) {
|
|
8624
|
+
const source = sourceInput.trim();
|
|
8625
|
+
if (!source) throw new Error("Catalogue source is required.");
|
|
8626
|
+
const unprefixedGitUrl = source.startsWith("git+http://") || source.startsWith("git+https://") ? source.slice("git+".length) : source;
|
|
8627
|
+
const githubSource = normalizeGitHubUrl(unprefixedGitUrl);
|
|
8628
|
+
if (githubSource) return githubSource;
|
|
8629
|
+
if (isHttpUrl(unprefixedGitUrl)) return `git:${unprefixedGitUrl}`;
|
|
8630
|
+
if (explicitSourcePrefixes.some((prefix) => source.startsWith(prefix))) return source;
|
|
8631
|
+
if (source.startsWith(".") || source.startsWith("/") || source.startsWith("local:")) {
|
|
8632
|
+
throw new Error("Catalogue submissions must use a public source, not a local path.");
|
|
8633
|
+
}
|
|
8634
|
+
const shorthand = normalizeOwnerRepoShorthand(source);
|
|
8635
|
+
if (shorthand) return shorthand;
|
|
8636
|
+
throw new Error(`Unsupported catalogue source: ${sourceInput}. Use a GitHub URL, github:owner/repo, git:https://..., skillkit:, vercel:, mcp-registry:, or clawhub:.`);
|
|
8637
|
+
}
|
|
8638
|
+
function inferRegistryEntryType(source) {
|
|
8639
|
+
if (source.startsWith("mcp-registry:")) return "mcp";
|
|
8640
|
+
if (source.startsWith("clawhub:")) return "plugin";
|
|
8641
|
+
if (source.startsWith("skillkit:") || source.startsWith("vercel:")) return "skill";
|
|
8642
|
+
return "package";
|
|
8643
|
+
}
|
|
8644
|
+
function parseRegistryEntryType(value) {
|
|
8645
|
+
const normalized = value.trim().toLowerCase();
|
|
8646
|
+
if (registryEntryTypes.includes(normalized)) return normalized;
|
|
8647
|
+
throw new Error(`Unsupported registry entry type: ${value}. Use ${registryEntryTypes.join(", ")}.`);
|
|
8648
|
+
}
|
|
8649
|
+
function installCommandForEntry(entry) {
|
|
8650
|
+
const base = ["agentwheel", "install", entry.source, ...selectorArgsForEntry(entry)];
|
|
8651
|
+
if (entry.type === "mcp") return [...base, "--adapter", "claude", "--local", "--dry-run"].map(shellQuoteArg).join(" ");
|
|
8652
|
+
if (entry.source.startsWith("clawhub:") || entry.type === "plugin") {
|
|
8653
|
+
return [...base, "--adapter", "openclaw", "--local", "--dry-run"].map(shellQuoteArg).join(" ");
|
|
8654
|
+
}
|
|
8655
|
+
return [...base, "--adapter", "codex", "--local", "--dry-run"].map(shellQuoteArg).join(" ");
|
|
8656
|
+
}
|
|
8657
|
+
function registrySubmissionUrl(entry, installCommand, baseUrl) {
|
|
8658
|
+
const url = new URL(baseUrl);
|
|
8659
|
+
url.searchParams.set("title", `Catalogue submission: ${entry.name}`);
|
|
8660
|
+
url.searchParams.set("body", registrySubmissionBody(entry, installCommand));
|
|
8661
|
+
return url.toString();
|
|
8662
|
+
}
|
|
8663
|
+
function registrySubmissionBody(entry, installCommand) {
|
|
8664
|
+
const descriptionNote = entry.description ? [] : ["", "Note: add a concise description before submitting."];
|
|
8665
|
+
return [
|
|
8666
|
+
"## Agentwheel catalogue submission",
|
|
8667
|
+
"",
|
|
8668
|
+
"Please review this generated registry entry:",
|
|
8669
|
+
"",
|
|
8670
|
+
"```json",
|
|
8671
|
+
JSON.stringify(entry, null, 2),
|
|
8672
|
+
"```",
|
|
8673
|
+
"",
|
|
8674
|
+
"## Verification",
|
|
8675
|
+
"",
|
|
8676
|
+
`Source: \`${entry.source}\``,
|
|
8677
|
+
`Suggested check: \`${installCommand}\``,
|
|
8678
|
+
"",
|
|
8679
|
+
"## Checklist",
|
|
8680
|
+
"",
|
|
8681
|
+
"- [ ] The source is public and installable.",
|
|
8682
|
+
"- [ ] The description is concise and factual.",
|
|
8683
|
+
"- [ ] Tags help discovery.",
|
|
8684
|
+
...descriptionNote
|
|
8685
|
+
].join("\n");
|
|
8686
|
+
}
|
|
8687
|
+
function normalizeGitHubUrl(value) {
|
|
8688
|
+
let url;
|
|
8689
|
+
try {
|
|
8690
|
+
url = new URL(value);
|
|
8691
|
+
} catch {
|
|
8692
|
+
return void 0;
|
|
8693
|
+
}
|
|
8694
|
+
if (url.hostname.toLowerCase() !== "github.com") return void 0;
|
|
8695
|
+
const segments = url.pathname.split("/").filter(Boolean).map((segment) => decodeURIComponent(segment));
|
|
8696
|
+
const [owner, repoSegment] = segments;
|
|
8697
|
+
if (!owner || !repoSegment) return void 0;
|
|
8698
|
+
const repo = repoSegment.replace(/\.git$/i, "");
|
|
8699
|
+
let ref = url.hash ? decodeURIComponent(url.hash.slice(1)) : "";
|
|
8700
|
+
if (segments[2] === "tree" && segments.length > 3) {
|
|
8701
|
+
ref = segments.slice(3).join("/");
|
|
8702
|
+
}
|
|
8703
|
+
return `github:${owner}/${repo}${ref ? `#${ref}` : ""}`;
|
|
8704
|
+
}
|
|
8705
|
+
function normalizeOwnerRepoShorthand(value) {
|
|
8706
|
+
const match = /^([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)(#[^\s]+)?$/.exec(value);
|
|
8707
|
+
if (!match) return void 0;
|
|
8708
|
+
return `github:${match[1]}/${match[2]}${match[3] ?? ""}`;
|
|
8709
|
+
}
|
|
8710
|
+
function inferRegistryName(source) {
|
|
8711
|
+
const withoutRef = source.split("#", 1)[0];
|
|
8712
|
+
if (withoutRef.startsWith("github:")) return lastPathSegment(withoutRef.slice("github:".length));
|
|
8713
|
+
if (withoutRef.startsWith("git:")) return nameFromGitUrl(withoutRef.slice("git:".length));
|
|
8714
|
+
if (withoutRef.startsWith("skillkit:")) return lastPathSegment(withoutRef.slice("skillkit:".length));
|
|
8715
|
+
if (withoutRef.startsWith("vercel:")) return lastPathSegment(withoutRef.slice("vercel:".length));
|
|
8716
|
+
if (withoutRef.startsWith("mcp-registry:")) return lastPathSegment(withoutRef.slice("mcp-registry:".length));
|
|
8717
|
+
if (withoutRef.startsWith("clawhub:")) return lastPathSegment(withoutRef.slice("clawhub:".length));
|
|
8718
|
+
return withoutRef;
|
|
8719
|
+
}
|
|
8720
|
+
function nameFromGitUrl(value) {
|
|
8721
|
+
try {
|
|
8722
|
+
return lastPathSegment(new URL(value).pathname);
|
|
8723
|
+
} catch {
|
|
8724
|
+
return lastPathSegment(value);
|
|
8725
|
+
}
|
|
8726
|
+
}
|
|
8727
|
+
function lastPathSegment(value) {
|
|
8728
|
+
const trimmed = value.replace(/\.git$/i, "").replace(/\/+$/g, "");
|
|
8729
|
+
const segments = trimmed.split("/").filter(Boolean);
|
|
8730
|
+
return segments.at(-1) ?? trimmed;
|
|
8731
|
+
}
|
|
8732
|
+
function normalizeRegistryName(value) {
|
|
8733
|
+
const name = slugify(value);
|
|
8734
|
+
if (!name) throw new Error("Registry entry name could not be inferred. Pass --name <short-name>.");
|
|
8735
|
+
return name;
|
|
8736
|
+
}
|
|
8737
|
+
function normalizeTags(tags) {
|
|
8738
|
+
const normalized = tags.flatMap((tag) => tag.split(",")).map((tag) => slugify(tag)).filter(Boolean);
|
|
8739
|
+
return [...new Set(normalized)];
|
|
8740
|
+
}
|
|
8741
|
+
function normalizeSkillNames(skills) {
|
|
8742
|
+
return [...new Set(skills.flatMap((skill) => skill.split(",")).map((skill) => skill.trim()).filter(Boolean))];
|
|
8743
|
+
}
|
|
8744
|
+
function selectorArgsForEntry(entry) {
|
|
8745
|
+
if (entry.skills?.length) return entry.skills.flatMap((skill) => ["--skill", skill]);
|
|
8746
|
+
return (entry.select ?? []).flatMap((selector) => ["--select", selector]);
|
|
8747
|
+
}
|
|
8748
|
+
function slugify(value) {
|
|
8749
|
+
return value.trim().toLowerCase().replace(/\.git$/i, "").replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
8750
|
+
}
|
|
8751
|
+
function isHttpUrl(value) {
|
|
8752
|
+
try {
|
|
8753
|
+
const url = new URL(value);
|
|
8754
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
8755
|
+
} catch {
|
|
8756
|
+
return false;
|
|
8757
|
+
}
|
|
8758
|
+
}
|
|
8759
|
+
function shellQuoteArg(value) {
|
|
8760
|
+
if (/^[A-Za-z0-9_./:@=-]+$/.test(value)) return value;
|
|
8761
|
+
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
8762
|
+
}
|
|
8014
8763
|
|
|
8015
8764
|
// src/cli/update-check.ts
|
|
8016
|
-
import { mkdir as
|
|
8765
|
+
import { mkdir as mkdir19, readFile as readFile24, writeFile as writeFile17 } from "fs/promises";
|
|
8017
8766
|
import { homedir as homedir8 } from "os";
|
|
8018
|
-
import { dirname as
|
|
8767
|
+
import { dirname as dirname26, join as join36 } from "path";
|
|
8019
8768
|
var DEFAULT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
8020
8769
|
var DEFAULT_TIMEOUT_MS = 300;
|
|
8021
8770
|
var REGISTRY_URL = "https://registry.npmjs.org/agentwheel";
|
|
@@ -8023,7 +8772,7 @@ async function maybeCheckForUpdate(options) {
|
|
|
8023
8772
|
if (isDisabled(options)) return;
|
|
8024
8773
|
const now = options.now?.() ?? /* @__PURE__ */ new Date();
|
|
8025
8774
|
const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
|
|
8026
|
-
const cachePath = options.cachePath ??
|
|
8775
|
+
const cachePath = options.cachePath ?? join36(homedir8(), ".agentwheel", "update-check.json");
|
|
8027
8776
|
try {
|
|
8028
8777
|
const cached = await readCache(cachePath);
|
|
8029
8778
|
if (cached && now.getTime() - Date.parse(cached.checkedAt) < ttlMs) {
|
|
@@ -8060,7 +8809,7 @@ async function fetchLatestVersion(fetchImpl, timeoutMs) {
|
|
|
8060
8809
|
}
|
|
8061
8810
|
async function readCache(path) {
|
|
8062
8811
|
try {
|
|
8063
|
-
const parsed = JSON.parse(await
|
|
8812
|
+
const parsed = JSON.parse(await readFile24(path, "utf8"));
|
|
8064
8813
|
if (typeof parsed.checkedAt !== "string" || typeof parsed.latest !== "string") return void 0;
|
|
8065
8814
|
return { checkedAt: parsed.checkedAt, latest: parsed.latest };
|
|
8066
8815
|
} catch {
|
|
@@ -8068,8 +8817,8 @@ async function readCache(path) {
|
|
|
8068
8817
|
}
|
|
8069
8818
|
}
|
|
8070
8819
|
async function writeCache(path, cache) {
|
|
8071
|
-
await
|
|
8072
|
-
await
|
|
8820
|
+
await mkdir19(dirname26(path), { recursive: true });
|
|
8821
|
+
await writeFile17(path, `${JSON.stringify(cache, null, 2)}
|
|
8073
8822
|
`, "utf8");
|
|
8074
8823
|
}
|
|
8075
8824
|
function warnIfNewer(latest, current, stderr = process.stderr) {
|
|
@@ -8092,9 +8841,9 @@ function normalizeVersion(version) {
|
|
|
8092
8841
|
|
|
8093
8842
|
// src/model/package-validate.ts
|
|
8094
8843
|
import { stat as stat11 } from "fs/promises";
|
|
8095
|
-
import { resolve as
|
|
8844
|
+
import { resolve as resolve18 } from "path";
|
|
8096
8845
|
async function validatePackage(root) {
|
|
8097
|
-
const packageRoot =
|
|
8846
|
+
const packageRoot = resolve18(root);
|
|
8098
8847
|
const findings = [];
|
|
8099
8848
|
const manifestPath = await findPackageManifestPath(packageRoot);
|
|
8100
8849
|
if (!manifestPath) {
|
|
@@ -8141,6 +8890,14 @@ function validateDeclaredSelectors(manifest, findings, manifestPath) {
|
|
|
8141
8890
|
validateSelector(selector, `requires.${alias}.select`, findings, manifestPath, { localOnly: true });
|
|
8142
8891
|
}
|
|
8143
8892
|
}
|
|
8893
|
+
for (const [alias, suggestion] of Object.entries(manifest.suggests ?? {})) {
|
|
8894
|
+
if (!alias.trim()) {
|
|
8895
|
+
findings.push({ level: "error", message: "Suggestion alias must be non-empty", path: manifestPath });
|
|
8896
|
+
}
|
|
8897
|
+
for (const selector of suggestion.select ?? []) {
|
|
8898
|
+
validateSelector(selector, `suggests.${alias}.select`, findings, manifestPath, { localOnly: true });
|
|
8899
|
+
}
|
|
8900
|
+
}
|
|
8144
8901
|
}
|
|
8145
8902
|
for (const [provideIndex, provide] of manifest.provides.entries()) {
|
|
8146
8903
|
if (!("items" in provide) || !provide.items) continue;
|
|
@@ -8149,6 +8906,15 @@ function validateDeclaredSelectors(manifest, findings, manifestPath) {
|
|
|
8149
8906
|
const selector = typeof requirement === "string" ? requirement : requirement.selector;
|
|
8150
8907
|
validateSelector(selector, `provides[${provideIndex}].items.${itemName}.requires`, findings, manifestPath, { aliases: manifest.schemaVersion === 2 ? Object.keys(manifest.requires ?? {}) : [] });
|
|
8151
8908
|
}
|
|
8909
|
+
for (const suggestion of item.suggests ?? []) {
|
|
8910
|
+
const alias = typeof suggestion === "string" ? suggestion : suggestion.alias;
|
|
8911
|
+
if (manifest.schemaVersion === 2 && !Object.keys(manifest.suggests ?? {}).includes(alias)) {
|
|
8912
|
+
findings.push({ level: "error", message: `provides[${provideIndex}].items.${itemName}.suggests: suggestion alias not declared: ${alias}`, path: manifestPath });
|
|
8913
|
+
}
|
|
8914
|
+
for (const selector of typeof suggestion === "string" ? [] : suggestion.select ?? []) {
|
|
8915
|
+
validateSelector(selector, `provides[${provideIndex}].items.${itemName}.suggests.${alias}.select`, findings, manifestPath, { localOnly: true });
|
|
8916
|
+
}
|
|
8917
|
+
}
|
|
8152
8918
|
for (const entry of item.compose ?? []) {
|
|
8153
8919
|
validateSelector(entry.include, `provides[${provideIndex}].items.${itemName}.compose.include`, findings, manifestPath, {
|
|
8154
8920
|
aliases: manifest.schemaVersion === 2 ? Object.keys(manifest.requires ?? {}) : [],
|
|
@@ -8162,7 +8928,7 @@ async function validateManifestComposeInclude(packageRoot, selector, optional, f
|
|
|
8162
8928
|
try {
|
|
8163
8929
|
validateSelector(selector, "compose.include", findings, manifestPath, { fragmentsOnly: true, aliases });
|
|
8164
8930
|
if (isCrossPackageSelector(selector)) return;
|
|
8165
|
-
const full =
|
|
8931
|
+
const full = resolve18(packageRoot, selector);
|
|
8166
8932
|
if (full !== packageRoot && !full.startsWith(`${packageRoot}/`)) {
|
|
8167
8933
|
findings.push({ level: "error", message: `Compose include escapes package root: ${selector}`, path: manifestPath });
|
|
8168
8934
|
return;
|
|
@@ -8215,13 +8981,13 @@ function isCrossPackageSelector(value) {
|
|
|
8215
8981
|
}
|
|
8216
8982
|
|
|
8217
8983
|
// src/model/package-migrate.ts
|
|
8218
|
-
import { readFile as
|
|
8219
|
-
import { join as
|
|
8984
|
+
import { readFile as readFile25, rename as rename4, writeFile as writeFile18 } from "fs/promises";
|
|
8985
|
+
import { join as join38, resolve as resolve19 } from "path";
|
|
8220
8986
|
import { applyEdits, modify, parse as parse4 } from "jsonc-parser";
|
|
8221
8987
|
async function migratePackageManifest(root) {
|
|
8222
|
-
const packageRoot =
|
|
8988
|
+
const packageRoot = resolve19(root);
|
|
8223
8989
|
for (const name of openPackManifestNames) {
|
|
8224
|
-
const path =
|
|
8990
|
+
const path = join38(packageRoot, name);
|
|
8225
8991
|
if (await pathExists(path)) {
|
|
8226
8992
|
return { changed: false, to: path, message: `Package already uses ${name}.` };
|
|
8227
8993
|
}
|
|
@@ -8230,18 +8996,18 @@ async function migratePackageManifest(root) {
|
|
|
8230
8996
|
if (!legacyName) {
|
|
8231
8997
|
throw new Error(`No legacy package manifest found at ${packageRoot}`);
|
|
8232
8998
|
}
|
|
8233
|
-
const from =
|
|
8999
|
+
const from = join38(packageRoot, legacyName);
|
|
8234
9000
|
const toName = legacyName.endsWith(".jsonc") ? "openpack.jsonc" : "openpack.json";
|
|
8235
|
-
const to =
|
|
8236
|
-
const content = await
|
|
9001
|
+
const to = join38(packageRoot, toName);
|
|
9002
|
+
const content = await readFile25(from, "utf8");
|
|
8237
9003
|
const updated = updateSchemaVersion(content);
|
|
8238
9004
|
await rename4(from, to);
|
|
8239
|
-
await
|
|
9005
|
+
await writeFile18(to, updated, "utf8");
|
|
8240
9006
|
return { changed: true, from, to, message: `Migrated ${legacyName} to ${toName}.` };
|
|
8241
9007
|
}
|
|
8242
9008
|
async function firstExistingLegacyManifest(root) {
|
|
8243
9009
|
for (const name of legacyPackageManifestNames) {
|
|
8244
|
-
if (await pathExists(
|
|
9010
|
+
if (await pathExists(join38(root, name))) return name;
|
|
8245
9011
|
}
|
|
8246
9012
|
return void 0;
|
|
8247
9013
|
}
|
|
@@ -8259,20 +9025,20 @@ function updateSchemaVersion(content) {
|
|
|
8259
9025
|
|
|
8260
9026
|
// src/cli/version.ts
|
|
8261
9027
|
import { readFileSync } from "fs";
|
|
8262
|
-
import { dirname as
|
|
9028
|
+
import { dirname as dirname27, join as join39 } from "path";
|
|
8263
9029
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8264
9030
|
var FALLBACK_VERSION = "0.0.0";
|
|
8265
9031
|
function resolveCliVersion() {
|
|
8266
|
-
let dir =
|
|
9032
|
+
let dir = dirname27(fileURLToPath2(import.meta.url));
|
|
8267
9033
|
while (true) {
|
|
8268
9034
|
try {
|
|
8269
|
-
const pkg = JSON.parse(readFileSync(
|
|
9035
|
+
const pkg = JSON.parse(readFileSync(join39(dir, "package.json"), "utf8"));
|
|
8270
9036
|
if (pkg.name === "agentwheel" && typeof pkg.version === "string") {
|
|
8271
9037
|
return pkg.version;
|
|
8272
9038
|
}
|
|
8273
9039
|
} catch {
|
|
8274
9040
|
}
|
|
8275
|
-
const parent =
|
|
9041
|
+
const parent = dirname27(dir);
|
|
8276
9042
|
if (parent === dir) return FALLBACK_VERSION;
|
|
8277
9043
|
dir = parent;
|
|
8278
9044
|
}
|
|
@@ -8308,7 +9074,7 @@ program.command("init").description("initialize an agentwheel workspace or packa
|
|
|
8308
9074
|
if (bootstrapPackage) console.log("Auto-added the agentwheel bootstrap skill for openclaw.");
|
|
8309
9075
|
console.log(nextInstallNudge());
|
|
8310
9076
|
});
|
|
8311
|
-
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,
|
|
9077
|
+
program.command("add").description("add a package to .agentwheel/config.json without touching runtimes").argument("<source>", "package source").option("--driver <driver>", "source driver (local, git, skillkit, vercel-skills, mcp-registry, or clawhub)").option("--adapter <adapter>", "built-in adapter", "openclaw").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "workspace root").option("--mode <mode>", "pinned or tracking", "pinned").option("--name <name>", "package alias").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots on future installs", false).option("--suggestion <alias>", "include one suggested companion alias on future installs (repeatable or comma-separated)", collectSuggestionOption, []).option("--override <source-or-package::type/name>", "allow this package to replace a colliding artifact (repeatable)", collectOverrideOption, []).action(async (source, options) => {
|
|
8312
9078
|
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
8313
9079
|
const targetRoot = normalizeTargetRoot(normalizedOptions.targetRoot ?? process.cwd());
|
|
8314
9080
|
const entry = await packageEntryFromSource(source, targetRoot, normalizedOptions);
|
|
@@ -8317,10 +9083,10 @@ program.command("add").description("add a package to .agentwheel/config.json wit
|
|
|
8317
9083
|
});
|
|
8318
9084
|
program.command("list").description("list artifacts exposed by a package source").argument("<source>", "package source").option("--driver <driver>", "source driver").option("-t, --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) => {
|
|
8319
9085
|
const targetRoot = normalizeTargetRoot(options.targetRoot);
|
|
8320
|
-
const selectedArtifacts = selectedArtifactsFromOptions(options);
|
|
8321
9086
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
9087
|
+
const selectedArtifacts = selectedArtifactsFromOptionsOrRegistry(options, resolvedInput.registryEntry);
|
|
8322
9088
|
const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
|
|
8323
|
-
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot:
|
|
9089
|
+
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join40(targetRoot, ".agentwheel", "cache") }))));
|
|
8324
9090
|
const artifacts = filterArtifactsBySelection(await driver.list(resolved), selectedArtifacts);
|
|
8325
9091
|
for (const artifact of artifacts) {
|
|
8326
9092
|
console.log(`${artifact.type} ${artifact.name} ${artifact.relativePath}`);
|
|
@@ -8330,7 +9096,7 @@ program.command("scan").description("scan a package source for validation findin
|
|
|
8330
9096
|
const targetRoot = normalizeTargetRoot(options.targetRoot);
|
|
8331
9097
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
8332
9098
|
const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
|
|
8333
|
-
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot:
|
|
9099
|
+
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join40(targetRoot, ".agentwheel", "cache") }))));
|
|
8334
9100
|
const result = await driver.scan(resolved);
|
|
8335
9101
|
if (result.findings.length === 0) {
|
|
8336
9102
|
console.log("Scan ok: no findings");
|
|
@@ -8341,17 +9107,17 @@ program.command("scan").description("scan a package source for validation findin
|
|
|
8341
9107
|
}
|
|
8342
9108
|
if (!result.ok) process.exitCode = 1;
|
|
8343
9109
|
});
|
|
8344
|
-
program.command("plan").description("preview what install would reconcile without writing").argument("[name-or-source]", "configured package name/source or package source to preview").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--override <source-or-package::type/name>", "for source previews, allow the source to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--dry-run", "accepted for symmetry; plan never writes", false).option("--force-drift", "replace drifted managed artifacts during install planning", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "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) => {
|
|
9110
|
+
program.command("plan").description("preview what install would reconcile without writing").argument("[name-or-source]", "configured package name/source or package source to preview").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--override <source-or-package::type/name>", "for source previews, allow the source to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--dry-run", "accepted for symmetry; plan never writes", false).option("--force-drift", "replace drifted managed artifacts during install planning", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "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) => {
|
|
8345
9111
|
await runInstallCommand(source, { ...options, dryRun: true }, { apply: false });
|
|
8346
9112
|
});
|
|
8347
|
-
program.command("install").description("install configured packages into runtime targets").argument("[name-or-source]", "configured package name/source or package source to add and install").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--override <source-or-package::type/name>", "when adding a source, allow it to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--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) => {
|
|
9113
|
+
program.command("install").description("install configured packages into runtime targets").argument("[name-or-source]", "configured package name/source or package source to add and install").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--override <source-or-package::type/name>", "when adding a source, allow it to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--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) => {
|
|
8348
9114
|
await runInstallCommand(source, options, { apply: !options.dryRun });
|
|
8349
9115
|
});
|
|
8350
|
-
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 or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--override <source-or-package::type/name>", "when adding a source, allow it to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--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) => {
|
|
9116
|
+
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 or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--override <source-or-package::type/name>", "when adding a source, allow it to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--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) => {
|
|
8351
9117
|
console.error("warning: 'agentwheel sync' is deprecated and will be removed in 0.10. Use 'agentwheel install'.");
|
|
8352
9118
|
await runInstallCommand(source, options, { apply: !options.dryRun });
|
|
8353
9119
|
});
|
|
8354
|
-
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 or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("-t, --target-root <path>", "workspace root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plans without writing", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--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) => {
|
|
9120
|
+
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 or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("-t, --target-root <path>", "workspace root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plans without writing", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--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("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).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) => {
|
|
8355
9121
|
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
8356
9122
|
const targets = await resolveCliTargets(normalizedOptions, { preferAllProfile: true });
|
|
8357
9123
|
for (const target of targets) {
|
|
@@ -8359,7 +9125,7 @@ program.command("update").description("re-resolve tracking packages, then apply
|
|
|
8359
9125
|
}
|
|
8360
9126
|
});
|
|
8361
9127
|
program.command("deps").description("inspect the OpenPack dependency graph").addCommand(
|
|
8362
|
-
new Command("tree").description("print the OpenPack dependency graph").argument("[source]", "optional package source to resolve").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--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) => {
|
|
9128
|
+
new Command("tree").description("print the OpenPack dependency graph").argument("[source]", "optional package source to resolve").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--with-suggestions", "include suggested companion artifacts for selected roots", false).option("--suggestion <alias>", "include one suggested companion alias (repeatable or comma-separated)", collectSuggestionOption, []).option("--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) => {
|
|
8363
9129
|
const normalizedOptions = normalizeRuntimeScopeOptions(options, { defaultUser: shouldDefaultUserInstall(source, options) });
|
|
8364
9130
|
const targets = await resolveCliTargets(normalizedOptions);
|
|
8365
9131
|
for (const target of targets) {
|
|
@@ -8372,7 +9138,7 @@ program.command("deps").description("inspect the OpenPack dependency graph").add
|
|
|
8372
9138
|
for (const decision of result.bundle.graphLock.canonical.overrides) {
|
|
8373
9139
|
console.log(`OVERRIDE ${decision.graphNodeId}:${decision.type}/${decision.name} replaces ${decision.overriddenGraphNodeId}:${decision.type}/${decision.name} via ${decision.rootId} (${decision.selector})`);
|
|
8374
9140
|
}
|
|
8375
|
-
await
|
|
9141
|
+
await rm11(result.bundle.root, { recursive: true, force: true });
|
|
8376
9142
|
}
|
|
8377
9143
|
continue;
|
|
8378
9144
|
}
|
|
@@ -8409,6 +9175,29 @@ program.command("registry").description("manage optional registry indexes").addC
|
|
|
8409
9175
|
const client = new RegistryClient({ workspaceRoot: normalizeTargetRoot(options.targetRoot), warn: (message) => console.warn(message) });
|
|
8410
9176
|
printRegistryEntries(await client.search(query));
|
|
8411
9177
|
})
|
|
9178
|
+
).addCommand(
|
|
9179
|
+
new Command("publish").description("draft a catalogue submission for a public source").argument("<source>", "public resource source or GitHub URL").option("--name <name>", "registry short name").option("--type <type>", "entry type (package, skill, plugin, mcp, or adapter)").option("--description <text>", "short catalogue description").option("--tag <tag>", "search tag (repeatable or comma-separated)", collectTagOption, []).option("--select <type/name>", "selected artifact inside a larger package (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "selected skill inside a larger package (repeatable or comma-separated)", collectSkillOption, []).option("--json", "print only the registry entry JSON", false).action(async (source, options) => {
|
|
9180
|
+
const draft = createRegistryPublishDraft(source, {
|
|
9181
|
+
name: options.name,
|
|
9182
|
+
type: options.type,
|
|
9183
|
+
description: options.description,
|
|
9184
|
+
tags: options.tag,
|
|
9185
|
+
select: options.select,
|
|
9186
|
+
skills: options.skill
|
|
9187
|
+
});
|
|
9188
|
+
if (options.json) {
|
|
9189
|
+
console.log(JSON.stringify(draft.entry, null, 2));
|
|
9190
|
+
return;
|
|
9191
|
+
}
|
|
9192
|
+
console.log("Draft registry entry:");
|
|
9193
|
+
console.log(JSON.stringify(draft.entry, null, 2));
|
|
9194
|
+
console.log("");
|
|
9195
|
+
console.log(`Verify: ${draft.installCommand}`);
|
|
9196
|
+
if (!draft.entry.description) console.log('Tip: add --description "..." or fill the description before submitting.');
|
|
9197
|
+
console.log("");
|
|
9198
|
+
console.log("Submit:");
|
|
9199
|
+
console.log(draft.issueUrl);
|
|
9200
|
+
})
|
|
8412
9201
|
);
|
|
8413
9202
|
program.command("trust").description("manage persisted source trust decisions").addCommand(
|
|
8414
9203
|
new Command("forget").description("forget a persisted trusted source pattern").argument("<pattern>", "trusted source glob to revoke").option("-t, --target-root <path>", "workspace root", process.cwd()).action(async (pattern, options) => {
|
|
@@ -8493,7 +9282,7 @@ program.command("status").description("show configured packages and runtime inst
|
|
|
8493
9282
|
await printStatus(target, normalizedOptions);
|
|
8494
9283
|
}
|
|
8495
9284
|
});
|
|
8496
|
-
program.command("doctor").description("check agentwheel runtime setup and companion skill guidance").option("--adapter <adapter>", "built-in adapter").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").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).action(async (options) => {
|
|
9285
|
+
program.command("doctor").description("check agentwheel runtime setup and companion skill guidance").option("--adapter <adapter>", "built-in adapter").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").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("--skill <name>", "check a specific skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--source <source>", "source to use in suggested install commands").option("--json", "print machine-readable doctor report", false).action(async (options) => {
|
|
8497
9286
|
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
8498
9287
|
const target = await resolveRuntimeTarget({
|
|
8499
9288
|
targetRoot: normalizedOptions.targetRoot,
|
|
@@ -8527,6 +9316,8 @@ async function runInstallCommand(nameOrSource, options, behavior) {
|
|
|
8527
9316
|
forceConflict: normalizedOptions.forceConflict,
|
|
8528
9317
|
replaceConflict: normalizedOptions.replaceConflict,
|
|
8529
9318
|
noDeps: noDepsFromOptions(normalizedOptions),
|
|
9319
|
+
includeSuggestions: normalizedOptions.withSuggestions,
|
|
9320
|
+
suggestionAliases: suggestionAliasesFromOptions(normalizedOptions),
|
|
8530
9321
|
lockedResolution: true,
|
|
8531
9322
|
frozenLock: normalizedOptions.frozenLock,
|
|
8532
9323
|
offline: normalizedOptions.offline,
|
|
@@ -8576,7 +9367,7 @@ async function runInstallCommand(nameOrSource, options, behavior) {
|
|
|
8576
9367
|
});
|
|
8577
9368
|
console.log(`Applied ${result.plan.adapter} at ${result.plan.targetRoot}.`);
|
|
8578
9369
|
}
|
|
8579
|
-
await
|
|
9370
|
+
await rm11(result.bundle.root, { recursive: true, force: true });
|
|
8580
9371
|
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
8581
9372
|
}
|
|
8582
9373
|
if (behavior.apply && extraPackage && !targetOptions.onlySource) {
|
|
@@ -8585,10 +9376,10 @@ async function runInstallCommand(nameOrSource, options, behavior) {
|
|
|
8585
9376
|
}
|
|
8586
9377
|
}
|
|
8587
9378
|
async function packageEntryFromSource(source, targetRoot, options) {
|
|
8588
|
-
const selectedArtifacts = selectedArtifactsFromOptions(options);
|
|
8589
9379
|
const lockMode = options.frozenLock === true || options.offline === true;
|
|
8590
9380
|
const resolvedInput = await resolvePackageSource(source, targetRoot, { offline: lockMode });
|
|
8591
9381
|
const resolvedSource = resolvedInput.source;
|
|
9382
|
+
const selectedArtifacts = selectedArtifactsFromOptionsOrRegistry(options, resolvedInput.registryEntry);
|
|
8592
9383
|
const driverName = options.driver ?? inferSourceDriverName(resolvedSource);
|
|
8593
9384
|
const driver = getSourceDriver(driverName);
|
|
8594
9385
|
const adapter = await resolveAdapter({
|
|
@@ -8602,7 +9393,7 @@ async function packageEntryFromSource(source, targetRoot, options) {
|
|
|
8602
9393
|
const bundle = await stageSource(driver, resolvedSource, {
|
|
8603
9394
|
workspaceRoot: targetRoot,
|
|
8604
9395
|
adapter,
|
|
8605
|
-
cacheRoot:
|
|
9396
|
+
cacheRoot: join40(targetRoot, ".agentwheel", "cache"),
|
|
8606
9397
|
mode: options.mode,
|
|
8607
9398
|
frozenLock: lockMode,
|
|
8608
9399
|
select: selectedArtifacts
|
|
@@ -8621,10 +9412,12 @@ async function packageEntryFromSource(source, targetRoot, options) {
|
|
|
8621
9412
|
mode: options.mode ?? "pinned",
|
|
8622
9413
|
requestedRef: bundle.source.requestedRef,
|
|
8623
9414
|
select: selectedArtifacts,
|
|
9415
|
+
withSuggestions: options.withSuggestions === true ? true : void 0,
|
|
9416
|
+
suggestions: suggestionAliasesFromOptions(options),
|
|
8624
9417
|
overrides: overrideArtifactsFromOptions(options)
|
|
8625
9418
|
};
|
|
8626
9419
|
} finally {
|
|
8627
|
-
await
|
|
9420
|
+
await rm11(bundle.root, { recursive: true, force: true });
|
|
8628
9421
|
}
|
|
8629
9422
|
}
|
|
8630
9423
|
function findConfiguredPackage(packages, value) {
|
|
@@ -8765,7 +9558,7 @@ async function runConfiguredGraphPackages(target, options, behavior) {
|
|
|
8765
9558
|
});
|
|
8766
9559
|
console.log(`Applied ${result.plan.adapter} at ${result.plan.targetRoot}.`);
|
|
8767
9560
|
}
|
|
8768
|
-
await
|
|
9561
|
+
await rm11(result.bundle.root, { recursive: true, force: true });
|
|
8769
9562
|
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
8770
9563
|
}
|
|
8771
9564
|
}
|
|
@@ -8830,6 +9623,8 @@ async function buildGraphPlansForTarget(target, source, options, behavior) {
|
|
|
8830
9623
|
select: selectedArtifacts && packageIsScoped ? selectedArtifacts : normalizeArtifactSelectors(pkg.select, pkg.skills),
|
|
8831
9624
|
aliases: pkg.aliases,
|
|
8832
9625
|
overrides: pkg.overrides,
|
|
9626
|
+
includeSuggestions: targetOptions.withSuggestions === true || pkg.withSuggestions === true,
|
|
9627
|
+
suggestionAliases: packageSuggestionAliases(pkg, targetOptions),
|
|
8833
9628
|
useLock: behavior.mode === "install" ? true : !updateThisPackage
|
|
8834
9629
|
};
|
|
8835
9630
|
}),
|
|
@@ -8854,6 +9649,8 @@ async function buildGraphPlansForTarget(target, source, options, behavior) {
|
|
|
8854
9649
|
targetFingerprintParts: targetFingerprintParts(group.target, adapter, group.adapterOptions, group.installationType),
|
|
8855
9650
|
installationType: group.installationType,
|
|
8856
9651
|
noDeps: noDepsFromOptions(targetOptions),
|
|
9652
|
+
includeSuggestions: targetOptions.withSuggestions,
|
|
9653
|
+
suggestionAliases: suggestionAliasesFromOptions(targetOptions),
|
|
8857
9654
|
lockedResolution: behavior.mode === "install",
|
|
8858
9655
|
frozenLock: targetOptions.frozenLock,
|
|
8859
9656
|
offline: targetOptions.offline,
|
|
@@ -8954,7 +9751,7 @@ function keepManifestEntryOperation(entry, targetRoot, rootId, operation, option
|
|
|
8954
9751
|
artifactType: entry.artifactType,
|
|
8955
9752
|
artifactName: entry.artifactName,
|
|
8956
9753
|
kind: entry.kind,
|
|
8957
|
-
destPath: operation?.destPath ??
|
|
9754
|
+
destPath: operation?.destPath ?? join40(targetRoot, entry.path),
|
|
8958
9755
|
relativeDestPath: entry.path,
|
|
8959
9756
|
desiredHash: entry.sourceHash,
|
|
8960
9757
|
currentHash: operation?.currentHash ?? entry.hash,
|
|
@@ -9019,7 +9816,9 @@ async function uninstallConfiguredPackage(target, packageName, options) {
|
|
|
9019
9816
|
ref: pkg2.requestedRef,
|
|
9020
9817
|
select: normalizeArtifactSelectors(pkg2.select, pkg2.skills),
|
|
9021
9818
|
aliases: pkg2.aliases,
|
|
9022
|
-
overrides: pkg2.overrides
|
|
9819
|
+
overrides: pkg2.overrides,
|
|
9820
|
+
includeSuggestions: options.withSuggestions === true || pkg2.withSuggestions === true,
|
|
9821
|
+
suggestionAliases: packageSuggestionAliases(pkg2, options)
|
|
9023
9822
|
})),
|
|
9024
9823
|
targetRoot: remainingGroup.target.targetRoot,
|
|
9025
9824
|
workspaceRoot: remainingGroup.target.workspaceRoot,
|
|
@@ -9028,6 +9827,8 @@ async function uninstallConfiguredPackage(target, packageName, options) {
|
|
|
9028
9827
|
targetKey: targetKeyForTarget(remainingGroup.target, remainingAdapter.name),
|
|
9029
9828
|
targetFingerprintParts: targetFingerprintParts(remainingGroup.target, remainingAdapter, remainingGroup.adapterOptions, remainingGroup.installationType),
|
|
9030
9829
|
installationType: remainingGroup.installationType,
|
|
9830
|
+
includeSuggestions: options.withSuggestions,
|
|
9831
|
+
suggestionAliases: suggestionAliasesFromOptions(options),
|
|
9031
9832
|
lockedResolution: true,
|
|
9032
9833
|
frozenLock: options.frozenLock,
|
|
9033
9834
|
offline: options.offline,
|
|
@@ -9065,7 +9866,7 @@ async function uninstallConfiguredPackage(target, packageName, options) {
|
|
|
9065
9866
|
if (!options.dryRun) {
|
|
9066
9867
|
console.log(formatUninstallResult(result));
|
|
9067
9868
|
}
|
|
9068
|
-
if (renderedRoot) await
|
|
9869
|
+
if (renderedRoot) await rm11(renderedRoot, { recursive: true, force: true });
|
|
9069
9870
|
if (plan.hasBlockingChanges) process.exitCode = 1;
|
|
9070
9871
|
}
|
|
9071
9872
|
}
|
|
@@ -9193,7 +9994,7 @@ async function printPendingInstallWork(target, options) {
|
|
|
9193
9994
|
const message = error instanceof Error ? error.message : String(error);
|
|
9194
9995
|
console.log(`Pending install work: unavailable (${message})`);
|
|
9195
9996
|
} finally {
|
|
9196
|
-
await Promise.all(results.map((result) =>
|
|
9997
|
+
await Promise.all(results.map((result) => rm11(result.bundle.root, { recursive: true, force: true })));
|
|
9197
9998
|
}
|
|
9198
9999
|
}
|
|
9199
10000
|
async function printDoctor(target, options) {
|
|
@@ -9204,33 +10005,113 @@ async function printDoctor(target, options) {
|
|
|
9204
10005
|
if (!targetMapping?.enabled) {
|
|
9205
10006
|
throw new Error(`Adapter ${adapter.name} does not support skills for installation type '${installationType}'.`);
|
|
9206
10007
|
}
|
|
9207
|
-
const
|
|
9208
|
-
const
|
|
9209
|
-
const
|
|
9210
|
-
|
|
9211
|
-
|
|
9212
|
-
|
|
10008
|
+
const transport = transportForTarget(target);
|
|
10009
|
+
const state = installStateForTarget(target, adapter, adapterOptions, installationType);
|
|
10010
|
+
const manifest = await readInstallManifest(state.installRoot, adapter.name, transport, state);
|
|
10011
|
+
const requestedSkills = doctorSkillRequests(target, options);
|
|
10012
|
+
const skills = [];
|
|
10013
|
+
for (const request of requestedSkills) {
|
|
10014
|
+
const skillPath = join40(state.installRoot, targetMapping.dest, request.name);
|
|
10015
|
+
const exists = await pathExists(skillPath);
|
|
10016
|
+
const manifestEntry = manifest?.entries.find((entry) => {
|
|
10017
|
+
if (entry.artifactType !== "skills") return false;
|
|
10018
|
+
const legacyInstallName = "installName" in entry && typeof entry.installName === "string" ? entry.installName : void 0;
|
|
10019
|
+
return entry.artifactName === request.name || legacyInstallName === request.name || entry.path === join40(targetMapping.dest, request.name);
|
|
10020
|
+
});
|
|
10021
|
+
const status = manifestEntry ? "managed" : exists ? "present-unmanaged" : "missing";
|
|
10022
|
+
skills.push({
|
|
10023
|
+
name: request.name,
|
|
10024
|
+
source: request.source,
|
|
10025
|
+
label: request.label,
|
|
10026
|
+
status,
|
|
10027
|
+
path: skillPath,
|
|
10028
|
+
managed: Boolean(manifestEntry),
|
|
10029
|
+
present: exists,
|
|
10030
|
+
suggestedCommands: status === "missing" ? {
|
|
10031
|
+
dryRun: skillInstallCommand(adapter.name, installationType, options, request, { dryRun: true }),
|
|
10032
|
+
apply: skillInstallCommand(adapter.name, installationType, options, request)
|
|
10033
|
+
} : void 0
|
|
10034
|
+
});
|
|
10035
|
+
}
|
|
10036
|
+
const report = {
|
|
10037
|
+
adapter: adapter.name,
|
|
10038
|
+
installationType,
|
|
10039
|
+
targetRoot: target.targetRoot,
|
|
10040
|
+
installRoot: state.installRoot,
|
|
10041
|
+
manifest: manifest ? { entries: manifest.entries.length, revision: manifest.revision } : null,
|
|
10042
|
+
skills
|
|
10043
|
+
};
|
|
10044
|
+
if (options.json) {
|
|
10045
|
+
console.log(JSON.stringify(report, null, 2));
|
|
9213
10046
|
return;
|
|
9214
10047
|
}
|
|
9215
|
-
console.log(`
|
|
9216
|
-
|
|
9217
|
-
|
|
9218
|
-
|
|
10048
|
+
console.log(`Doctor for ${adapter.name}/${installationType} at ${state.installRoot}`);
|
|
10049
|
+
for (const skill of skills) {
|
|
10050
|
+
const statusLabel = skill.status === "managed" ? "installed" : skill.status === "present-unmanaged" ? "installed (unmanaged)" : "missing";
|
|
10051
|
+
console.log(`${skill.label}: ${statusLabel} at ${skill.path}`);
|
|
10052
|
+
}
|
|
10053
|
+
const missing = skills.filter((skill) => skill.status === "missing");
|
|
10054
|
+
if (missing.length > 0) {
|
|
10055
|
+
console.log("Suggested commands:");
|
|
10056
|
+
for (const skill of missing) {
|
|
10057
|
+
console.log(` ${skill.suggestedCommands?.dryRun}`);
|
|
10058
|
+
console.log(` ${skill.suggestedCommands?.apply}`);
|
|
10059
|
+
}
|
|
10060
|
+
}
|
|
10061
|
+
}
|
|
10062
|
+
function doctorSkillRequests(target, options) {
|
|
10063
|
+
const explicitSkills = normalizeDoctorSkillNames(options.skill ?? []);
|
|
10064
|
+
if (explicitSkills.length > 0) {
|
|
10065
|
+
return explicitSkills.map((name) => ({
|
|
10066
|
+
name,
|
|
10067
|
+
source: options.source ?? defaultSourceForSkill(name),
|
|
10068
|
+
label: doctorSkillLabel(name)
|
|
10069
|
+
}));
|
|
10070
|
+
}
|
|
10071
|
+
const requests = [{
|
|
10072
|
+
name: COMPANION_SKILL_NAME,
|
|
10073
|
+
source: COMPANION_SKILL_SOURCE,
|
|
10074
|
+
label: "Agentwheel companion skill"
|
|
10075
|
+
}];
|
|
10076
|
+
if (isSyncwheelWorkspace(target.targetRoot)) {
|
|
10077
|
+
requests.push({
|
|
10078
|
+
name: "syncwheel",
|
|
10079
|
+
source: "github:NestDevLab/syncwheel",
|
|
10080
|
+
label: "Syncwheel skill"
|
|
10081
|
+
});
|
|
10082
|
+
}
|
|
10083
|
+
return requests;
|
|
10084
|
+
}
|
|
10085
|
+
function normalizeDoctorSkillNames(skills) {
|
|
10086
|
+
return [...new Set(skills.flatMap(splitSelectorList).map((item) => item.trim()).filter(Boolean))];
|
|
10087
|
+
}
|
|
10088
|
+
function defaultSourceForSkill(name) {
|
|
10089
|
+
if (name === COMPANION_SKILL_NAME) return COMPANION_SKILL_SOURCE;
|
|
10090
|
+
if (name === "syncwheel") return "github:NestDevLab/syncwheel";
|
|
10091
|
+
return `github:NestDevLab/${name}`;
|
|
9219
10092
|
}
|
|
9220
|
-
function
|
|
10093
|
+
function doctorSkillLabel(name) {
|
|
10094
|
+
if (name === COMPANION_SKILL_NAME) return "Agentwheel companion skill";
|
|
10095
|
+
if (name === "syncwheel") return "Syncwheel skill";
|
|
10096
|
+
return `${name} skill`;
|
|
10097
|
+
}
|
|
10098
|
+
function isSyncwheelWorkspace(targetRoot) {
|
|
10099
|
+
return existsSync(join40(targetRoot, ".syncwheel", "manifest.json"));
|
|
10100
|
+
}
|
|
10101
|
+
function skillInstallCommand(adapter, installationType, options, skill, behavior = {}) {
|
|
9221
10102
|
const args = [
|
|
9222
10103
|
"agentwheel",
|
|
9223
10104
|
"install",
|
|
9224
|
-
|
|
10105
|
+
skill.source,
|
|
9225
10106
|
"--adapter",
|
|
9226
10107
|
adapter,
|
|
9227
10108
|
...installationTypeCommandArgs(installationType),
|
|
9228
10109
|
...targetRootCommandArgs(options),
|
|
9229
10110
|
"--skill",
|
|
9230
|
-
|
|
10111
|
+
skill.name
|
|
9231
10112
|
];
|
|
9232
10113
|
if (behavior.dryRun) args.push("--dry-run");
|
|
9233
|
-
return args.map(
|
|
10114
|
+
return args.map(shellQuoteArg2).join(" ");
|
|
9234
10115
|
}
|
|
9235
10116
|
function installationTypeCommandArgs(installationType) {
|
|
9236
10117
|
if (installationType === "user") return ["--user"];
|
|
@@ -9240,7 +10121,7 @@ function installationTypeCommandArgs(installationType) {
|
|
|
9240
10121
|
function targetRootCommandArgs(options) {
|
|
9241
10122
|
return options.targetRoot && options.installationType !== "user" ? ["--target-root", options.targetRoot] : [];
|
|
9242
10123
|
}
|
|
9243
|
-
function
|
|
10124
|
+
function shellQuoteArg2(value) {
|
|
9244
10125
|
if (/^[A-Za-z0-9_./:@=-]+$/.test(value)) return value;
|
|
9245
10126
|
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
9246
10127
|
}
|
|
@@ -9250,12 +10131,18 @@ function collectSelectOption(value, previous) {
|
|
|
9250
10131
|
function collectSkillOption(value, previous) {
|
|
9251
10132
|
return [...previous, ...splitSelectorList(value)];
|
|
9252
10133
|
}
|
|
10134
|
+
function collectSuggestionOption(value, previous) {
|
|
10135
|
+
return [...previous, ...splitSelectorList(value)];
|
|
10136
|
+
}
|
|
9253
10137
|
function collectTrustOption(value, previous) {
|
|
9254
10138
|
return [...previous, value];
|
|
9255
10139
|
}
|
|
9256
10140
|
function collectOverrideOption(value, previous) {
|
|
9257
10141
|
return [...previous, ...splitSelectorList(value)];
|
|
9258
10142
|
}
|
|
10143
|
+
function collectTagOption(value, previous) {
|
|
10144
|
+
return [...previous, ...splitSelectorList(value)];
|
|
10145
|
+
}
|
|
9259
10146
|
function normalizeRuntimeScopeOptions(options, behavior = {}) {
|
|
9260
10147
|
if (options.user && options.local) {
|
|
9261
10148
|
throw new Error("Choose either --user or --local.");
|
|
@@ -9292,11 +10179,11 @@ function looksLikeSourceSpecifier(value) {
|
|
|
9292
10179
|
}
|
|
9293
10180
|
function normalizeCliPath(value) {
|
|
9294
10181
|
if (value === "~") return homedir9();
|
|
9295
|
-
if (value.startsWith("~/")) return
|
|
9296
|
-
return
|
|
10182
|
+
if (value.startsWith("~/")) return resolve20(homedir9(), value.slice(2));
|
|
10183
|
+
return resolve20(value);
|
|
9297
10184
|
}
|
|
9298
10185
|
function isHomePath(path) {
|
|
9299
|
-
return
|
|
10186
|
+
return resolve20(path) === resolve20(homedir9());
|
|
9300
10187
|
}
|
|
9301
10188
|
function adapterListFromOption(adapter) {
|
|
9302
10189
|
if (!adapter) return [];
|
|
@@ -9310,6 +10197,18 @@ function adapterListFromOption(adapter) {
|
|
|
9310
10197
|
function selectedArtifactsFromOptions(options) {
|
|
9311
10198
|
return normalizeArtifactSelectors(options.select, options.skills ?? options.skill);
|
|
9312
10199
|
}
|
|
10200
|
+
function selectedArtifactsFromOptionsOrRegistry(options, registryEntry) {
|
|
10201
|
+
return selectedArtifactsFromOptions(options) ?? selectorsFromRegistryEntry(registryEntry);
|
|
10202
|
+
}
|
|
10203
|
+
function suggestionAliasesFromOptions(options) {
|
|
10204
|
+
const values = options.suggestions ?? options.suggestion;
|
|
10205
|
+
if (!values?.length) return void 0;
|
|
10206
|
+
return [...new Set(values.flatMap(splitSelectorList).map((item) => item.trim()).filter(Boolean))].sort((a, b) => a.localeCompare(b));
|
|
10207
|
+
}
|
|
10208
|
+
function packageSuggestionAliases(pkg, options) {
|
|
10209
|
+
const aliases = [...pkg.suggestions ?? [], ...suggestionAliasesFromOptions(options) ?? []].map((item) => item.trim()).filter(Boolean);
|
|
10210
|
+
return aliases.length > 0 ? [...new Set(aliases)].sort((a, b) => a.localeCompare(b)) : void 0;
|
|
10211
|
+
}
|
|
9313
10212
|
function overrideArtifactsFromOptions(options) {
|
|
9314
10213
|
const values = options.overrides ?? options.override;
|
|
9315
10214
|
return values && values.length > 0 ? values : void 0;
|
|
@@ -9339,10 +10238,10 @@ function filterUninstallPlanBySelection(plan, selected) {
|
|
|
9339
10238
|
};
|
|
9340
10239
|
}
|
|
9341
10240
|
async function initPackage(root) {
|
|
9342
|
-
await
|
|
9343
|
-
await
|
|
9344
|
-
await
|
|
9345
|
-
const manifestPath =
|
|
10241
|
+
await mkdir20(join40(root, "instructions"), { recursive: true });
|
|
10242
|
+
await mkdir20(join40(root, "rules"), { recursive: true });
|
|
10243
|
+
await mkdir20(join40(root, "skills"), { recursive: true });
|
|
10244
|
+
const manifestPath = join40(root, "openpack.json");
|
|
9346
10245
|
const manifest = {
|
|
9347
10246
|
schemaVersion: 2,
|
|
9348
10247
|
name: "example/agentwheel-package",
|
|
@@ -9353,12 +10252,12 @@ async function initPackage(root) {
|
|
|
9353
10252
|
{ type: "skills", path: "skills" }
|
|
9354
10253
|
]
|
|
9355
10254
|
};
|
|
9356
|
-
await
|
|
10255
|
+
await writeFile19(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
9357
10256
|
`, "utf8");
|
|
9358
|
-
await
|
|
10257
|
+
await writeFile19(join40(root, "instructions", "AGENTS.md"), "# Agent Instructions\n", "utf8");
|
|
9359
10258
|
}
|
|
9360
10259
|
async function defaultBootstrapPackage(_root) {
|
|
9361
|
-
const packageRoot = await findAgentwheelPackageRoot(
|
|
10260
|
+
const packageRoot = await findAgentwheelPackageRoot(dirname28(fileURLToPath3(import.meta.url)));
|
|
9362
10261
|
if (!packageRoot) return void 0;
|
|
9363
10262
|
return {
|
|
9364
10263
|
name: "agentwheel",
|
|
@@ -9402,10 +10301,10 @@ function withFleetExample(config) {
|
|
|
9402
10301
|
};
|
|
9403
10302
|
}
|
|
9404
10303
|
async function findAgentwheelPackageRoot(start) {
|
|
9405
|
-
let current =
|
|
10304
|
+
let current = resolve20(start);
|
|
9406
10305
|
while (true) {
|
|
9407
10306
|
if (await findPackageManifestPath(current, { warnLegacy: false })) return current;
|
|
9408
|
-
const parent =
|
|
10307
|
+
const parent = dirname28(current);
|
|
9409
10308
|
if (parent === current) return void 0;
|
|
9410
10309
|
current = parent;
|
|
9411
10310
|
}
|