agentwheel 0.14.5 → 0.14.7
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 +109 -8
- package/dist/{chunk-B3FMBTWC.js → chunk-QJTTISLY.js} +3 -1
- package/dist/{identify-TXIDGMNL.js → identify-T4RE5RBD.js} +1 -1
- package/dist/index.js +1481 -415
- 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 mkdir21, rm as rm11, writeFile as writeFile20 } from "fs/promises";
|
|
13
14
|
import { homedir as homedir9 } from "os";
|
|
14
|
-
import { dirname as
|
|
15
|
+
import { dirname as dirname30, join as join41, 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()
|
|
@@ -97,12 +111,13 @@ var targetMappingSchema = z2.object({
|
|
|
97
111
|
"codex-plugin",
|
|
98
112
|
"hermes-plugin",
|
|
99
113
|
"copilot-plugin",
|
|
114
|
+
"openclaw-subagent",
|
|
100
115
|
"codex-subagent",
|
|
101
116
|
"copilot-instruction",
|
|
102
117
|
"copilot-prompt",
|
|
103
118
|
"copilot-agent"
|
|
104
119
|
]).optional(),
|
|
105
|
-
merge: z2.enum(["json-deep", "yaml-deep", "codex-toml-mcp"]).optional(),
|
|
120
|
+
merge: z2.enum(["json-deep", "openclaw-json-deep", "yaml-deep", "codex-toml-mcp"]).optional(),
|
|
106
121
|
mode: z2.enum(["managed-block"]).optional()
|
|
107
122
|
});
|
|
108
123
|
var targetRegistrySchema = z2.record(installationTypeSchema, targetMappingSchema);
|
|
@@ -362,14 +377,17 @@ var openClawAdapter = {
|
|
|
362
377
|
local: { enabled: true, dest: "skills" },
|
|
363
378
|
user: { enabled: true, root: "home", dest: ".openclaw/skills" }
|
|
364
379
|
},
|
|
380
|
+
subagents: {
|
|
381
|
+
user: { enabled: true, root: "home", dest: ".openclaw/workspace-subagents", semantic: "openclaw-subagent" }
|
|
382
|
+
},
|
|
365
383
|
mcp: {
|
|
366
|
-
user: { enabled: true, root: "home", dest: ".openclaw/openclaw.json", merge: "json-deep" }
|
|
384
|
+
user: { enabled: true, root: "home", dest: ".openclaw/openclaw.json", merge: "openclaw-json-deep" }
|
|
367
385
|
},
|
|
368
386
|
settings: {
|
|
369
|
-
user: { enabled: true, root: "home", dest: ".openclaw/openclaw.json", merge: "json-deep" }
|
|
387
|
+
user: { enabled: true, root: "home", dest: ".openclaw/openclaw.json", merge: "openclaw-json-deep" }
|
|
370
388
|
},
|
|
371
389
|
plugins: {
|
|
372
|
-
local: { enabled: true, dest: ".openclaw/plugins", formats: ["openclaw-plugin"], semantic: "openclaw-plugin" }
|
|
390
|
+
local: { enabled: true, dest: ".openclaw/plugins", formats: ["openclaw-plugin", "openclaw-clawhub-plugin"], semantic: "openclaw-plugin" }
|
|
373
391
|
}
|
|
374
392
|
}
|
|
375
393
|
};
|
|
@@ -453,9 +471,9 @@ async function resolveAdapter(options) {
|
|
|
453
471
|
|
|
454
472
|
// src/install/apply.ts
|
|
455
473
|
import { execFile as execFile3 } from "child_process";
|
|
456
|
-
import { mkdtemp as mkdtemp2, rm as rm4, writeFile as
|
|
474
|
+
import { mkdtemp as mkdtemp2, rm as rm4, writeFile as writeFile9 } from "fs/promises";
|
|
457
475
|
import { tmpdir as tmpdir3 } from "os";
|
|
458
|
-
import { basename as basename3, join as join5 } from "path";
|
|
476
|
+
import { basename as basename3, dirname as dirname10, join as join5 } from "path";
|
|
459
477
|
import { promisify as promisify3 } from "util";
|
|
460
478
|
|
|
461
479
|
// src/model/graph-lock.ts
|
|
@@ -759,13 +777,13 @@ async function spawnWithInput(command, args, input) {
|
|
|
759
777
|
if (result.stderr) throw new Error(result.stderr);
|
|
760
778
|
}
|
|
761
779
|
function waitForProcess(child, label) {
|
|
762
|
-
return new Promise((
|
|
780
|
+
return new Promise((resolve21, reject) => {
|
|
763
781
|
const stderr = [];
|
|
764
782
|
child.stderr?.on("data", (chunk) => stderr.push(chunk));
|
|
765
783
|
child.on("error", reject);
|
|
766
784
|
child.on("close", (code) => {
|
|
767
785
|
const message = Buffer.concat(stderr).toString("utf8");
|
|
768
|
-
if (code === 0)
|
|
786
|
+
if (code === 0) resolve21({ stderr: "" });
|
|
769
787
|
else reject(new Error(`${label} exited ${code}${message ? `: ${message}` : ""}`));
|
|
770
788
|
});
|
|
771
789
|
});
|
|
@@ -869,6 +887,98 @@ function dedupeArray(values) {
|
|
|
869
887
|
return out;
|
|
870
888
|
}
|
|
871
889
|
|
|
890
|
+
// src/install/openclaw-json-merge.ts
|
|
891
|
+
import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
|
|
892
|
+
import { dirname as dirname5 } from "path";
|
|
893
|
+
async function mergeOpenClawJsonFile(sourcePath, destPath) {
|
|
894
|
+
const source = expandEnvPlaceholders(
|
|
895
|
+
normalizeOpenClawConfig(JSON.parse(await readFile6(sourcePath, "utf8"))),
|
|
896
|
+
sourcePath
|
|
897
|
+
);
|
|
898
|
+
const current = await pathExists(destPath) ? JSON.parse(await readFile6(destPath, "utf8")) : {};
|
|
899
|
+
const merged = mergeOpenClawJson(current, source);
|
|
900
|
+
await mkdir4(dirname5(destPath), { recursive: true });
|
|
901
|
+
await writeFile5(destPath, `${JSON.stringify(merged, null, 2)}
|
|
902
|
+
`, "utf8");
|
|
903
|
+
}
|
|
904
|
+
function mergeOpenClawJson(base, incoming, path = []) {
|
|
905
|
+
if (isMcpServerCodexAgentsPath(path) && Array.isArray(incoming)) {
|
|
906
|
+
return incoming;
|
|
907
|
+
}
|
|
908
|
+
if (path.join(".") === "agents.list" && Array.isArray(base) && Array.isArray(incoming)) {
|
|
909
|
+
return mergeOpenClawAgentsById(base, incoming);
|
|
910
|
+
}
|
|
911
|
+
if (Array.isArray(base) && Array.isArray(incoming)) {
|
|
912
|
+
return deepMerge(base, incoming);
|
|
913
|
+
}
|
|
914
|
+
if (isRecord(base) && isRecord(incoming)) {
|
|
915
|
+
const out = { ...base };
|
|
916
|
+
for (const [key, value] of Object.entries(incoming)) {
|
|
917
|
+
out[key] = key in out ? mergeOpenClawJson(out[key], value, [...path, key]) : value;
|
|
918
|
+
}
|
|
919
|
+
return out;
|
|
920
|
+
}
|
|
921
|
+
return incoming;
|
|
922
|
+
}
|
|
923
|
+
function isMcpServerCodexAgentsPath(path) {
|
|
924
|
+
return path.length === 5 && path[0] === "mcp" && path[1] === "servers" && path[3] === "codex" && path[4] === "agents";
|
|
925
|
+
}
|
|
926
|
+
function expandEnvPlaceholders(value, sourcePath) {
|
|
927
|
+
if (typeof value === "string") {
|
|
928
|
+
return value.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_match, name) => {
|
|
929
|
+
const replacement = process.env[name];
|
|
930
|
+
if (replacement === void 0) {
|
|
931
|
+
throw new Error(`Missing environment variable ${name} while rendering OpenClaw JSON merge artifact ${sourcePath}`);
|
|
932
|
+
}
|
|
933
|
+
return replacement;
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
if (Array.isArray(value)) return value.map((item) => expandEnvPlaceholders(item, sourcePath));
|
|
937
|
+
if (!isRecord(value)) return value;
|
|
938
|
+
return Object.fromEntries(
|
|
939
|
+
Object.entries(value).map(([key, child]) => [key, expandEnvPlaceholders(child, sourcePath)])
|
|
940
|
+
);
|
|
941
|
+
}
|
|
942
|
+
function normalizeOpenClawConfig(value) {
|
|
943
|
+
if (!isRecord(value)) return value;
|
|
944
|
+
const rootMcpServers = isRecord(value.mcpServers) ? value.mcpServers : void 0;
|
|
945
|
+
if (!rootMcpServers) return value;
|
|
946
|
+
const out = { ...value };
|
|
947
|
+
const normalizedServers = {};
|
|
948
|
+
for (const [name, server] of Object.entries(rootMcpServers)) {
|
|
949
|
+
if (isRecord(server)) normalizedServers[name] = normalizeOpenClawMcpServer(server);
|
|
950
|
+
}
|
|
951
|
+
const mcp = isRecord(out.mcp) ? out.mcp : {};
|
|
952
|
+
out.mcp = deepMerge(mcp, { servers: normalizedServers });
|
|
953
|
+
delete out.mcpServers;
|
|
954
|
+
return out;
|
|
955
|
+
}
|
|
956
|
+
function normalizeOpenClawMcpServer(server) {
|
|
957
|
+
const out = { ...server };
|
|
958
|
+
const type = typeof out.type === "string" ? out.type : void 0;
|
|
959
|
+
if (type && typeof out.transport !== "string") out.transport = type;
|
|
960
|
+
delete out.type;
|
|
961
|
+
return out;
|
|
962
|
+
}
|
|
963
|
+
function mergeOpenClawAgentsById(base, incoming) {
|
|
964
|
+
const out = [...base];
|
|
965
|
+
const indexById = /* @__PURE__ */ new Map();
|
|
966
|
+
for (const [index, value] of out.entries()) {
|
|
967
|
+
const id = isRecord(value) && typeof value.id === "string" ? value.id : void 0;
|
|
968
|
+
if (id) indexById.set(id, index);
|
|
969
|
+
}
|
|
970
|
+
for (const value of incoming) {
|
|
971
|
+
const id = isRecord(value) && typeof value.id === "string" ? value.id : void 0;
|
|
972
|
+
if (!id || !indexById.has(id)) {
|
|
973
|
+
out.push(value);
|
|
974
|
+
if (id) indexById.set(id, out.length - 1);
|
|
975
|
+
continue;
|
|
976
|
+
}
|
|
977
|
+
out[indexById.get(id)] = value;
|
|
978
|
+
}
|
|
979
|
+
return out;
|
|
980
|
+
}
|
|
981
|
+
|
|
872
982
|
// src/install/manifest.ts
|
|
873
983
|
import { createHash as createHash2 } from "crypto";
|
|
874
984
|
import { resolve as resolve3 } from "path";
|
|
@@ -900,7 +1010,7 @@ var manifestEntryV1Schema = z4.object({
|
|
|
900
1010
|
semanticCommand: z4.array(z4.string()).optional(),
|
|
901
1011
|
semanticPlugin: semanticPluginSpecSchema.optional(),
|
|
902
1012
|
executed: z4.boolean().optional(),
|
|
903
|
-
mergeStrategy: z4.enum(["json-deep", "yaml-deep", "codex-toml-mcp"]).optional(),
|
|
1013
|
+
mergeStrategy: z4.enum(["json-deep", "openclaw-json-deep", "yaml-deep", "codex-toml-mcp"]).optional(),
|
|
904
1014
|
mode: z4.enum(["managed-block"]).optional(),
|
|
905
1015
|
composedFrom: z4.array(composedFromEntrySchema).optional()
|
|
906
1016
|
});
|
|
@@ -1092,8 +1202,8 @@ function assertOperationContained(operation, targetRoot) {
|
|
|
1092
1202
|
}
|
|
1093
1203
|
|
|
1094
1204
|
// src/install/transaction.ts
|
|
1095
|
-
import { cp, mkdir as
|
|
1096
|
-
import { dirname as
|
|
1205
|
+
import { cp, mkdir as mkdir5, rm as rm2, stat as stat2 } from "fs/promises";
|
|
1206
|
+
import { dirname as dirname6, join as join3 } from "path";
|
|
1097
1207
|
function applyLockPath(targetRoot, adapter, scope = {}) {
|
|
1098
1208
|
return join3(metadataDir(targetRoot), `${stateKeyFor(adapter, scope)}.apply-lock`);
|
|
1099
1209
|
}
|
|
@@ -1158,7 +1268,7 @@ async function recordBackup(operation, index, targetRoot, adapter, transport = l
|
|
|
1158
1268
|
}
|
|
1159
1269
|
const backupPath = join3(applyBackupDir(targetRoot, adapter, scope), String(index));
|
|
1160
1270
|
await rm2(backupPath, { recursive: true, force: true });
|
|
1161
|
-
await
|
|
1271
|
+
await mkdir5(dirname6(backupPath), { recursive: true });
|
|
1162
1272
|
await cp(operation.destPath, backupPath, { recursive: operation.kind === "dir", dereference: true });
|
|
1163
1273
|
return {
|
|
1164
1274
|
index,
|
|
@@ -1212,16 +1322,16 @@ async function localPathExists(path) {
|
|
|
1212
1322
|
}
|
|
1213
1323
|
|
|
1214
1324
|
// src/install/toml-merge.ts
|
|
1215
|
-
import { mkdir as
|
|
1216
|
-
import { dirname as
|
|
1325
|
+
import { mkdir as mkdir6, readFile as readFile7, writeFile as writeFile6 } from "fs/promises";
|
|
1326
|
+
import { dirname as dirname7 } from "path";
|
|
1217
1327
|
async function mergeCodexTomlMcp(sourcePath, destPath) {
|
|
1218
|
-
const source = JSON.parse(await
|
|
1328
|
+
const source = JSON.parse(await readFile7(sourcePath, "utf8"));
|
|
1219
1329
|
const servers = extractMcpServers(source);
|
|
1220
|
-
const current = await pathExists(destPath) ? await
|
|
1330
|
+
const current = await pathExists(destPath) ? await readFile7(destPath, "utf8") : "";
|
|
1221
1331
|
const withoutManaged = removeManagedMcpSections(current, Object.keys(servers));
|
|
1222
1332
|
const merged = appendMcpServers(withoutManaged, servers);
|
|
1223
|
-
await
|
|
1224
|
-
await
|
|
1333
|
+
await mkdir6(dirname7(destPath), { recursive: true });
|
|
1334
|
+
await writeFile6(destPath, merged, "utf8");
|
|
1225
1335
|
}
|
|
1226
1336
|
function extractMcpServers(source) {
|
|
1227
1337
|
const raw = isRecord2(source.mcpServers) ? source.mcpServers : source;
|
|
@@ -1300,15 +1410,15 @@ function isRecord2(value) {
|
|
|
1300
1410
|
}
|
|
1301
1411
|
|
|
1302
1412
|
// src/install/yaml-merge.ts
|
|
1303
|
-
import { mkdir as
|
|
1304
|
-
import { dirname as
|
|
1413
|
+
import { mkdir as mkdir7, readFile as readFile8, writeFile as writeFile7 } from "fs/promises";
|
|
1414
|
+
import { dirname as dirname8 } from "path";
|
|
1305
1415
|
import { parse as parse2, stringify } from "yaml";
|
|
1306
1416
|
async function mergeYamlFile(sourcePath, destPath) {
|
|
1307
|
-
const source = parseYamlValue(await
|
|
1308
|
-
const current = await pathExists(destPath) ? parseYamlValue(await
|
|
1417
|
+
const source = parseYamlValue(await readFile8(sourcePath, "utf8"));
|
|
1418
|
+
const current = await pathExists(destPath) ? parseYamlValue(await readFile8(destPath, "utf8")) : {};
|
|
1309
1419
|
const merged = deepMerge2(current, source);
|
|
1310
|
-
await
|
|
1311
|
-
await
|
|
1420
|
+
await mkdir7(dirname8(destPath), { recursive: true });
|
|
1421
|
+
await writeFile7(destPath, stringify(merged), "utf8");
|
|
1312
1422
|
}
|
|
1313
1423
|
function parseYamlValue(content) {
|
|
1314
1424
|
return normalizeYamlValue(parse2(content));
|
|
@@ -1369,13 +1479,13 @@ function normalizeOwners(owners) {
|
|
|
1369
1479
|
|
|
1370
1480
|
// src/install/instructions-block.ts
|
|
1371
1481
|
import { createHash as createHash3 } from "crypto";
|
|
1372
|
-
import { mkdtemp, readFile as
|
|
1482
|
+
import { mkdtemp, readFile as readFile9, realpath, rm as rm3, writeFile as writeFile8 } from "fs/promises";
|
|
1373
1483
|
import { tmpdir as tmpdir2 } from "os";
|
|
1374
|
-
import { basename as basename2, dirname as
|
|
1484
|
+
import { basename as basename2, dirname as dirname9, join as join4, relative as relative2 } from "path";
|
|
1375
1485
|
var managedInstructionBlockMode = "managed-block";
|
|
1376
1486
|
var managedInstructionBanner = "<!-- agentwheel-managed: edit fragments, not this block -->";
|
|
1377
1487
|
async function desiredManagedInstructionBlockHash(sourcePath) {
|
|
1378
|
-
const source = await
|
|
1488
|
+
const source = await readFile9(sourcePath, "utf8");
|
|
1379
1489
|
return hashText(managedBlockBody(source));
|
|
1380
1490
|
}
|
|
1381
1491
|
async function readManagedInstructionBlockState(destPath, selector, transport) {
|
|
@@ -1394,18 +1504,18 @@ async function readManagedInstructionBlockState(destPath, selector, transport) {
|
|
|
1394
1504
|
drifted: hash !== block.markerHash
|
|
1395
1505
|
};
|
|
1396
1506
|
}
|
|
1397
|
-
async function writeManagedInstructionBlock(sourcePath, destPath, selector, transport,
|
|
1398
|
-
const source = await
|
|
1507
|
+
async function writeManagedInstructionBlock(sourcePath, destPath, selector, transport, options = {}) {
|
|
1508
|
+
const source = await readFile9(sourcePath, "utf8");
|
|
1399
1509
|
const desired = renderManagedInstructionBlock(selector, source);
|
|
1400
1510
|
const existing = await readOptionalText(destPath, transport);
|
|
1401
|
-
const merged = upsertManagedInstructionBlock(existing ?? "", selector, desired.block,
|
|
1511
|
+
const merged = upsertManagedInstructionBlock(existing ?? "", selector, desired.block, options);
|
|
1402
1512
|
await writeTextWithTransport(destPath, merged, transport);
|
|
1403
1513
|
return desired.hash;
|
|
1404
1514
|
}
|
|
1405
|
-
async function removeManagedInstructionBlock(destPath, selector, transport,
|
|
1515
|
+
async function removeManagedInstructionBlock(destPath, selector, transport, options = {}) {
|
|
1406
1516
|
if (!await transport.pathExists(destPath)) return;
|
|
1407
1517
|
const existing = await transport.readFile(destPath);
|
|
1408
|
-
const updated = removeManagedBlockFromContent(existing, selector,
|
|
1518
|
+
const updated = removeManagedBlockFromContent(existing, selector, options);
|
|
1409
1519
|
await writeTextWithTransport(destPath, updated, transport);
|
|
1410
1520
|
}
|
|
1411
1521
|
async function managedInstructionBlockLanded(destPath, selector, expectedHash, transport) {
|
|
@@ -1438,24 +1548,30 @@ ${body}<!-- END openpack:include ${selector} -->
|
|
|
1438
1548
|
hash
|
|
1439
1549
|
};
|
|
1440
1550
|
}
|
|
1441
|
-
function upsertManagedInstructionBlock(content, selector, block,
|
|
1551
|
+
function upsertManagedInstructionBlock(content, selector, block, options) {
|
|
1552
|
+
const { expectedHash, allowDrift = false } = options;
|
|
1442
1553
|
const existing = findManagedInstructionBlock(content, selector);
|
|
1443
1554
|
if (!existing) {
|
|
1444
1555
|
if (expectedHash) throw new Error(`Managed instruction block missing for ${selector}`);
|
|
1445
1556
|
return appendManagedInstructionBlock(content, block);
|
|
1446
1557
|
}
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1558
|
+
if (!allowDrift) {
|
|
1559
|
+
assertCleanBlock(existing, selector);
|
|
1560
|
+
if (expectedHash && hashText(existing.body) !== expectedHash) {
|
|
1561
|
+
throw new Error(`Managed instruction block drift detected for ${selector}`);
|
|
1562
|
+
}
|
|
1450
1563
|
}
|
|
1451
1564
|
return `${content.slice(0, existing.start)}${block}${content.slice(existing.end)}`;
|
|
1452
1565
|
}
|
|
1453
|
-
function removeManagedBlockFromContent(content, selector,
|
|
1566
|
+
function removeManagedBlockFromContent(content, selector, options) {
|
|
1567
|
+
const { expectedHash, allowDrift = false } = options;
|
|
1454
1568
|
const existing = findManagedInstructionBlock(content, selector);
|
|
1455
1569
|
if (!existing) return content;
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1570
|
+
if (!allowDrift) {
|
|
1571
|
+
assertCleanBlock(existing, selector);
|
|
1572
|
+
if (expectedHash && hashText(existing.body) !== expectedHash) {
|
|
1573
|
+
throw new Error(`Managed instruction block drift detected for ${selector}`);
|
|
1574
|
+
}
|
|
1459
1575
|
}
|
|
1460
1576
|
return `${content.slice(0, existing.start)}${content.slice(existing.end)}`;
|
|
1461
1577
|
}
|
|
@@ -1501,7 +1617,7 @@ function assertCleanBlock(block, selector) {
|
|
|
1501
1617
|
}
|
|
1502
1618
|
}
|
|
1503
1619
|
function referencesAgentsMd(content, claudePath, agentsPath) {
|
|
1504
|
-
const claudeDir =
|
|
1620
|
+
const claudeDir = dirname9(claudePath);
|
|
1505
1621
|
for (const rawLine of content.split(/\r?\n/)) {
|
|
1506
1622
|
const line = rawLine.trim();
|
|
1507
1623
|
const atImport = /^@import\s+(.+)$/i.exec(line);
|
|
@@ -1511,7 +1627,7 @@ function referencesAgentsMd(content, claudePath, agentsPath) {
|
|
|
1511
1627
|
const cleaned = referenced.trim().replace(/^["']|["']$/g, "");
|
|
1512
1628
|
if (!/AGENTS\.md$/i.test(cleaned)) continue;
|
|
1513
1629
|
const candidate = cleaned.startsWith("/") ? cleaned : join4(claudeDir, cleaned);
|
|
1514
|
-
if (relative2(
|
|
1630
|
+
if (relative2(dirname9(agentsPath), candidate).replaceAll("\\", "/") === "AGENTS.md") return true;
|
|
1515
1631
|
if (candidate === agentsPath) return true;
|
|
1516
1632
|
}
|
|
1517
1633
|
return false;
|
|
@@ -1533,7 +1649,7 @@ async function writeTextWithTransport(path, content, transport) {
|
|
|
1533
1649
|
const tempRoot = await mkdtemp(join4(tmpdir2(), "agentwheel-instructions-"));
|
|
1534
1650
|
const localPath = join4(tempRoot, basename2(path) || "instructions.md");
|
|
1535
1651
|
try {
|
|
1536
|
-
await
|
|
1652
|
+
await writeFile8(localPath, content, "utf8");
|
|
1537
1653
|
await transport.atomicCopy(localPath, path, "file");
|
|
1538
1654
|
} finally {
|
|
1539
1655
|
await rm3(tempRoot, { recursive: true, force: true });
|
|
@@ -1677,7 +1793,7 @@ async function uninstall(plan, options = {}) {
|
|
|
1677
1793
|
const blockers = plan.operations.filter((operation) => operation.action === "conflict");
|
|
1678
1794
|
throw new Error(`Refusing to uninstall with blocking changes: ${blockers.map((item) => item.relativeDestPath).join(", ")}`);
|
|
1679
1795
|
}
|
|
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);
|
|
1796
|
+
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
1797
|
const kept = plan.operations.filter((operation) => operation.action === "keep" && (!resolvedOptions.force || !isForceRemovableKeep(operation)));
|
|
1682
1798
|
const skipped = plan.operations.filter((operation) => operation.action === "skip");
|
|
1683
1799
|
const removedDrifted = resolvedOptions.force ? plan.operations.filter((operation) => operation.action === "keep" && isForceRemovableKeep(operation)).length : 0;
|
|
@@ -1822,7 +1938,7 @@ async function applyOperation(operation, context) {
|
|
|
1822
1938
|
}
|
|
1823
1939
|
if (operation.mode === managedInstructionBlockMode) {
|
|
1824
1940
|
const selector = managedInstructionSelector(operation.logicalSelector, operation.artifactType, operation.artifactName);
|
|
1825
|
-
const hash2 = await writeManagedInstructionBlock(operation.sourcePath, operation.destPath, selector, transport, operation
|
|
1941
|
+
const hash2 = await writeManagedInstructionBlock(operation.sourcePath, operation.destPath, selector, transport, managedBlockMutationOptions(operation));
|
|
1826
1942
|
if (hash2 !== operation.desiredHash) {
|
|
1827
1943
|
throw new Error(`Managed block hash verification failed for ${operation.relativeDestPath}: expected ${operation.desiredHash}, got ${hash2}`);
|
|
1828
1944
|
}
|
|
@@ -1835,6 +1951,8 @@ async function applyOperation(operation, context) {
|
|
|
1835
1951
|
}
|
|
1836
1952
|
if (operation.mergeStrategy === "json-deep") {
|
|
1837
1953
|
await mergeWithTransport(operation.sourcePath, operation.destPath, transport, mergeJsonFile);
|
|
1954
|
+
} else if (operation.mergeStrategy === "openclaw-json-deep") {
|
|
1955
|
+
await mergeOpenClawJsonWithTransport(operation.sourcePath, operation.destPath, transport);
|
|
1838
1956
|
} else if (operation.mergeStrategy === "yaml-deep") {
|
|
1839
1957
|
await mergeWithTransport(operation.sourcePath, operation.destPath, transport, mergeYamlFile);
|
|
1840
1958
|
} else if (operation.mergeStrategy === "codex-toml-mcp") {
|
|
@@ -1884,7 +2002,7 @@ async function applyOperation(operation, context) {
|
|
|
1884
2002
|
}
|
|
1885
2003
|
if (operation.mode === managedInstructionBlockMode) {
|
|
1886
2004
|
const selector = managedInstructionSelector(operation.logicalSelector, operation.artifactType, operation.artifactName);
|
|
1887
|
-
await removeManagedInstructionBlock(operation.destPath, selector, transport, operation
|
|
2005
|
+
await removeManagedInstructionBlock(operation.destPath, selector, transport, managedBlockMutationOptions(operation));
|
|
1888
2006
|
} else {
|
|
1889
2007
|
await transport.rm(operation.destPath);
|
|
1890
2008
|
}
|
|
@@ -2057,6 +2175,12 @@ function semanticInstallCommands(operation) {
|
|
|
2057
2175
|
if (operation.semanticPlugin) return operation.semanticPlugin.installCommands;
|
|
2058
2176
|
return operation.semanticCommand ? [operation.semanticCommand] : [];
|
|
2059
2177
|
}
|
|
2178
|
+
function managedBlockMutationOptions(operation) {
|
|
2179
|
+
return {
|
|
2180
|
+
expectedHash: operation.overrideDrift ? void 0 : operation.manifestHash,
|
|
2181
|
+
allowDrift: operation.overrideDrift === true
|
|
2182
|
+
};
|
|
2183
|
+
}
|
|
2060
2184
|
async function entryForCompletedOperation(operation, transport, now, graphLockDigest) {
|
|
2061
2185
|
if (operation.action === "remove") return void 0;
|
|
2062
2186
|
if (operation.action === "create" || operation.action === "update") {
|
|
@@ -2170,7 +2294,7 @@ async function mergeWithTransport(sourcePath, destPath, transport, merge) {
|
|
|
2170
2294
|
const localDest = join5(tempRoot, basename3(destPath) || "merged");
|
|
2171
2295
|
try {
|
|
2172
2296
|
if (await transport.pathExists(destPath)) {
|
|
2173
|
-
await
|
|
2297
|
+
await writeFile9(localDest, await transport.readFile(destPath), "utf8");
|
|
2174
2298
|
}
|
|
2175
2299
|
await merge(sourcePath, localDest);
|
|
2176
2300
|
await transport.atomicCopy(localDest, destPath, "file");
|
|
@@ -2178,13 +2302,58 @@ async function mergeWithTransport(sourcePath, destPath, transport, merge) {
|
|
|
2178
2302
|
await rm4(tempRoot, { recursive: true, force: true });
|
|
2179
2303
|
}
|
|
2180
2304
|
}
|
|
2305
|
+
async function mergeOpenClawJsonWithTransport(sourcePath, destPath, transport) {
|
|
2306
|
+
const tempRoot = await mkdtemp2(join5(tmpdir3(), "agentwheel-openclaw-merge-"));
|
|
2307
|
+
const localDest = join5(tempRoot, basename3(destPath) || "openclaw.json");
|
|
2308
|
+
const validationPath = transport.kind === "local" ? localDest : `${destPath}.validate-agentwheel-${process.pid}-${Date.now()}`;
|
|
2309
|
+
try {
|
|
2310
|
+
if (await transport.pathExists(destPath)) {
|
|
2311
|
+
await writeFile9(localDest, await transport.readFile(destPath), "utf8");
|
|
2312
|
+
}
|
|
2313
|
+
await mergeOpenClawJsonFile(sourcePath, localDest);
|
|
2314
|
+
if (transport.kind !== "local") {
|
|
2315
|
+
await transport.atomicCopy(localDest, validationPath, "file");
|
|
2316
|
+
}
|
|
2317
|
+
await validateOpenClawConfig(validationPath, destPath, transport);
|
|
2318
|
+
await transport.atomicCopy(localDest, destPath, "file");
|
|
2319
|
+
} finally {
|
|
2320
|
+
if (transport.kind !== "local") await transport.rm(validationPath);
|
|
2321
|
+
await rm4(tempRoot, { recursive: true, force: true });
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
async function validateOpenClawConfig(configPath, destPath, transport) {
|
|
2325
|
+
if (!transport.execFile) {
|
|
2326
|
+
throw new Error(`Cannot validate OpenClaw config over ${transport.description}: transport does not support command execution.`);
|
|
2327
|
+
}
|
|
2328
|
+
const openClawHome = dirname10(destPath);
|
|
2329
|
+
const bundledBin = join5(openClawHome, "npm", "node_modules", ".bin", "openclaw");
|
|
2330
|
+
const script = String.raw`
|
|
2331
|
+
set -euo pipefail
|
|
2332
|
+
cfg=$1
|
|
2333
|
+
bundled_bin=$2
|
|
2334
|
+
if [ -x "$bundled_bin" ]; then
|
|
2335
|
+
bin="$bundled_bin"
|
|
2336
|
+
elif command -v openclaw >/dev/null 2>&1; then
|
|
2337
|
+
bin="openclaw"
|
|
2338
|
+
else
|
|
2339
|
+
echo "OpenClaw binary not found; cannot validate $cfg" >&2
|
|
2340
|
+
exit 127
|
|
2341
|
+
fi
|
|
2342
|
+
out=$(OPENCLAW_CONFIG_PATH="$cfg" "$bin" config validate --json 2>&1) || {
|
|
2343
|
+
printf '%s\n' "$out" >&2
|
|
2344
|
+
exit 1
|
|
2345
|
+
}
|
|
2346
|
+
printf '%s' "$out" | node -e 'let s=""; process.stdin.on("data", c => s += c); process.stdin.on("end", () => { const data = JSON.parse(s); if (!data.valid) { console.error(JSON.stringify(data, null, 2)); process.exit(1); } });'
|
|
2347
|
+
`;
|
|
2348
|
+
await transport.execFile("bash", ["-lc", script, "agentwheel-openclaw-validate", configPath, bundledBin]);
|
|
2349
|
+
}
|
|
2181
2350
|
|
|
2182
2351
|
// src/install/plan.ts
|
|
2183
|
-
import { basename as
|
|
2352
|
+
import { basename as basename8, join as join16, relative as relative3 } from "path";
|
|
2184
2353
|
|
|
2185
2354
|
// src/staging/codex-subagents.ts
|
|
2186
|
-
import { mkdir as
|
|
2187
|
-
import { basename as basename4, dirname as
|
|
2355
|
+
import { mkdir as mkdir8, readFile as readFile10, writeFile as writeFile10 } from "fs/promises";
|
|
2356
|
+
import { basename as basename4, dirname as dirname11, join as join6 } from "path";
|
|
2188
2357
|
var requiredCodexAgentFields = ["name", "description", "developer_instructions"];
|
|
2189
2358
|
async function renderCodexSubagents(artifacts, stageRoot, adapter) {
|
|
2190
2359
|
if (adapter?.name !== "codex") return artifacts;
|
|
@@ -2210,7 +2379,7 @@ async function renderCodexSubagent(artifact, stageRoot) {
|
|
|
2210
2379
|
const renderedPath = join6(stageRoot, ".agentwheel-rendered", "codex-subagents", `${agentName}.toml`);
|
|
2211
2380
|
const lowerSourcePath = sourcePath.toLowerCase();
|
|
2212
2381
|
if (artifact.kind === "file" && (artifact.name.toLowerCase().endsWith(".toml") || lowerSourcePath.endsWith(".toml"))) {
|
|
2213
|
-
const content = await
|
|
2382
|
+
const content = await readFile10(sourcePath, "utf8");
|
|
2214
2383
|
validateCodexAgentToml(content, sourcePath);
|
|
2215
2384
|
return {
|
|
2216
2385
|
...artifact,
|
|
@@ -2227,10 +2396,10 @@ async function renderCodexSubagent(artifact, stageRoot) {
|
|
|
2227
2396
|
if (artifact.kind === "file" && !artifact.name.toLowerCase().endsWith(".md") && !lowerSourcePath.endsWith(".md")) {
|
|
2228
2397
|
throw new Error(`Codex subagent ${artifact.relativePath} must be a .toml file, .md file, or directory containing AGENTS.md.`);
|
|
2229
2398
|
}
|
|
2230
|
-
const markdown = await
|
|
2399
|
+
const markdown = await readFile10(markdownPath, "utf8");
|
|
2231
2400
|
const toml = markdownToCodexAgentToml(agentName, markdown);
|
|
2232
|
-
await
|
|
2233
|
-
await
|
|
2401
|
+
await mkdir8(dirname11(renderedPath), { recursive: true });
|
|
2402
|
+
await writeFile10(renderedPath, toml, "utf8");
|
|
2234
2403
|
return {
|
|
2235
2404
|
...artifact,
|
|
2236
2405
|
name: agentName,
|
|
@@ -2298,8 +2467,8 @@ function escapeRegExp(value) {
|
|
|
2298
2467
|
}
|
|
2299
2468
|
|
|
2300
2469
|
// src/staging/copilot-artifacts.ts
|
|
2301
|
-
import { mkdir as
|
|
2302
|
-
import { basename as basename5, dirname as
|
|
2470
|
+
import { mkdir as mkdir9, readFile as readFile11, writeFile as writeFile11 } from "fs/promises";
|
|
2471
|
+
import { basename as basename5, dirname as dirname12, join as join7 } from "path";
|
|
2303
2472
|
async function renderCopilotArtifacts(artifacts, stageRoot, adapter) {
|
|
2304
2473
|
if (adapter?.name !== "copilot") return artifacts;
|
|
2305
2474
|
const names = /* @__PURE__ */ new Set();
|
|
@@ -2329,9 +2498,9 @@ async function renderCopilotSubagent(artifact, stageRoot) {
|
|
|
2329
2498
|
if (artifact.kind === "file" && !artifact.name.toLowerCase().endsWith(".md")) {
|
|
2330
2499
|
throw new Error(`Copilot subagent ${artifact.relativePath} must be a .md file or directory containing AGENTS.md.`);
|
|
2331
2500
|
}
|
|
2332
|
-
const markdown = await
|
|
2333
|
-
await
|
|
2334
|
-
await
|
|
2501
|
+
const markdown = await readFile11(markdownPath, "utf8");
|
|
2502
|
+
await mkdir9(dirname12(renderedPath), { recursive: true });
|
|
2503
|
+
await writeFile11(renderedPath, ensureCopilotAgentDescription(agentName, markdown), "utf8");
|
|
2335
2504
|
return {
|
|
2336
2505
|
...artifact,
|
|
2337
2506
|
name: `${agentName}.agent.md`,
|
|
@@ -2383,15 +2552,84 @@ function yamlString(value) {
|
|
|
2383
2552
|
return JSON.stringify(value);
|
|
2384
2553
|
}
|
|
2385
2554
|
|
|
2555
|
+
// src/staging/openclaw-subagents.ts
|
|
2556
|
+
import { mkdir as mkdir10, readFile as readFile12, writeFile as writeFile12 } from "fs/promises";
|
|
2557
|
+
import { basename as basename6, dirname as dirname13, join as join8 } from "path";
|
|
2558
|
+
async function renderOpenClawSubagents(artifacts, stageRoot, adapter) {
|
|
2559
|
+
if (adapter?.name !== "openclaw") return artifacts;
|
|
2560
|
+
const names = /* @__PURE__ */ new Set();
|
|
2561
|
+
const rendered = [];
|
|
2562
|
+
for (const artifact of artifacts) {
|
|
2563
|
+
if (artifact.type !== "subagents") {
|
|
2564
|
+
rendered.push(artifact);
|
|
2565
|
+
continue;
|
|
2566
|
+
}
|
|
2567
|
+
const next = await renderOpenClawSubagent(artifact, stageRoot);
|
|
2568
|
+
if (names.has(next.name)) {
|
|
2569
|
+
throw new Error(`OpenClaw subagents produce duplicate agent id '${next.name}'.`);
|
|
2570
|
+
}
|
|
2571
|
+
names.add(next.name);
|
|
2572
|
+
rendered.push(next);
|
|
2573
|
+
}
|
|
2574
|
+
return rendered;
|
|
2575
|
+
}
|
|
2576
|
+
async function renderOpenClawSubagent(artifact, stageRoot) {
|
|
2577
|
+
const sourcePath = artifact.stagedPath ?? artifact.sourcePath;
|
|
2578
|
+
const agentId = openClawAgentId(artifact);
|
|
2579
|
+
const markdownPath = artifact.kind === "dir" ? join8(sourcePath, "AGENTS.md") : sourcePath;
|
|
2580
|
+
if (artifact.kind === "dir" && !await pathExists(markdownPath)) {
|
|
2581
|
+
throw new Error(`OpenClaw subagent directory ${artifact.relativePath} must contain AGENTS.md.`);
|
|
2582
|
+
}
|
|
2583
|
+
if (artifact.kind === "file" && !artifact.name.toLowerCase().endsWith(".md") && !sourcePath.toLowerCase().endsWith(".md")) {
|
|
2584
|
+
throw new Error(`OpenClaw subagent ${artifact.relativePath} must be a .md file or directory containing AGENTS.md.`);
|
|
2585
|
+
}
|
|
2586
|
+
const parsed = splitFrontmatter3(await readFile12(markdownPath, "utf8"));
|
|
2587
|
+
const body = parsed.body.trim().length > 0 ? parsed.body.trim() : `# ${titleFromAgentId(agentId)}
|
|
2588
|
+
|
|
2589
|
+
${parsed.description ?? `OpenClaw subagent ${agentId}.`}`;
|
|
2590
|
+
const renderedPath = join8(stageRoot, ".agentwheel-rendered", "openclaw-subagents", agentId, "AGENTS.md");
|
|
2591
|
+
await mkdir10(dirname13(renderedPath), { recursive: true });
|
|
2592
|
+
await writeFile12(renderedPath, `${body}
|
|
2593
|
+
`, "utf8");
|
|
2594
|
+
const renderedDir = dirname13(renderedPath);
|
|
2595
|
+
return {
|
|
2596
|
+
...artifact,
|
|
2597
|
+
name: agentId,
|
|
2598
|
+
sourcePath: renderedDir,
|
|
2599
|
+
stagedPath: renderedDir,
|
|
2600
|
+
relativePath: join8("subagents", agentId),
|
|
2601
|
+
kind: "dir",
|
|
2602
|
+
hash: await hashPath(renderedDir)
|
|
2603
|
+
};
|
|
2604
|
+
}
|
|
2605
|
+
function openClawAgentId(artifact) {
|
|
2606
|
+
const raw = artifact.kind === "dir" ? artifact.name : basename6(artifact.name);
|
|
2607
|
+
return raw.replace(/\.agent\.md$/i, "").replace(/\.md$/i, "");
|
|
2608
|
+
}
|
|
2609
|
+
function splitFrontmatter3(markdown) {
|
|
2610
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(markdown);
|
|
2611
|
+
if (!match) return { body: markdown };
|
|
2612
|
+
const frontmatter = match[1] ?? "";
|
|
2613
|
+
const body = markdown.slice(match[0].length);
|
|
2614
|
+
const description = frontmatter.split(/\r?\n/).map((line) => /^description:\s*(?:"([^"]*)"|'([^']*)'|(.+))\s*$/.exec(line.trim())).find((item) => item !== null);
|
|
2615
|
+
return {
|
|
2616
|
+
body,
|
|
2617
|
+
description: description ? (description[1] ?? description[2] ?? description[3] ?? "").trim() : void 0
|
|
2618
|
+
};
|
|
2619
|
+
}
|
|
2620
|
+
function titleFromAgentId(agentId) {
|
|
2621
|
+
return agentId.split(/[-_]/g).filter(Boolean).map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`).join(" ");
|
|
2622
|
+
}
|
|
2623
|
+
|
|
2386
2624
|
// src/targets/plugins/claude.ts
|
|
2387
|
-
import { join as
|
|
2625
|
+
import { join as join10 } from "path";
|
|
2388
2626
|
|
|
2389
2627
|
// src/targets/plugins/common.ts
|
|
2390
|
-
import { readFile as
|
|
2391
|
-
import { join as
|
|
2628
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
2629
|
+
import { join as join9 } from "path";
|
|
2392
2630
|
import { parseDocument } from "yaml";
|
|
2393
2631
|
function pluginStateRoot(request) {
|
|
2394
|
-
return
|
|
2632
|
+
return join9(
|
|
2395
2633
|
request.targetRoot,
|
|
2396
2634
|
".agentwheel",
|
|
2397
2635
|
"plugins",
|
|
@@ -2409,16 +2647,16 @@ function safeNameSegment(value) {
|
|
|
2409
2647
|
return normalized.length > 0 ? normalized : "unnamed";
|
|
2410
2648
|
}
|
|
2411
2649
|
async function jsonPluginName(root, relativeManifestPath, fallback) {
|
|
2412
|
-
const manifestPath =
|
|
2650
|
+
const manifestPath = join9(root, relativeManifestPath);
|
|
2413
2651
|
if (!await pathExists(manifestPath)) return fallback;
|
|
2414
|
-
const parsed = JSON.parse(await
|
|
2652
|
+
const parsed = JSON.parse(await readFile13(manifestPath, "utf8"));
|
|
2415
2653
|
return typeof parsed.name === "string" && parsed.name.trim().length > 0 ? parsed.name.trim() : fallback;
|
|
2416
2654
|
}
|
|
2417
2655
|
async function yamlPluginName(root, relativeManifestPaths, fallback) {
|
|
2418
2656
|
for (const relativeManifestPath of relativeManifestPaths) {
|
|
2419
|
-
const manifestPath =
|
|
2657
|
+
const manifestPath = join9(root, relativeManifestPath);
|
|
2420
2658
|
if (!await pathExists(manifestPath)) continue;
|
|
2421
|
-
const document = parseDocument(await
|
|
2659
|
+
const document = parseDocument(await readFile13(manifestPath, "utf8"));
|
|
2422
2660
|
const parsed = document.toJSON();
|
|
2423
2661
|
if (!isRecord4(parsed)) continue;
|
|
2424
2662
|
for (const key of ["name", "module", "package"]) {
|
|
@@ -2447,7 +2685,7 @@ async function claudePluginSpec(request) {
|
|
|
2447
2685
|
packageName: request.artifact.packageName,
|
|
2448
2686
|
installName: request.installName
|
|
2449
2687
|
});
|
|
2450
|
-
const marketplaceRoot =
|
|
2688
|
+
const marketplaceRoot = join10(stateRoot, "marketplace");
|
|
2451
2689
|
const scope = claudeScope(request.installationType);
|
|
2452
2690
|
const selector = `${pluginName}@${marketplaceName}`;
|
|
2453
2691
|
return {
|
|
@@ -2472,7 +2710,7 @@ function claudeScope(installationType) {
|
|
|
2472
2710
|
}
|
|
2473
2711
|
|
|
2474
2712
|
// src/targets/plugins/codex.ts
|
|
2475
|
-
import { join as
|
|
2713
|
+
import { join as join11 } from "path";
|
|
2476
2714
|
async function codexPluginSpec(request) {
|
|
2477
2715
|
const pluginName = await jsonPluginName(request.sourcePath, ".codex-plugin/plugin.json", request.installName);
|
|
2478
2716
|
const marketplaceName = agentwheelMarketplaceName(request.artifact.packageName, pluginName);
|
|
@@ -2483,7 +2721,7 @@ async function codexPluginSpec(request) {
|
|
|
2483
2721
|
packageName: request.artifact.packageName,
|
|
2484
2722
|
installName: request.installName
|
|
2485
2723
|
});
|
|
2486
|
-
const marketplaceRoot =
|
|
2724
|
+
const marketplaceRoot = join11(stateRoot, "marketplace");
|
|
2487
2725
|
const selector = `${pluginName}@${marketplaceName}`;
|
|
2488
2726
|
return {
|
|
2489
2727
|
runtime: "codex",
|
|
@@ -2502,7 +2740,7 @@ async function codexPluginSpec(request) {
|
|
|
2502
2740
|
}
|
|
2503
2741
|
|
|
2504
2742
|
// src/targets/plugins/copilot.ts
|
|
2505
|
-
import { join as
|
|
2743
|
+
import { join as join12 } from "path";
|
|
2506
2744
|
async function copilotPluginSpec(request) {
|
|
2507
2745
|
if (request.installationType !== "user") {
|
|
2508
2746
|
throw new Error("Copilot plugins are persistent user-level installs only; pass --installation-type user.");
|
|
@@ -2515,7 +2753,7 @@ async function copilotPluginSpec(request) {
|
|
|
2515
2753
|
packageName: request.artifact.packageName,
|
|
2516
2754
|
installName: request.installName
|
|
2517
2755
|
});
|
|
2518
|
-
const pluginRoot =
|
|
2756
|
+
const pluginRoot = join12(stateRoot, "plugin");
|
|
2519
2757
|
return {
|
|
2520
2758
|
runtime: "copilot",
|
|
2521
2759
|
pluginName,
|
|
@@ -2526,7 +2764,7 @@ async function copilotPluginSpec(request) {
|
|
|
2526
2764
|
}
|
|
2527
2765
|
|
|
2528
2766
|
// src/targets/plugins/hermes.ts
|
|
2529
|
-
import { join as
|
|
2767
|
+
import { join as join13 } from "path";
|
|
2530
2768
|
async function hermesPluginSpec(request) {
|
|
2531
2769
|
if (request.installationType !== "user") {
|
|
2532
2770
|
throw new Error("Hermes plugins are user-level installs only; pass --installation-type user.");
|
|
@@ -2539,7 +2777,7 @@ async function hermesPluginSpec(request) {
|
|
|
2539
2777
|
packageName: request.artifact.packageName,
|
|
2540
2778
|
installName: request.installName
|
|
2541
2779
|
});
|
|
2542
|
-
const repoRoot =
|
|
2780
|
+
const repoRoot = join13(stateRoot, "repo");
|
|
2543
2781
|
return {
|
|
2544
2782
|
runtime: "hermes",
|
|
2545
2783
|
pluginName,
|
|
@@ -2550,8 +2788,8 @@ async function hermesPluginSpec(request) {
|
|
|
2550
2788
|
}
|
|
2551
2789
|
|
|
2552
2790
|
// src/targets/plugins/openclaw.ts
|
|
2553
|
-
import { readFile as
|
|
2554
|
-
import { join as
|
|
2791
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
2792
|
+
import { join as join14 } from "path";
|
|
2555
2793
|
function openClawPluginInstallCommand(request) {
|
|
2556
2794
|
return ["openclaw", "plugins", "install", "--force", request.path];
|
|
2557
2795
|
}
|
|
@@ -2559,6 +2797,15 @@ function openClawPluginUninstallCommand(pluginName) {
|
|
|
2559
2797
|
return ["openclaw", "plugins", "uninstall", pluginName, "--force"];
|
|
2560
2798
|
}
|
|
2561
2799
|
async function openClawPluginSpec(request) {
|
|
2800
|
+
if (request.format === "openclaw-clawhub-plugin") {
|
|
2801
|
+
const metadata = await openClawClawHubPluginMetadata(request.path, request.fallbackPluginName);
|
|
2802
|
+
return {
|
|
2803
|
+
runtime: "openclaw",
|
|
2804
|
+
pluginName: metadata.pluginName,
|
|
2805
|
+
installCommands: [openClawPluginInstallCommand({ path: metadata.installSpec, dryRun: true })],
|
|
2806
|
+
uninstallCommands: [openClawPluginUninstallCommand(metadata.pluginName)]
|
|
2807
|
+
};
|
|
2808
|
+
}
|
|
2562
2809
|
const pluginName = await openClawPluginName(request.path, request.fallbackPluginName);
|
|
2563
2810
|
return {
|
|
2564
2811
|
runtime: "openclaw",
|
|
@@ -2569,19 +2816,39 @@ async function openClawPluginSpec(request) {
|
|
|
2569
2816
|
}
|
|
2570
2817
|
async function openClawPluginName(root, fallback) {
|
|
2571
2818
|
for (const manifestName of ["plugin.json", "openclaw.plugin.json"]) {
|
|
2572
|
-
const manifestPath =
|
|
2819
|
+
const manifestPath = join14(root, manifestName);
|
|
2573
2820
|
if (!await pathExists(manifestPath)) continue;
|
|
2574
|
-
const parsed = JSON.parse(await
|
|
2821
|
+
const parsed = JSON.parse(await readFile14(manifestPath, "utf8"));
|
|
2575
2822
|
if (typeof parsed.name === "string" && parsed.name.trim().length > 0) return parsed.name.trim();
|
|
2576
2823
|
}
|
|
2577
2824
|
return fallback;
|
|
2578
2825
|
}
|
|
2826
|
+
async function openClawClawHubPluginMetadata(root, fallback) {
|
|
2827
|
+
const metadataPath = join14(root, "clawhub.json");
|
|
2828
|
+
if (!await pathExists(metadataPath)) {
|
|
2829
|
+
throw new Error("OpenClaw ClawHub plugins must contain clawhub.json");
|
|
2830
|
+
}
|
|
2831
|
+
const parsed = JSON.parse(await readFile14(metadataPath, "utf8"));
|
|
2832
|
+
const installSpec = stringField(parsed.installSpec);
|
|
2833
|
+
if (!installSpec?.startsWith("clawhub:")) {
|
|
2834
|
+
throw new Error("OpenClaw ClawHub plugin metadata must declare installSpec starting with clawhub:");
|
|
2835
|
+
}
|
|
2836
|
+
const pluginName = stringField(parsed.runtimeId) ?? installNameFor(stringField(parsed.name) ?? fallback);
|
|
2837
|
+
return { installSpec, pluginName };
|
|
2838
|
+
}
|
|
2839
|
+
function stringField(value) {
|
|
2840
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
2841
|
+
}
|
|
2842
|
+
function installNameFor(value) {
|
|
2843
|
+
return value.split("/").at(-1).replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "clawhub-plugin";
|
|
2844
|
+
}
|
|
2579
2845
|
|
|
2580
2846
|
// src/targets/plugins/index.ts
|
|
2581
2847
|
async function semanticPluginSpecForArtifact(request) {
|
|
2582
2848
|
if (request.semantic === "openclaw-plugin") {
|
|
2583
2849
|
return openClawPluginSpec({
|
|
2584
2850
|
path: request.sourcePath,
|
|
2851
|
+
format: request.artifact.format,
|
|
2585
2852
|
fallbackPluginName: request.installName
|
|
2586
2853
|
});
|
|
2587
2854
|
}
|
|
@@ -2601,8 +2868,8 @@ async function semanticPluginSpecForArtifact(request) {
|
|
|
2601
2868
|
}
|
|
2602
2869
|
|
|
2603
2870
|
// src/validation/artifacts.ts
|
|
2604
|
-
import { readFile as
|
|
2605
|
-
import { basename as
|
|
2871
|
+
import { readFile as readFile15 } from "fs/promises";
|
|
2872
|
+
import { basename as basename7, join as join15 } from "path";
|
|
2606
2873
|
import { parseDocument as parseDocument2 } from "yaml";
|
|
2607
2874
|
|
|
2608
2875
|
// src/model/selection.ts
|
|
@@ -2657,7 +2924,7 @@ function subagentBaseName(name) {
|
|
|
2657
2924
|
|
|
2658
2925
|
// src/validation/artifacts.ts
|
|
2659
2926
|
var behavioralRuleFormats = ["markdown-rule", "claude-markdown-rule", "copilot-instruction-rule"];
|
|
2660
|
-
var pluginFormats = ["claude-plugin", "codex-plugin", "hermes-plugin", "copilot-plugin", "openclaw-plugin"];
|
|
2927
|
+
var pluginFormats = ["claude-plugin", "codex-plugin", "hermes-plugin", "copilot-plugin", "openclaw-plugin", "openclaw-clawhub-plugin"];
|
|
2661
2928
|
async function filterArtifactsByInstallFormat(artifacts, adapter, installationType, options = {}) {
|
|
2662
2929
|
const selectedSet = new Set(normalizeArtifactSelectors(options.selected ?? []) ?? []);
|
|
2663
2930
|
const kept = [];
|
|
@@ -2766,6 +3033,7 @@ async function inferArtifactFormat(artifact, target) {
|
|
|
2766
3033
|
return void 0;
|
|
2767
3034
|
}
|
|
2768
3035
|
if (artifact.type === "plugins" && target.semantic === "openclaw-plugin") {
|
|
3036
|
+
if (artifact.kind === "dir" && await pathExists(join15(artifactPath(artifact), "clawhub.json"))) return "openclaw-clawhub-plugin";
|
|
2769
3037
|
if (artifact.kind === "dir" && (await openClawPluginManifestPaths(artifact)).length > 0) return "openclaw-plugin";
|
|
2770
3038
|
}
|
|
2771
3039
|
return void 0;
|
|
@@ -2781,9 +3049,9 @@ function expectedFormats(artifact, target) {
|
|
|
2781
3049
|
return behavioralRuleFormats;
|
|
2782
3050
|
}
|
|
2783
3051
|
if (artifact.type === "plugins") {
|
|
2784
|
-
if (isPluginFormat(target.semantic)) return [target.semantic];
|
|
2785
3052
|
const declared = target.formats?.filter((format) => pluginFormats.includes(format));
|
|
2786
3053
|
if (target.formats?.length) return declared?.length ? declared : pluginFormats;
|
|
3054
|
+
if (isPluginFormat(target.semantic)) return [target.semantic];
|
|
2787
3055
|
}
|
|
2788
3056
|
return target.formats;
|
|
2789
3057
|
}
|
|
@@ -2792,7 +3060,10 @@ async function validateKnownFormat(artifact, format, target) {
|
|
|
2792
3060
|
if (format === "markdown-rule" || format === "claude-markdown-rule" || format === "copilot-instruction-rule") {
|
|
2793
3061
|
return validateMarkdownRule(artifact, format);
|
|
2794
3062
|
}
|
|
2795
|
-
if (format === "openclaw-
|
|
3063
|
+
if (format === "openclaw-clawhub-plugin") {
|
|
3064
|
+
return validateOpenClawClawHubPlugin(artifact);
|
|
3065
|
+
}
|
|
3066
|
+
if (format === "openclaw-plugin") {
|
|
2796
3067
|
return validateOpenClawPlugin(artifact);
|
|
2797
3068
|
}
|
|
2798
3069
|
if (format === "claude-plugin" || target.semantic === "claude-plugin") {
|
|
@@ -2814,7 +3085,7 @@ async function validateGenericStructure(artifact, target) {
|
|
|
2814
3085
|
const issues = [];
|
|
2815
3086
|
if (artifact.type === "skills") {
|
|
2816
3087
|
if (artifact.kind === "dir") {
|
|
2817
|
-
const skillMd =
|
|
3088
|
+
const skillMd = join15(artifactPath(artifact), "SKILL.md");
|
|
2818
3089
|
if (!await pathExists(skillMd)) {
|
|
2819
3090
|
issues.push({ artifact, message: "skill directory must contain SKILL.md" });
|
|
2820
3091
|
} else {
|
|
@@ -2826,10 +3097,17 @@ async function validateGenericStructure(artifact, target) {
|
|
|
2826
3097
|
issues.push(...await validateSkillFrontmatter(artifact, artifactPath(artifact)));
|
|
2827
3098
|
}
|
|
2828
3099
|
}
|
|
2829
|
-
if (target.merge === "json-deep") {
|
|
3100
|
+
if (target.merge === "json-deep" || target.merge === "openclaw-json-deep") {
|
|
2830
3101
|
const parsed = await parseJsonObjectArtifact(artifact);
|
|
2831
3102
|
if (!parsed.ok) issues.push({ artifact, message: parsed.message });
|
|
2832
3103
|
}
|
|
3104
|
+
if (artifact.type === "subagents" && target.semantic === "openclaw-subagent") {
|
|
3105
|
+
if (artifact.kind !== "dir") {
|
|
3106
|
+
issues.push({ artifact, message: "OpenClaw subagent artifacts must render to a workspace directory containing AGENTS.md" });
|
|
3107
|
+
} else if (!await pathExists(join15(artifactPath(artifact), "AGENTS.md"))) {
|
|
3108
|
+
issues.push({ artifact, message: "OpenClaw subagent workspace directory must contain AGENTS.md" });
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
2833
3111
|
if (target.merge === "yaml-deep") {
|
|
2834
3112
|
const parsed = await parseYamlObjectArtifact(artifact);
|
|
2835
3113
|
if (!parsed.ok) issues.push({ artifact, message: parsed.message });
|
|
@@ -2847,7 +3125,7 @@ async function validateGenericStructure(artifact, target) {
|
|
|
2847
3125
|
async function validateSkillFrontmatter(artifact, skillMdPath) {
|
|
2848
3126
|
let content;
|
|
2849
3127
|
try {
|
|
2850
|
-
content = await
|
|
3128
|
+
content = await readFile15(skillMdPath, "utf8");
|
|
2851
3129
|
} catch (error) {
|
|
2852
3130
|
return [{ artifact, message: `could not read SKILL.md: ${errorMessage(error)}` }];
|
|
2853
3131
|
}
|
|
@@ -2900,7 +3178,7 @@ function validatePluginArtifact(artifact, format) {
|
|
|
2900
3178
|
async function validateJsonPluginDescriptor(artifact, relativeManifestPath, label) {
|
|
2901
3179
|
const generic = validatePluginArtifact(artifact, `${label.toLowerCase()}-plugin`);
|
|
2902
3180
|
if (generic.length > 0) return generic;
|
|
2903
|
-
const manifestPath =
|
|
3181
|
+
const manifestPath = join15(artifactPath(artifact), relativeManifestPath);
|
|
2904
3182
|
if (!await pathExists(manifestPath)) {
|
|
2905
3183
|
return [{ artifact, message: `${label} plugins must contain ${relativeManifestPath}` }];
|
|
2906
3184
|
}
|
|
@@ -2911,26 +3189,26 @@ async function validateHermesPlugin(artifact) {
|
|
|
2911
3189
|
const generic = validatePluginArtifact(artifact, "hermes-plugin");
|
|
2912
3190
|
if (generic.length > 0) return generic;
|
|
2913
3191
|
const root = artifactPath(artifact);
|
|
2914
|
-
const manifestPaths = [
|
|
3192
|
+
const manifestPaths = [join15(root, "plugin.yaml"), join15(root, "plugin.yml")];
|
|
2915
3193
|
const manifestPath = await firstExistingPath(manifestPaths);
|
|
2916
3194
|
if (!manifestPath) {
|
|
2917
3195
|
return [{ artifact, message: "Hermes plugins must contain plugin.yaml or plugin.yml" }];
|
|
2918
3196
|
}
|
|
2919
3197
|
try {
|
|
2920
|
-
const document = parseDocument2(await
|
|
3198
|
+
const document = parseDocument2(await readFile15(manifestPath, "utf8"));
|
|
2921
3199
|
if (document.errors.length > 0) {
|
|
2922
|
-
return [{ artifact, message: `Hermes ${
|
|
3200
|
+
return [{ artifact, message: `Hermes ${basename7(manifestPath)} must be valid YAML: ${document.errors[0]?.message ?? "parse error"}` }];
|
|
2923
3201
|
}
|
|
2924
3202
|
const parsed = document.toJSON();
|
|
2925
3203
|
if (!isUnknownRecord(parsed)) {
|
|
2926
|
-
return [{ artifact, message: `Hermes ${
|
|
3204
|
+
return [{ artifact, message: `Hermes ${basename7(manifestPath)} must contain a YAML object` }];
|
|
2927
3205
|
}
|
|
2928
3206
|
if (!hasStringField(parsed, "name") && !hasStringField(parsed, "module") && !hasStringField(parsed, "package") && !hasNestedPackageName(parsed)) {
|
|
2929
|
-
return [{ artifact, message: `Hermes ${
|
|
3207
|
+
return [{ artifact, message: `Hermes ${basename7(manifestPath)} must declare a non-empty name, module, or package name` }];
|
|
2930
3208
|
}
|
|
2931
3209
|
return [];
|
|
2932
3210
|
} catch (error) {
|
|
2933
|
-
return [{ artifact, message: `Hermes ${
|
|
3211
|
+
return [{ artifact, message: `Hermes ${basename7(manifestPath)} must be valid YAML: ${errorMessage(error)}` }];
|
|
2934
3212
|
}
|
|
2935
3213
|
}
|
|
2936
3214
|
async function firstExistingPath(paths) {
|
|
@@ -2941,16 +3219,16 @@ async function firstExistingPath(paths) {
|
|
|
2941
3219
|
}
|
|
2942
3220
|
async function parseJsonPluginManifest(manifestPath, label) {
|
|
2943
3221
|
try {
|
|
2944
|
-
const parsed = JSON.parse(await
|
|
3222
|
+
const parsed = JSON.parse(await readFile15(manifestPath, "utf8"));
|
|
2945
3223
|
if (!isRecord5(parsed)) {
|
|
2946
|
-
return { ok: false, message: `${label} ${
|
|
3224
|
+
return { ok: false, message: `${label} ${basename7(manifestPath)} must be a JSON object` };
|
|
2947
3225
|
}
|
|
2948
3226
|
if (typeof parsed.name !== "string" || parsed.name.trim().length === 0) {
|
|
2949
|
-
return { ok: false, message: `${label} ${
|
|
3227
|
+
return { ok: false, message: `${label} ${basename7(manifestPath)} must declare a non-empty name` };
|
|
2950
3228
|
}
|
|
2951
3229
|
return { ok: true, name: parsed.name.trim() };
|
|
2952
3230
|
} catch (error) {
|
|
2953
|
-
return { ok: false, message: `${label} ${
|
|
3231
|
+
return { ok: false, message: `${label} ${basename7(manifestPath)} must be valid JSON: ${errorMessage(error)}` };
|
|
2954
3232
|
}
|
|
2955
3233
|
}
|
|
2956
3234
|
async function validateOpenClawPlugin(artifact) {
|
|
@@ -2976,16 +3254,43 @@ async function validateOpenClawPlugin(artifact) {
|
|
|
2976
3254
|
}
|
|
2977
3255
|
return issues;
|
|
2978
3256
|
}
|
|
3257
|
+
async function validateOpenClawClawHubPlugin(artifact) {
|
|
3258
|
+
if (artifact.type !== "plugins") {
|
|
3259
|
+
return [{ artifact, message: "openclaw-clawhub-plugin format is only valid for plugins artifacts" }];
|
|
3260
|
+
}
|
|
3261
|
+
if (artifact.kind !== "dir") {
|
|
3262
|
+
return [{ artifact, message: "OpenClaw ClawHub plugins must be directory artifacts" }];
|
|
3263
|
+
}
|
|
3264
|
+
const metadataPath = join15(artifactPath(artifact), "clawhub.json");
|
|
3265
|
+
if (!await pathExists(metadataPath)) {
|
|
3266
|
+
return [{ artifact, message: "OpenClaw ClawHub plugins must contain clawhub.json" }];
|
|
3267
|
+
}
|
|
3268
|
+
try {
|
|
3269
|
+
const parsed = JSON.parse(await readFile15(metadataPath, "utf8"));
|
|
3270
|
+
if (!isRecord5(parsed)) {
|
|
3271
|
+
return [{ artifact, message: "OpenClaw ClawHub clawhub.json must be a JSON object" }];
|
|
3272
|
+
}
|
|
3273
|
+
if (typeof parsed.installSpec !== "string" || !parsed.installSpec.trim().startsWith("clawhub:")) {
|
|
3274
|
+
return [{ artifact, message: "OpenClaw ClawHub clawhub.json must declare installSpec starting with clawhub:" }];
|
|
3275
|
+
}
|
|
3276
|
+
if (typeof parsed.name !== "string" || parsed.name.trim().length === 0) {
|
|
3277
|
+
return [{ artifact, message: "OpenClaw ClawHub clawhub.json must declare a non-empty name" }];
|
|
3278
|
+
}
|
|
3279
|
+
return [];
|
|
3280
|
+
} catch (error) {
|
|
3281
|
+
return [{ artifact, message: `OpenClaw ClawHub clawhub.json must be valid JSON: ${errorMessage(error)}` }];
|
|
3282
|
+
}
|
|
3283
|
+
}
|
|
2979
3284
|
async function openClawPluginManifestPaths(artifact) {
|
|
2980
3285
|
const root = artifactPath(artifact);
|
|
2981
|
-
const candidates = [
|
|
3286
|
+
const candidates = [join15(root, "plugin.json"), join15(root, "openclaw.plugin.json")];
|
|
2982
3287
|
const existing = await Promise.all(candidates.map(async (candidate) => await pathExists(candidate) ? candidate : void 0));
|
|
2983
3288
|
return existing.filter((candidate) => candidate !== void 0);
|
|
2984
3289
|
}
|
|
2985
3290
|
async function parseOpenClawPluginManifest(manifestPath) {
|
|
2986
|
-
const manifestName =
|
|
3291
|
+
const manifestName = basename7(manifestPath);
|
|
2987
3292
|
try {
|
|
2988
|
-
const parsed = JSON.parse(await
|
|
3293
|
+
const parsed = JSON.parse(await readFile15(manifestPath, "utf8"));
|
|
2989
3294
|
if (!isRecord5(parsed)) {
|
|
2990
3295
|
return { ok: false, path: manifestPath, message: `OpenClaw ${manifestName} must be a JSON object` };
|
|
2991
3296
|
}
|
|
@@ -3002,7 +3307,7 @@ async function parseJsonObjectArtifact(artifact) {
|
|
|
3002
3307
|
return { ok: false, message: "merge artifacts must be JSON files" };
|
|
3003
3308
|
}
|
|
3004
3309
|
try {
|
|
3005
|
-
const parsed = JSON.parse(await
|
|
3310
|
+
const parsed = JSON.parse(await readFile15(artifactPath(artifact), "utf8"));
|
|
3006
3311
|
if (!isRecord5(parsed)) return { ok: false, message: "merge artifacts must contain a JSON object" };
|
|
3007
3312
|
return { ok: true, value: parsed };
|
|
3008
3313
|
} catch (error) {
|
|
@@ -3014,7 +3319,7 @@ async function parseYamlObjectArtifact(artifact) {
|
|
|
3014
3319
|
return { ok: false, message: "merge artifacts must be YAML files" };
|
|
3015
3320
|
}
|
|
3016
3321
|
try {
|
|
3017
|
-
const document = parseDocument2(await
|
|
3322
|
+
const document = parseDocument2(await readFile15(artifactPath(artifact), "utf8"));
|
|
3018
3323
|
if (document.errors.length > 0) {
|
|
3019
3324
|
return { ok: false, message: `merge artifact must be valid YAML: ${document.errors[0]?.message ?? "parse error"}` };
|
|
3020
3325
|
}
|
|
@@ -3041,7 +3346,7 @@ function artifactLabel(artifact) {
|
|
|
3041
3346
|
return `${owner}${artifact.type}/${artifact.name}`;
|
|
3042
3347
|
}
|
|
3043
3348
|
function hasExtension(artifact, extension) {
|
|
3044
|
-
return
|
|
3349
|
+
return basename7(artifact.name).toLowerCase().endsWith(extension) || basename7(artifactPath(artifact)).toLowerCase().endsWith(extension);
|
|
3045
3350
|
}
|
|
3046
3351
|
function isPluginFormat(value) {
|
|
3047
3352
|
return value !== void 0 && pluginFormats.includes(value);
|
|
@@ -3139,7 +3444,7 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
|
|
|
3139
3444
|
}
|
|
3140
3445
|
const currentHash2 = await transport.hashPath(op.destPath);
|
|
3141
3446
|
if (existing2 && existing2.sourceHash === op.desiredHash) {
|
|
3142
|
-
operations.push({ ...op, action: "skip", currentHash: currentHash2, manifestHash: existing2.hash, reason: "merged source already up to date" });
|
|
3447
|
+
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
3448
|
} else {
|
|
3144
3449
|
operations.push({ ...op, action: "update", currentHash: currentHash2, manifestHash: existing2?.hash, reason: existing2 ? "merge source changed" : "merge into existing destination" });
|
|
3145
3450
|
}
|
|
@@ -3186,6 +3491,7 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
|
|
|
3186
3491
|
action: "update",
|
|
3187
3492
|
currentHash,
|
|
3188
3493
|
manifestHash: existing.hash,
|
|
3494
|
+
overrideDrift: true,
|
|
3189
3495
|
reason: reasonWithComposedDiff("force replacing drifted managed destination", op.composedFrom, existing.composedFrom),
|
|
3190
3496
|
composedFromDiff
|
|
3191
3497
|
});
|
|
@@ -3218,7 +3524,7 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
|
|
|
3218
3524
|
for (const entry of effectiveEntries) {
|
|
3219
3525
|
if (desired.has(entry.path)) continue;
|
|
3220
3526
|
const semanticPlugin = entry.semanticPlugin;
|
|
3221
|
-
const destPath = semanticPlugin ? targetRoot :
|
|
3527
|
+
const destPath = semanticPlugin ? targetRoot : join16(targetRoot, entry.path);
|
|
3222
3528
|
if (!semanticPlugin && !await transport.pathExists(destPath)) continue;
|
|
3223
3529
|
const currentHash = semanticPlugin ? entry.hash : await currentEntryHash(entry, destPath, transport);
|
|
3224
3530
|
if (workspaceOwner && !entryOwnedByWorkspace(entry, workspaceOwner)) {
|
|
@@ -3245,7 +3551,7 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
|
|
|
3245
3551
|
mode: entry.mode,
|
|
3246
3552
|
composedFrom: entry.composedFrom,
|
|
3247
3553
|
...operationMetadataFromEntry(entry),
|
|
3248
|
-
...options.forceDrift ? { action: "remove", reason: "force removing drifted stale managed destination" } : {}
|
|
3554
|
+
...options.forceDrift ? { action: "remove", reason: "force removing drifted stale managed destination", overrideDrift: true } : {}
|
|
3249
3555
|
});
|
|
3250
3556
|
} else {
|
|
3251
3557
|
operations.push({
|
|
@@ -3367,15 +3673,15 @@ async function prepareManagedBlockOperations(desiredOps, adapter, targetRoot, tr
|
|
|
3367
3673
|
return prepared;
|
|
3368
3674
|
}
|
|
3369
3675
|
async function shouldSkipClaudeBridge(adapter, op, targetRoot, transport) {
|
|
3370
|
-
if (adapter.name !== "claude" || op.artifactType !== "instructions" ||
|
|
3676
|
+
if (adapter.name !== "claude" || op.artifactType !== "instructions" || basename8(op.destPath).toLowerCase() !== "claude.md") {
|
|
3371
3677
|
return false;
|
|
3372
3678
|
}
|
|
3373
|
-
const agentsPath =
|
|
3679
|
+
const agentsPath = join16(targetRoot, "AGENTS.md");
|
|
3374
3680
|
return claudeInstructionBridgesAgents(op.destPath, agentsPath, transport);
|
|
3375
3681
|
}
|
|
3376
3682
|
async function warnOnCopilotDoubleRead(adapter, op, targetRoot, transport, options) {
|
|
3377
|
-
if (adapter.name !== "claude" || op.artifactType !== "instructions" ||
|
|
3378
|
-
const agentsPath =
|
|
3683
|
+
if (adapter.name !== "claude" || op.artifactType !== "instructions" || basename8(op.destPath).toLowerCase() !== "claude.md") return;
|
|
3684
|
+
const agentsPath = join16(targetRoot, "AGENTS.md");
|
|
3379
3685
|
if (!await transport.pathExists(agentsPath)) return;
|
|
3380
3686
|
if (await claudeInstructionBridgesAgents(op.destPath, agentsPath, transport)) return;
|
|
3381
3687
|
options.warn?.("CLAUDE.md and AGENTS.md are separate instruction files; if Copilot is active it may read the managed instructions twice.");
|
|
@@ -3422,6 +3728,7 @@ async function planManagedBlockOperation(op, manifestByPath, transport, options,
|
|
|
3422
3728
|
action: "update",
|
|
3423
3729
|
currentHash: state.hash,
|
|
3424
3730
|
manifestHash: existing.hash,
|
|
3731
|
+
overrideDrift: true,
|
|
3425
3732
|
reason: reasonWithComposedDiff("force replacing drifted managed instruction block", op.composedFrom, existing.composedFrom),
|
|
3426
3733
|
composedFromDiff
|
|
3427
3734
|
}];
|
|
@@ -3452,13 +3759,13 @@ async function currentEntryHash(entry, destPath, transport) {
|
|
|
3452
3759
|
if (entry.mode !== managedInstructionBlockMode) return transport.hashPath(destPath);
|
|
3453
3760
|
const selector = managedInstructionSelector("logicalSelector" in entry ? entry.logicalSelector : void 0, entry.artifactType, entry.artifactName);
|
|
3454
3761
|
const state = await readManagedInstructionBlockState(destPath, selector, transport);
|
|
3455
|
-
return state.
|
|
3762
|
+
return state.hash;
|
|
3456
3763
|
}
|
|
3457
3764
|
async function canStrictlyAdoptLegacyEntry(entry, op, targetRoot, transport) {
|
|
3458
3765
|
if (entry.artifactType !== op.artifactType || entry.artifactName !== op.artifactName) return false;
|
|
3459
3766
|
if (!op.desiredHash || entry.sourceHash !== op.desiredHash) return false;
|
|
3460
3767
|
if (!packageIdentityMatches(entry, op)) return false;
|
|
3461
|
-
const destPath =
|
|
3768
|
+
const destPath = join16(targetRoot, entry.path);
|
|
3462
3769
|
if (!await transport.pathExists(destPath)) return false;
|
|
3463
3770
|
return await transport.hashPath(destPath) === entry.hash;
|
|
3464
3771
|
}
|
|
@@ -3589,7 +3896,7 @@ function keepForeignManifestEntryOperation(entry, targetRoot, workspaceOwner, op
|
|
|
3589
3896
|
artifactType: entry.artifactType,
|
|
3590
3897
|
artifactName: entry.artifactName,
|
|
3591
3898
|
kind: entry.kind,
|
|
3592
|
-
destPath: operation?.destPath ??
|
|
3899
|
+
destPath: operation?.destPath ?? join16(targetRoot, entry.path),
|
|
3593
3900
|
relativeDestPath: entry.path,
|
|
3594
3901
|
desiredHash: entry.sourceHash,
|
|
3595
3902
|
currentHash: currentHash ?? operation?.currentHash ?? entry.hash,
|
|
@@ -3657,7 +3964,7 @@ async function operationForArtifact(artifact, adapter, targetRoot, installationT
|
|
|
3657
3964
|
}
|
|
3658
3965
|
}
|
|
3659
3966
|
if (artifact.type === "subagents" && target.semantic === "codex-subagent") {
|
|
3660
|
-
const destPath2 =
|
|
3967
|
+
const destPath2 = join16(targetRoot, target.dest, `${installName.replace(/\.toml$/i, "")}.toml`);
|
|
3661
3968
|
return {
|
|
3662
3969
|
action: "create",
|
|
3663
3970
|
artifactType: artifact.type,
|
|
@@ -3675,7 +3982,7 @@ async function operationForArtifact(artifact, adapter, targetRoot, installationT
|
|
|
3675
3982
|
installName: installName.replace(/\.toml$/i, "")
|
|
3676
3983
|
};
|
|
3677
3984
|
}
|
|
3678
|
-
const destPath = artifact.type === "instructions" || artifact.type === "settings" || isFileTarget(target.dest) ?
|
|
3985
|
+
const destPath = artifact.type === "instructions" || artifact.type === "settings" || isFileTarget(target.dest) ? join16(targetRoot, target.dest) : join16(targetRoot, target.dest, installName);
|
|
3679
3986
|
return {
|
|
3680
3987
|
action: "create",
|
|
3681
3988
|
artifactType: artifact.type,
|
|
@@ -3754,12 +4061,12 @@ function isPendingInstallOperation(operation) {
|
|
|
3754
4061
|
}
|
|
3755
4062
|
|
|
3756
4063
|
// src/install/uninstall.ts
|
|
3757
|
-
import { join as
|
|
4064
|
+
import { join as join17 } from "path";
|
|
3758
4065
|
async function createUninstallPlan(manifest, transport = localTransport) {
|
|
3759
4066
|
const operations = [];
|
|
3760
4067
|
for (const entry of manifest.entries) {
|
|
3761
4068
|
const semanticPlugin = "semanticPlugin" in entry ? entry.semanticPlugin : void 0;
|
|
3762
|
-
const destPath = semanticPlugin ? manifest.targetRoot :
|
|
4069
|
+
const destPath = semanticPlugin ? manifest.targetRoot : join17(manifest.targetRoot, entry.path);
|
|
3763
4070
|
if (!semanticPlugin && !await transport.pathExists(destPath)) continue;
|
|
3764
4071
|
const currentHash = semanticPlugin ? entry.hash : await currentEntryHash2(entry, destPath, transport);
|
|
3765
4072
|
if (currentHash !== entry.hash) {
|
|
@@ -3830,7 +4137,7 @@ async function createOwnershipUninstallPlan(manifest, remainingDesired, adapter,
|
|
|
3830
4137
|
const operations = [];
|
|
3831
4138
|
for (const entry of manifest.entries) {
|
|
3832
4139
|
const semanticPlugin = "semanticPlugin" in entry ? entry.semanticPlugin : void 0;
|
|
3833
|
-
const destPath = semanticPlugin ? manifest.targetRoot :
|
|
4140
|
+
const destPath = semanticPlugin ? manifest.targetRoot : join17(manifest.targetRoot, entry.path);
|
|
3834
4141
|
if (!semanticPlugin && !await transport.pathExists(destPath)) continue;
|
|
3835
4142
|
const currentHash = semanticPlugin ? entry.hash : await currentEntryHash2(entry, destPath, transport);
|
|
3836
4143
|
const remainingOwners = ownersByPath.get(entry.path) ?? [];
|
|
@@ -3924,7 +4231,7 @@ async function currentEntryHash2(entry, destPath, transport) {
|
|
|
3924
4231
|
if (entry.mode !== managedInstructionBlockMode) return transport.hashPath(destPath);
|
|
3925
4232
|
const selector = managedInstructionSelector("logicalSelector" in entry ? entry.logicalSelector : void 0, entry.artifactType, entry.artifactName);
|
|
3926
4233
|
const state = await readManagedInstructionBlockState(destPath, selector, transport);
|
|
3927
|
-
return state.
|
|
4234
|
+
return state.hash;
|
|
3928
4235
|
}
|
|
3929
4236
|
function operationMetadataFromEntry2(entry, ownersOverride) {
|
|
3930
4237
|
if ("owners" in entry) {
|
|
@@ -4148,16 +4455,18 @@ function ownerChains(lock, nodeId) {
|
|
|
4148
4455
|
return incoming.flatMap((edge) => ownerChains(lock, edge.from).map((chain) => [...chain, `${edge.alias}:${nodeId}`]));
|
|
4149
4456
|
}
|
|
4150
4457
|
|
|
4151
|
-
// src/source/
|
|
4152
|
-
import {
|
|
4153
|
-
import {
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
import {
|
|
4458
|
+
// src/source/clawhub.ts
|
|
4459
|
+
import { mkdir as mkdir11, rm as rm5, writeFile as writeFile13 } from "fs/promises";
|
|
4460
|
+
import { basename as basename10, dirname as dirname14, join as join20, resolve as resolve6 } from "path";
|
|
4461
|
+
|
|
4462
|
+
// src/source/local.ts
|
|
4463
|
+
import { createHash as createHash4 } from "crypto";
|
|
4464
|
+
import { readdir, stat as stat3 } from "fs/promises";
|
|
4465
|
+
import { basename as basename9, join as join19, relative as relative4, resolve as resolve5 } from "path";
|
|
4157
4466
|
|
|
4158
4467
|
// src/model/package.ts
|
|
4159
|
-
import { readFile as
|
|
4160
|
-
import { join as
|
|
4468
|
+
import { readFile as readFile16 } from "fs/promises";
|
|
4469
|
+
import { join as join18 } from "path";
|
|
4161
4470
|
import { parse as parse3, printParseErrorCode as printParseErrorCode2 } from "jsonc-parser";
|
|
4162
4471
|
import { z as z5 } from "zod";
|
|
4163
4472
|
var legacyArtifactTypeSchema = z5.enum([
|
|
@@ -4181,6 +4490,7 @@ var packageProvideBaseSchema = z5.object({
|
|
|
4181
4490
|
var packageItemSchema = z5.object({
|
|
4182
4491
|
format: artifactFormatSchema.optional(),
|
|
4183
4492
|
requires: z5.array(packageItemRequireSchema).optional(),
|
|
4493
|
+
suggests: z5.array(packageItemSuggestSchema).optional(),
|
|
4184
4494
|
compose: z5.array(packageComposeEntrySchema).optional(),
|
|
4185
4495
|
runtimes: runtimeListSchema.optional()
|
|
4186
4496
|
});
|
|
@@ -4194,6 +4504,10 @@ var packageDependencySchema = z5.object({
|
|
|
4194
4504
|
integrity: z5.string().min(1).optional(),
|
|
4195
4505
|
runtimes: runtimeListSchema.optional()
|
|
4196
4506
|
});
|
|
4507
|
+
var packageSuggestionSchema = packageDependencySchema.extend({
|
|
4508
|
+
reason: z5.string().min(1).optional(),
|
|
4509
|
+
when: z5.string().min(1).optional()
|
|
4510
|
+
});
|
|
4197
4511
|
var packageProvideV1Schema = packageProvideBaseSchema.extend({
|
|
4198
4512
|
type: legacyArtifactTypeSchema
|
|
4199
4513
|
});
|
|
@@ -4214,6 +4528,7 @@ var packageManifestV2Schema = z5.object({
|
|
|
4214
4528
|
version: z5.string().min(1),
|
|
4215
4529
|
runtimes: runtimeListSchema.optional(),
|
|
4216
4530
|
requires: z5.record(z5.string().min(1), packageDependencySchema).optional(),
|
|
4531
|
+
suggests: z5.record(z5.string().min(1), packageSuggestionSchema).optional(),
|
|
4217
4532
|
compose: z5.array(packageComposeEntrySchema).optional(),
|
|
4218
4533
|
provides: z5.array(packageProvideSchema).default([])
|
|
4219
4534
|
}).superRefine((manifest, ctx) => {
|
|
@@ -4234,7 +4549,7 @@ var packageManifestNames = [...openPackManifestNames, ...legacyPackageManifestNa
|
|
|
4234
4549
|
var warnedLegacyManifestPaths = /* @__PURE__ */ new Set();
|
|
4235
4550
|
async function findPackageManifestPath(root, options = {}) {
|
|
4236
4551
|
for (const name of packageManifestNames) {
|
|
4237
|
-
const candidate =
|
|
4552
|
+
const candidate = join18(root, name);
|
|
4238
4553
|
if (!await pathExists(candidate)) continue;
|
|
4239
4554
|
if (isLegacyPackageManifestName(name) && options.warnLegacy !== false && !warnedLegacyManifestPaths.has(candidate)) {
|
|
4240
4555
|
warnedLegacyManifestPaths.add(candidate);
|
|
@@ -4247,7 +4562,7 @@ async function findPackageManifestPath(root, options = {}) {
|
|
|
4247
4562
|
async function readPackageManifest(root) {
|
|
4248
4563
|
const path = await findPackageManifestPath(root);
|
|
4249
4564
|
if (!path) return void 0;
|
|
4250
|
-
const content = await
|
|
4565
|
+
const content = await readFile16(path, "utf8");
|
|
4251
4566
|
const errors = [];
|
|
4252
4567
|
const parsed = parse3(content, errors, { allowTrailingComma: true, disallowComments: false });
|
|
4253
4568
|
if (errors.length > 0) {
|
|
@@ -4301,9 +4616,6 @@ function isRecord6(value) {
|
|
|
4301
4616
|
}
|
|
4302
4617
|
|
|
4303
4618
|
// 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
4619
|
var LocalSourceDriver = class {
|
|
4308
4620
|
name = "local";
|
|
4309
4621
|
async resolve(source) {
|
|
@@ -4333,29 +4645,29 @@ var LocalSourceDriver = class {
|
|
|
4333
4645
|
}
|
|
4334
4646
|
const artifacts = [];
|
|
4335
4647
|
const root = resolved.resolvedPath;
|
|
4336
|
-
const instructions = await firstExisting([
|
|
4648
|
+
const instructions = await firstExisting([join19(root, "instructions.md"), join19(root, "AGENTS.md")]);
|
|
4337
4649
|
if (instructions) {
|
|
4338
4650
|
artifacts.push({
|
|
4339
4651
|
type: "instructions",
|
|
4340
|
-
name:
|
|
4652
|
+
name: basename9(instructions),
|
|
4341
4653
|
sourcePath: instructions,
|
|
4342
|
-
relativePath:
|
|
4654
|
+
relativePath: basename9(instructions),
|
|
4343
4655
|
kind: "file",
|
|
4344
4656
|
hash: await hashPath(instructions),
|
|
4345
4657
|
packageName: resolved.packageName,
|
|
4346
4658
|
channel: "managed"
|
|
4347
4659
|
});
|
|
4348
4660
|
}
|
|
4349
|
-
const rulesDir =
|
|
4661
|
+
const rulesDir = join19(root, "rules");
|
|
4350
4662
|
if (await pathExists(rulesDir)) {
|
|
4351
4663
|
for (const entry of await sortedDirEntries(rulesDir)) {
|
|
4352
|
-
const full =
|
|
4664
|
+
const full = join19(rulesDir, entry.name);
|
|
4353
4665
|
if (entry.isFile()) {
|
|
4354
4666
|
artifacts.push({
|
|
4355
4667
|
type: "rules",
|
|
4356
4668
|
name: entry.name,
|
|
4357
4669
|
sourcePath: full,
|
|
4358
|
-
relativePath:
|
|
4670
|
+
relativePath: join19("rules", entry.name),
|
|
4359
4671
|
kind: "file",
|
|
4360
4672
|
hash: await hashPath(full),
|
|
4361
4673
|
packageName: resolved.packageName,
|
|
@@ -4364,16 +4676,16 @@ var LocalSourceDriver = class {
|
|
|
4364
4676
|
}
|
|
4365
4677
|
}
|
|
4366
4678
|
}
|
|
4367
|
-
const fragmentsDir =
|
|
4679
|
+
const fragmentsDir = join19(root, "fragments");
|
|
4368
4680
|
if (await pathExists(fragmentsDir)) {
|
|
4369
4681
|
for (const entry of await sortedDirEntries(fragmentsDir)) {
|
|
4370
|
-
const full =
|
|
4682
|
+
const full = join19(fragmentsDir, entry.name);
|
|
4371
4683
|
if (entry.isFile()) {
|
|
4372
4684
|
artifacts.push({
|
|
4373
4685
|
type: "fragments",
|
|
4374
4686
|
name: entry.name,
|
|
4375
4687
|
sourcePath: full,
|
|
4376
|
-
relativePath:
|
|
4688
|
+
relativePath: join19("fragments", entry.name),
|
|
4377
4689
|
kind: "file",
|
|
4378
4690
|
hash: await hashPath(full),
|
|
4379
4691
|
packageName: resolved.packageName,
|
|
@@ -4382,16 +4694,16 @@ var LocalSourceDriver = class {
|
|
|
4382
4694
|
}
|
|
4383
4695
|
}
|
|
4384
4696
|
}
|
|
4385
|
-
const skillsDir =
|
|
4697
|
+
const skillsDir = join19(root, "skills");
|
|
4386
4698
|
if (await pathExists(skillsDir)) {
|
|
4387
4699
|
for (const entry of await sortedDirEntries(skillsDir)) {
|
|
4388
|
-
const full =
|
|
4700
|
+
const full = join19(skillsDir, entry.name);
|
|
4389
4701
|
if (entry.isDirectory()) {
|
|
4390
4702
|
artifacts.push({
|
|
4391
4703
|
type: "skills",
|
|
4392
4704
|
name: entry.name,
|
|
4393
4705
|
sourcePath: full,
|
|
4394
|
-
relativePath:
|
|
4706
|
+
relativePath: join19("skills", entry.name),
|
|
4395
4707
|
kind: "dir",
|
|
4396
4708
|
hash: await hashPath(full),
|
|
4397
4709
|
packageName: resolved.packageName,
|
|
@@ -4402,7 +4714,7 @@ var LocalSourceDriver = class {
|
|
|
4402
4714
|
type: "skills",
|
|
4403
4715
|
name: entry.name.replace(/\.md$/, ""),
|
|
4404
4716
|
sourcePath: full,
|
|
4405
|
-
relativePath:
|
|
4717
|
+
relativePath: join19("skills", entry.name),
|
|
4406
4718
|
kind: "file",
|
|
4407
4719
|
hash: await hashPath(full),
|
|
4408
4720
|
packageName: resolved.packageName,
|
|
@@ -4412,7 +4724,7 @@ var LocalSourceDriver = class {
|
|
|
4412
4724
|
}
|
|
4413
4725
|
}
|
|
4414
4726
|
for (const type of ["commands", "subagents", "mcp", "hooks", "settings", "plugins"]) {
|
|
4415
|
-
const dir =
|
|
4727
|
+
const dir = join19(root, type);
|
|
4416
4728
|
if (!await pathExists(dir)) continue;
|
|
4417
4729
|
artifacts.push(...await listGenericArtifacts(type, dir, type, resolved.packageName));
|
|
4418
4730
|
}
|
|
@@ -4428,7 +4740,7 @@ var LocalSourceDriver = class {
|
|
|
4428
4740
|
findings.push({ level: "warning", message: "No instructions.md or AGENTS.md found", path: resolved.resolvedPath });
|
|
4429
4741
|
}
|
|
4430
4742
|
for (const artifact of artifacts.filter((item) => item.type === "skills" && item.kind === "dir")) {
|
|
4431
|
-
if (!await pathExists(
|
|
4743
|
+
if (!await pathExists(join19(artifact.sourcePath, "SKILL.md"))) {
|
|
4432
4744
|
findings.push({ level: "warning", message: `Skill directory has no SKILL.md: ${artifact.name}`, path: artifact.sourcePath });
|
|
4433
4745
|
}
|
|
4434
4746
|
}
|
|
@@ -4452,7 +4764,7 @@ async function hashLocalSource(root, manifest) {
|
|
|
4452
4764
|
}
|
|
4453
4765
|
const provides = [...manifest.provides].sort((a, b) => `${a.type}\0${a.path}`.localeCompare(`${b.type}\0${b.path}`));
|
|
4454
4766
|
for (const provide of provides) {
|
|
4455
|
-
const full =
|
|
4767
|
+
const full = join19(root, provide.path);
|
|
4456
4768
|
if (!await pathExists(full)) continue;
|
|
4457
4769
|
hash.update("provide\0");
|
|
4458
4770
|
hash.update(provide.type).update("\0");
|
|
@@ -4475,27 +4787,27 @@ async function listFromManifest(root, packageName) {
|
|
|
4475
4787
|
if (!manifest) return [];
|
|
4476
4788
|
const artifacts = [];
|
|
4477
4789
|
for (const provide of manifest.provides) {
|
|
4478
|
-
const full =
|
|
4790
|
+
const full = join19(root, provide.path);
|
|
4479
4791
|
if (!await pathExists(full)) continue;
|
|
4480
4792
|
const stats = await stat3(full);
|
|
4481
4793
|
if (provide.type === "instructions") {
|
|
4482
4794
|
if (stats.isFile()) {
|
|
4483
|
-
artifacts.push(await artifactForFile(provide.type,
|
|
4795
|
+
artifacts.push(await artifactForFile(provide.type, basename9(full), full, provide.path, packageName, provide, manifest, basename9(full)));
|
|
4484
4796
|
}
|
|
4485
4797
|
continue;
|
|
4486
4798
|
}
|
|
4487
4799
|
if (stats.isDirectory()) {
|
|
4488
4800
|
for (const entry of await sortedDirEntries(full)) {
|
|
4489
|
-
const child =
|
|
4801
|
+
const child = join19(full, entry.name);
|
|
4490
4802
|
if ((provide.type === "skills" || provide.type === "plugins" || provide.type === "subagents") && entry.isDirectory()) {
|
|
4491
|
-
artifacts.push(await artifactForDir(provide.type, entry.name, child,
|
|
4803
|
+
artifacts.push(await artifactForDir(provide.type, entry.name, child, join19(provide.path, entry.name), packageName, provide, manifest, entry.name));
|
|
4492
4804
|
} else if (entry.isFile()) {
|
|
4493
4805
|
const name = provide.type === "rules" && entry.name.endsWith(".md") ? entry.name : entry.name;
|
|
4494
|
-
artifacts.push(await artifactForFile(provide.type, name, child,
|
|
4806
|
+
artifacts.push(await artifactForFile(provide.type, name, child, join19(provide.path, entry.name), packageName, provide, manifest, name));
|
|
4495
4807
|
}
|
|
4496
4808
|
}
|
|
4497
4809
|
} else if (stats.isFile()) {
|
|
4498
|
-
artifacts.push(await artifactForFile(provide.type,
|
|
4810
|
+
artifacts.push(await artifactForFile(provide.type, basename9(full), full, provide.path, packageName, provide, manifest, basename9(full)));
|
|
4499
4811
|
}
|
|
4500
4812
|
}
|
|
4501
4813
|
return artifacts;
|
|
@@ -4503,11 +4815,11 @@ async function listFromManifest(root, packageName) {
|
|
|
4503
4815
|
async function listGenericArtifacts(type, dir, relativeRoot, packageName) {
|
|
4504
4816
|
const artifacts = [];
|
|
4505
4817
|
for (const entry of await sortedDirEntries(dir)) {
|
|
4506
|
-
const full =
|
|
4818
|
+
const full = join19(dir, entry.name);
|
|
4507
4819
|
if (entry.isDirectory()) {
|
|
4508
|
-
artifacts.push(await artifactForDir(type, entry.name, full,
|
|
4820
|
+
artifacts.push(await artifactForDir(type, entry.name, full, join19(relativeRoot, entry.name), packageName));
|
|
4509
4821
|
} else if (entry.isFile()) {
|
|
4510
|
-
artifacts.push(await artifactForFile(type, entry.name, full,
|
|
4822
|
+
artifacts.push(await artifactForFile(type, entry.name, full, join19(relativeRoot, entry.name), packageName));
|
|
4511
4823
|
}
|
|
4512
4824
|
}
|
|
4513
4825
|
return artifacts;
|
|
@@ -4527,6 +4839,7 @@ async function artifactForFile(type, name, sourcePath, relativePath, packageName
|
|
|
4527
4839
|
assets: provide?.assets,
|
|
4528
4840
|
required: provide?.required,
|
|
4529
4841
|
requires: item.requires,
|
|
4842
|
+
suggests: item.suggests,
|
|
4530
4843
|
compose: item.compose,
|
|
4531
4844
|
runtimes: item.runtimes ?? provideRuntimes(provide) ?? manifestRuntimes(manifest)
|
|
4532
4845
|
};
|
|
@@ -4546,6 +4859,7 @@ async function artifactForDir(type, name, sourcePath, relativePath, packageName,
|
|
|
4546
4859
|
assets: provide?.assets,
|
|
4547
4860
|
required: provide?.required,
|
|
4548
4861
|
requires: item.requires,
|
|
4862
|
+
suggests: item.suggests,
|
|
4549
4863
|
compose: item.compose,
|
|
4550
4864
|
runtimes: item.runtimes ?? provideRuntimes(provide) ?? manifestRuntimes(manifest)
|
|
4551
4865
|
};
|
|
@@ -4554,7 +4868,7 @@ function itemMetadata(provide, itemName) {
|
|
|
4554
4868
|
if (!provide || !("items" in provide) || !provide.items || !itemName) return {};
|
|
4555
4869
|
const item = provide.items[itemName];
|
|
4556
4870
|
if (!item) return {};
|
|
4557
|
-
return { format: item.format, requires: item.requires, compose: item.compose, runtimes: item.runtimes };
|
|
4871
|
+
return { format: item.format, requires: item.requires, suggests: item.suggests, compose: item.compose, runtimes: item.runtimes };
|
|
4558
4872
|
}
|
|
4559
4873
|
function provideRuntimes(provide) {
|
|
4560
4874
|
return provide && "runtimes" in provide ? provide.runtimes : void 0;
|
|
@@ -4563,7 +4877,154 @@ function manifestRuntimes(manifest) {
|
|
|
4563
4877
|
return manifest && manifest.schemaVersion === 2 ? manifest.runtimes : void 0;
|
|
4564
4878
|
}
|
|
4565
4879
|
|
|
4880
|
+
// src/source/clawhub.ts
|
|
4881
|
+
var clawHubBaseUrl = "https://clawhub.ai/api/v1";
|
|
4882
|
+
var sourcePrefix = "clawhub:";
|
|
4883
|
+
var transientStatuses = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
|
|
4884
|
+
var ClawHubSourceDriver = class {
|
|
4885
|
+
constructor(fetchImpl = fetch) {
|
|
4886
|
+
this.fetchImpl = fetchImpl;
|
|
4887
|
+
}
|
|
4888
|
+
fetchImpl;
|
|
4889
|
+
name = "clawhub";
|
|
4890
|
+
local = new LocalSourceDriver();
|
|
4891
|
+
async resolve(source, options = {}) {
|
|
4892
|
+
const packageName = parseClawHubSource(source);
|
|
4893
|
+
return {
|
|
4894
|
+
driver: this.name,
|
|
4895
|
+
source,
|
|
4896
|
+
resolvedPath: cachePathFor(packageName, options.cacheRoot),
|
|
4897
|
+
packageName: `clawhub/${packageName}`,
|
|
4898
|
+
mode: options.mode ?? "tracking",
|
|
4899
|
+
requestedRef: options.ref ?? "latest",
|
|
4900
|
+
frozenLock: options.frozenLock,
|
|
4901
|
+
cacheLockTimeoutMs: options.cacheLockTimeoutMs
|
|
4902
|
+
};
|
|
4903
|
+
}
|
|
4904
|
+
async fetch(resolved) {
|
|
4905
|
+
if (resolved.frozenLock) {
|
|
4906
|
+
if (!await pathExists(resolved.resolvedPath)) {
|
|
4907
|
+
throw new Error(`Frozen lock requires cached ClawHub source at ${resolved.resolvedPath}`);
|
|
4908
|
+
}
|
|
4909
|
+
return {
|
|
4910
|
+
...resolved,
|
|
4911
|
+
sourceHash: await hashPath(resolved.resolvedPath)
|
|
4912
|
+
};
|
|
4913
|
+
}
|
|
4914
|
+
const requestedName = parseClawHubSource(resolved.source);
|
|
4915
|
+
const response = await fetchClawHubPackage(this.fetchImpl, requestedName);
|
|
4916
|
+
if (!response.ok) {
|
|
4917
|
+
throw new Error(`ClawHub lookup failed for ${requestedName}: HTTP ${response.status}`);
|
|
4918
|
+
}
|
|
4919
|
+
const payload = await response.json();
|
|
4920
|
+
const packageInfo = payload.package;
|
|
4921
|
+
if (!packageInfo?.name) {
|
|
4922
|
+
throw new Error(`ClawHub response missing package metadata for ${requestedName}`);
|
|
4923
|
+
}
|
|
4924
|
+
if (!isInstallableOpenClawPackage(packageInfo.family)) {
|
|
4925
|
+
throw new Error(`ClawHub package is not an OpenClaw plugin or hook package: ${packageInfo.name}`);
|
|
4926
|
+
}
|
|
4927
|
+
await writeGeneratedPackage(resolved.resolvedPath, packageInfo);
|
|
4928
|
+
return {
|
|
4929
|
+
...resolved,
|
|
4930
|
+
packageName: `clawhub/${packageInfo.name}`,
|
|
4931
|
+
packageVersion: packageInfo.latestVersion ?? "latest",
|
|
4932
|
+
sourceHash: await hashPath(resolved.resolvedPath)
|
|
4933
|
+
};
|
|
4934
|
+
}
|
|
4935
|
+
async list(resolved) {
|
|
4936
|
+
return this.local.list({ ...resolved, driver: "local" });
|
|
4937
|
+
}
|
|
4938
|
+
async scan(resolved) {
|
|
4939
|
+
if (!await pathExists(join20(resolved.resolvedPath, "plugins"))) {
|
|
4940
|
+
return { ok: false, findings: [{ level: "error", message: "ClawHub source has no generated plugin artifact" }] };
|
|
4941
|
+
}
|
|
4942
|
+
return { ok: true, findings: [] };
|
|
4943
|
+
}
|
|
4944
|
+
async translate(resolved) {
|
|
4945
|
+
return resolved;
|
|
4946
|
+
}
|
|
4947
|
+
async export(resolved) {
|
|
4948
|
+
return resolved;
|
|
4949
|
+
}
|
|
4950
|
+
};
|
|
4951
|
+
async function fetchClawHubPackage(fetchImpl, packageName) {
|
|
4952
|
+
const url = `${clawHubBaseUrl}/packages/${encodeURIComponent(packageName)}`;
|
|
4953
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
4954
|
+
const response = await fetchImpl(url, { headers: { Accept: "application/json" } });
|
|
4955
|
+
if (!transientStatuses.has(response.status) || attempt === 2) return response;
|
|
4956
|
+
await delay((attempt + 1) * 250);
|
|
4957
|
+
}
|
|
4958
|
+
throw new Error(`ClawHub lookup failed for ${packageName}`);
|
|
4959
|
+
}
|
|
4960
|
+
async function delay(ms) {
|
|
4961
|
+
await new Promise((resolve21) => setTimeout(resolve21, ms));
|
|
4962
|
+
}
|
|
4963
|
+
function parseClawHubSource(source) {
|
|
4964
|
+
if (!source.startsWith(sourcePrefix)) {
|
|
4965
|
+
throw new Error(`Invalid ClawHub source: ${source}`);
|
|
4966
|
+
}
|
|
4967
|
+
const packageName = source.slice(sourcePrefix.length).trim();
|
|
4968
|
+
if (!packageName) throw new Error(`Invalid ClawHub source: ${source}`);
|
|
4969
|
+
return packageName;
|
|
4970
|
+
}
|
|
4971
|
+
function isInstallableOpenClawPackage(family) {
|
|
4972
|
+
if (!family) return true;
|
|
4973
|
+
return family.endsWith("-plugin") || family === "hook-pack";
|
|
4974
|
+
}
|
|
4975
|
+
async function writeGeneratedPackage(root, packageInfo) {
|
|
4976
|
+
const name = packageInfo.name?.trim();
|
|
4977
|
+
if (!name) throw new Error("ClawHub package metadata must include a name");
|
|
4978
|
+
const pluginId = installNameFor2(packageInfo.runtimeId ?? name);
|
|
4979
|
+
const plugin = {
|
|
4980
|
+
name,
|
|
4981
|
+
installSpec: `${sourcePrefix}${name}`,
|
|
4982
|
+
pluginId,
|
|
4983
|
+
displayName: packageInfo.displayName,
|
|
4984
|
+
runtimeId: packageInfo.runtimeId,
|
|
4985
|
+
latestVersion: packageInfo.latestVersion,
|
|
4986
|
+
family: packageInfo.family,
|
|
4987
|
+
summary: packageInfo.summary ?? packageInfo.description,
|
|
4988
|
+
artifact: packageInfo.artifact,
|
|
4989
|
+
verification: packageInfo.verification
|
|
4990
|
+
};
|
|
4991
|
+
const pluginPath = join20(root, "plugins", pluginId, "clawhub.json");
|
|
4992
|
+
await rm5(root, { recursive: true, force: true });
|
|
4993
|
+
await mkdir11(dirname14(pluginPath), { recursive: true });
|
|
4994
|
+
await writeFile13(join20(root, "openpack.json"), `${JSON.stringify({
|
|
4995
|
+
schemaVersion: 2,
|
|
4996
|
+
name: `clawhub/${name}`,
|
|
4997
|
+
version: packageInfo.latestVersion ?? "latest",
|
|
4998
|
+
runtimes: ["openclaw"],
|
|
4999
|
+
provides: [
|
|
5000
|
+
{
|
|
5001
|
+
type: "plugins",
|
|
5002
|
+
path: "plugins",
|
|
5003
|
+
format: "openclaw-clawhub-plugin",
|
|
5004
|
+
runtimes: ["openclaw"],
|
|
5005
|
+
required: true
|
|
5006
|
+
}
|
|
5007
|
+
]
|
|
5008
|
+
}, null, 2)}
|
|
5009
|
+
`, "utf8");
|
|
5010
|
+
await writeFile13(pluginPath, `${JSON.stringify(plugin, null, 2)}
|
|
5011
|
+
`, "utf8");
|
|
5012
|
+
}
|
|
5013
|
+
function installNameFor2(value) {
|
|
5014
|
+
return basename10(value).replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "clawhub-plugin";
|
|
5015
|
+
}
|
|
5016
|
+
function cachePathFor(packageName, cacheRoot) {
|
|
5017
|
+
const root = cacheRoot ? resolve6(cacheRoot) : join20(process.env.HOME ?? ".", ".agentwheel", "cache");
|
|
5018
|
+
const slug2 = `clawhub-${packageName}`.replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "");
|
|
5019
|
+
return join20(root, slug2 || "clawhub-package");
|
|
5020
|
+
}
|
|
5021
|
+
|
|
4566
5022
|
// src/source/git.ts
|
|
5023
|
+
import { execFile as execFile4 } from "child_process";
|
|
5024
|
+
import { cp as cp2, mkdir as mkdir12, rename as rename3, rm as rm6, writeFile as writeFile14 } from "fs/promises";
|
|
5025
|
+
import { homedir as homedir2 } from "os";
|
|
5026
|
+
import { basename as basename11, dirname as dirname15, join as join21, resolve as resolve7 } from "path";
|
|
5027
|
+
import { promisify as promisify4 } from "util";
|
|
4567
5028
|
var execFileAsync4 = promisify4(execFile4);
|
|
4568
5029
|
var GitSourceDriver = class {
|
|
4569
5030
|
name = "git";
|
|
@@ -4575,7 +5036,7 @@ var GitSourceDriver = class {
|
|
|
4575
5036
|
return {
|
|
4576
5037
|
driver: this.name,
|
|
4577
5038
|
source,
|
|
4578
|
-
resolvedPath:
|
|
5039
|
+
resolvedPath: cachePathFor2(parsed.url, options.cacheRoot),
|
|
4579
5040
|
mode,
|
|
4580
5041
|
requestedRef,
|
|
4581
5042
|
frozenLock: options.frozenLock,
|
|
@@ -4585,12 +5046,12 @@ var GitSourceDriver = class {
|
|
|
4585
5046
|
async fetch(resolved) {
|
|
4586
5047
|
return withFilesystemLock(`${resolved.resolvedPath}.lock`, resolved.cacheLockTimeoutMs ?? 3e4, async () => {
|
|
4587
5048
|
const parsed = parseGitSource(resolved.source);
|
|
4588
|
-
await
|
|
4589
|
-
if (!await pathExists(
|
|
5049
|
+
await mkdir12(resolve7(resolved.resolvedPath, ".."), { recursive: true });
|
|
5050
|
+
if (!await pathExists(join21(resolved.resolvedPath, ".git"))) {
|
|
4590
5051
|
if (resolved.frozenLock) {
|
|
4591
5052
|
throw new Error(`Frozen lock requires cached git checkout at ${resolved.resolvedPath}`);
|
|
4592
5053
|
}
|
|
4593
|
-
await
|
|
5054
|
+
await rm6(resolved.resolvedPath, { recursive: true, force: true });
|
|
4594
5055
|
await git(["clone", "--no-tags", parsed.url, resolved.resolvedPath]);
|
|
4595
5056
|
} else if (!resolved.frozenLock) {
|
|
4596
5057
|
await git(["-C", resolved.resolvedPath, "fetch", "--prune", "origin"]);
|
|
@@ -4652,65 +5113,201 @@ function parseGitSource(source) {
|
|
|
4652
5113
|
}
|
|
4653
5114
|
throw new Error(`Invalid git source: ${source}`);
|
|
4654
5115
|
}
|
|
4655
|
-
function
|
|
4656
|
-
const root = cacheRoot ?
|
|
5116
|
+
function cachePathFor2(url, cacheRoot) {
|
|
5117
|
+
const root = cacheRoot ? resolve7(cacheRoot) : join21(homedir2(), ".agentwheel", "cache");
|
|
4657
5118
|
const slug2 = url.replace(/^[a-z]+:\/\//i, "").replace(/\.git$/i, "").replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "");
|
|
4658
|
-
return
|
|
5119
|
+
return join21(root, slug2 || basename11(url));
|
|
4659
5120
|
}
|
|
4660
5121
|
async function git(args) {
|
|
4661
5122
|
return execFileAsync4("git", args, { maxBuffer: 1024 * 1024 * 10 });
|
|
4662
5123
|
}
|
|
4663
5124
|
async function snapshotCheckout(checkoutPath, commit) {
|
|
4664
|
-
const snapshotPath =
|
|
5125
|
+
const snapshotPath = join21(dirname15(checkoutPath), `${basename11(checkoutPath)}-${commit.slice(0, 12)}`);
|
|
4665
5126
|
if (await pathExists(snapshotPath)) return snapshotPath;
|
|
4666
|
-
const tempPath =
|
|
4667
|
-
await
|
|
5127
|
+
const tempPath = join21(dirname15(checkoutPath), `${basename11(snapshotPath)}.tmp-${process.pid}-${Date.now()}`);
|
|
5128
|
+
await rm6(tempPath, { recursive: true, force: true });
|
|
4668
5129
|
await cp2(checkoutPath, tempPath, { recursive: true, dereference: true });
|
|
4669
|
-
await
|
|
5130
|
+
await rm6(join21(tempPath, ".git"), { recursive: true, force: true });
|
|
4670
5131
|
try {
|
|
4671
5132
|
await rename3(tempPath, snapshotPath);
|
|
4672
5133
|
} catch (error) {
|
|
4673
5134
|
if (!isAlreadyExists2(error)) throw error;
|
|
4674
|
-
await
|
|
5135
|
+
await rm6(tempPath, { recursive: true, force: true });
|
|
4675
5136
|
return snapshotPath;
|
|
4676
5137
|
}
|
|
4677
5138
|
return snapshotPath;
|
|
4678
5139
|
}
|
|
4679
5140
|
async function withFilesystemLock(lockPath, timeoutMs, fn) {
|
|
4680
|
-
await
|
|
5141
|
+
await mkdir12(dirname15(lockPath), { recursive: true });
|
|
4681
5142
|
const started = Date.now();
|
|
4682
5143
|
while (true) {
|
|
4683
5144
|
try {
|
|
4684
|
-
await
|
|
4685
|
-
await
|
|
5145
|
+
await mkdir12(lockPath);
|
|
5146
|
+
await writeFile14(join21(lockPath, "owner.json"), JSON.stringify({ pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf8");
|
|
4686
5147
|
break;
|
|
4687
5148
|
} catch (error) {
|
|
4688
5149
|
if (!isAlreadyExists2(error)) throw error;
|
|
4689
5150
|
if (Date.now() - started > timeoutMs) {
|
|
4690
5151
|
throw new Error(`Timed out waiting for git cache lock at ${lockPath}`);
|
|
4691
5152
|
}
|
|
4692
|
-
await new Promise((
|
|
5153
|
+
await new Promise((resolve21) => setTimeout(resolve21, 50));
|
|
4693
5154
|
}
|
|
4694
5155
|
}
|
|
4695
5156
|
try {
|
|
4696
5157
|
return await fn();
|
|
4697
5158
|
} finally {
|
|
4698
|
-
await
|
|
5159
|
+
await rm6(lockPath, { recursive: true, force: true });
|
|
4699
5160
|
}
|
|
4700
5161
|
}
|
|
4701
5162
|
function isAlreadyExists2(error) {
|
|
4702
5163
|
return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
|
|
4703
5164
|
}
|
|
4704
5165
|
|
|
5166
|
+
// src/source/mcp-registry.ts
|
|
5167
|
+
import { mkdir as mkdir13, writeFile as writeFile15 } from "fs/promises";
|
|
5168
|
+
import { basename as basename12, dirname as dirname16, join as join22, resolve as resolve8 } from "path";
|
|
5169
|
+
var registryBaseUrl = "https://registry.modelcontextprotocol.io/v0.1";
|
|
5170
|
+
var sourcePrefix2 = "mcp-registry:";
|
|
5171
|
+
var McpRegistrySourceDriver = class {
|
|
5172
|
+
constructor(fetchImpl = fetch) {
|
|
5173
|
+
this.fetchImpl = fetchImpl;
|
|
5174
|
+
}
|
|
5175
|
+
fetchImpl;
|
|
5176
|
+
name = "mcp-registry";
|
|
5177
|
+
local = new LocalSourceDriver();
|
|
5178
|
+
async resolve(source, options = {}) {
|
|
5179
|
+
const serverName = parseMcpRegistrySource(source);
|
|
5180
|
+
return {
|
|
5181
|
+
driver: this.name,
|
|
5182
|
+
source,
|
|
5183
|
+
resolvedPath: cachePathFor3(serverName, options.cacheRoot),
|
|
5184
|
+
packageName: `mcp-registry/${serverName}`,
|
|
5185
|
+
mode: options.mode ?? "tracking",
|
|
5186
|
+
requestedRef: options.ref ?? "latest",
|
|
5187
|
+
frozenLock: options.frozenLock
|
|
5188
|
+
};
|
|
5189
|
+
}
|
|
5190
|
+
async fetch(resolved) {
|
|
5191
|
+
if (resolved.frozenLock) {
|
|
5192
|
+
if (!await pathExists(resolved.resolvedPath)) {
|
|
5193
|
+
throw new Error(`Frozen lock requires cached MCP registry source at ${resolved.resolvedPath}`);
|
|
5194
|
+
}
|
|
5195
|
+
return {
|
|
5196
|
+
...resolved,
|
|
5197
|
+
sourceHash: await hashPath(resolved.resolvedPath)
|
|
5198
|
+
};
|
|
5199
|
+
}
|
|
5200
|
+
const serverName = parseMcpRegistrySource(resolved.source);
|
|
5201
|
+
const response = await this.fetchImpl(`${registryBaseUrl}/servers/${encodeURIComponent(serverName)}/versions/latest`, {
|
|
5202
|
+
headers: { Accept: "application/json" }
|
|
5203
|
+
});
|
|
5204
|
+
if (!response.ok) {
|
|
5205
|
+
throw new Error(`MCP registry lookup failed for ${serverName}: HTTP ${response.status}`);
|
|
5206
|
+
}
|
|
5207
|
+
const payload = await response.json();
|
|
5208
|
+
const server = payload.server;
|
|
5209
|
+
if (!server?.name) {
|
|
5210
|
+
throw new Error(`MCP registry response missing server metadata for ${serverName}`);
|
|
5211
|
+
}
|
|
5212
|
+
const remote = supportedRemote(server.remotes ?? []);
|
|
5213
|
+
if (!remote) {
|
|
5214
|
+
throw new Error(`MCP registry server is discovery-only for Agentwheel: ${serverName}`);
|
|
5215
|
+
}
|
|
5216
|
+
await writeGeneratedPackage2(resolved.resolvedPath, {
|
|
5217
|
+
serverName: server.name,
|
|
5218
|
+
title: server.title,
|
|
5219
|
+
description: server.description,
|
|
5220
|
+
version: server.version,
|
|
5221
|
+
url: remote.url
|
|
5222
|
+
});
|
|
5223
|
+
return {
|
|
5224
|
+
...resolved,
|
|
5225
|
+
packageName: `mcp-registry/${server.name}`,
|
|
5226
|
+
packageVersion: server.version,
|
|
5227
|
+
sourceHash: await hashPath(resolved.resolvedPath)
|
|
5228
|
+
};
|
|
5229
|
+
}
|
|
5230
|
+
async list(resolved) {
|
|
5231
|
+
return this.local.list({ ...resolved, driver: "local" });
|
|
5232
|
+
}
|
|
5233
|
+
async scan(resolved) {
|
|
5234
|
+
if (!await pathExists(join22(resolved.resolvedPath, "mcp"))) {
|
|
5235
|
+
return { ok: false, findings: [{ level: "error", message: "MCP registry source has no generated mcp artifact" }] };
|
|
5236
|
+
}
|
|
5237
|
+
return { ok: true, findings: [] };
|
|
5238
|
+
}
|
|
5239
|
+
async translate(resolved) {
|
|
5240
|
+
return resolved;
|
|
5241
|
+
}
|
|
5242
|
+
async export(resolved) {
|
|
5243
|
+
return resolved;
|
|
5244
|
+
}
|
|
5245
|
+
};
|
|
5246
|
+
function parseMcpRegistrySource(source) {
|
|
5247
|
+
if (!source.startsWith(sourcePrefix2)) {
|
|
5248
|
+
throw new Error(`Invalid MCP registry source: ${source}`);
|
|
5249
|
+
}
|
|
5250
|
+
const serverName = source.slice(sourcePrefix2.length).trim();
|
|
5251
|
+
if (!serverName) throw new Error(`Invalid MCP registry source: ${source}`);
|
|
5252
|
+
return serverName;
|
|
5253
|
+
}
|
|
5254
|
+
function supportedRemote(remotes) {
|
|
5255
|
+
for (const remote of remotes ?? []) {
|
|
5256
|
+
if (remote?.type !== "streamable-http") continue;
|
|
5257
|
+
if (!isSafeHttpUrl(remote.url)) continue;
|
|
5258
|
+
if ((remote.headers ?? []).some((header) => header.isRequired && header.isSecret)) continue;
|
|
5259
|
+
return { url: remote.url };
|
|
5260
|
+
}
|
|
5261
|
+
return void 0;
|
|
5262
|
+
}
|
|
5263
|
+
function isSafeHttpUrl(value) {
|
|
5264
|
+
if (typeof value !== "string") return false;
|
|
5265
|
+
try {
|
|
5266
|
+
const url = new URL(value);
|
|
5267
|
+
return url.protocol === "https:" || url.protocol === "http:";
|
|
5268
|
+
} catch {
|
|
5269
|
+
return false;
|
|
5270
|
+
}
|
|
5271
|
+
}
|
|
5272
|
+
async function writeGeneratedPackage2(root, server) {
|
|
5273
|
+
const serverId = installNameFor3(server.serverName);
|
|
5274
|
+
const mcpPath = join22(root, "mcp", `${serverId}.json`);
|
|
5275
|
+
await mkdir13(dirname16(mcpPath), { recursive: true });
|
|
5276
|
+
await writeFile15(join22(root, "openpack.json"), `${JSON.stringify({
|
|
5277
|
+
schemaVersion: 2,
|
|
5278
|
+
name: `mcp-registry/${server.serverName}`,
|
|
5279
|
+
version: server.version ?? "latest",
|
|
5280
|
+
provides: [{ type: "mcp", path: "mcp" }]
|
|
5281
|
+
}, null, 2)}
|
|
5282
|
+
`, "utf8");
|
|
5283
|
+
await writeFile15(mcpPath, `${JSON.stringify({
|
|
5284
|
+
mcpServers: {
|
|
5285
|
+
[serverId]: {
|
|
5286
|
+
type: "streamable-http",
|
|
5287
|
+
url: server.url
|
|
5288
|
+
}
|
|
5289
|
+
}
|
|
5290
|
+
}, null, 2)}
|
|
5291
|
+
`, "utf8");
|
|
5292
|
+
}
|
|
5293
|
+
function installNameFor3(serverName) {
|
|
5294
|
+
return basename12(serverName).replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "mcp-server";
|
|
5295
|
+
}
|
|
5296
|
+
function cachePathFor3(serverName, cacheRoot) {
|
|
5297
|
+
const root = cacheRoot ? resolve8(cacheRoot) : join22(process.env.HOME ?? ".", ".agentwheel", "cache");
|
|
5298
|
+
const slug2 = `mcp-registry-${serverName}`.replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "");
|
|
5299
|
+
return join22(root, slug2 || "mcp-registry-server");
|
|
5300
|
+
}
|
|
5301
|
+
|
|
4705
5302
|
// src/source/skillkit.ts
|
|
4706
|
-
import { cp as cp3, mkdir as
|
|
5303
|
+
import { cp as cp3, mkdir as mkdir14, readFile as readFile17, rm as rm7 } from "fs/promises";
|
|
4707
5304
|
import { homedir as homedir3 } from "os";
|
|
4708
|
-
import { basename as
|
|
5305
|
+
import { basename as basename14, dirname as dirname18, join as join24, resolve as resolve9 } from "path";
|
|
4709
5306
|
import * as defaultSkillKit from "@skillkit/core";
|
|
4710
5307
|
|
|
4711
5308
|
// src/source/skill-artifacts.ts
|
|
4712
5309
|
import { readdir as readdir2, stat as stat4 } from "fs/promises";
|
|
4713
|
-
import { basename as
|
|
5310
|
+
import { basename as basename13, dirname as dirname17, extname as extname2, join as join23 } from "path";
|
|
4714
5311
|
async function artifactsFromSkillPaths(paths, packageName) {
|
|
4715
5312
|
const artifacts = [];
|
|
4716
5313
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -4732,28 +5329,28 @@ async function discoverSkillPaths(root) {
|
|
|
4732
5329
|
async function artifactFromSkillPath(item, packageName) {
|
|
4733
5330
|
const stats = await stat4(item.path);
|
|
4734
5331
|
if (stats.isDirectory()) {
|
|
4735
|
-
const skillMd =
|
|
5332
|
+
const skillMd = join23(item.path, "SKILL.md");
|
|
4736
5333
|
if (!await pathExists(skillMd)) return void 0;
|
|
4737
|
-
const name = sanitizeSkillName(item.name ??
|
|
5334
|
+
const name = sanitizeSkillName(item.name ?? basename13(item.path));
|
|
4738
5335
|
return {
|
|
4739
5336
|
type: "skills",
|
|
4740
5337
|
name,
|
|
4741
5338
|
sourcePath: item.path,
|
|
4742
|
-
relativePath:
|
|
5339
|
+
relativePath: join23("skills", name),
|
|
4743
5340
|
kind: "dir",
|
|
4744
5341
|
hash: await hashPath(item.path),
|
|
4745
5342
|
packageName,
|
|
4746
5343
|
channel: "managed"
|
|
4747
5344
|
};
|
|
4748
5345
|
}
|
|
4749
|
-
if (stats.isFile() &&
|
|
4750
|
-
const dir =
|
|
4751
|
-
const name = sanitizeSkillName(item.name ??
|
|
5346
|
+
if (stats.isFile() && basename13(item.path).toLowerCase() === "skill.md") {
|
|
5347
|
+
const dir = dirname17(item.path);
|
|
5348
|
+
const name = sanitizeSkillName(item.name ?? basename13(dir));
|
|
4752
5349
|
return {
|
|
4753
5350
|
type: "skills",
|
|
4754
5351
|
name,
|
|
4755
5352
|
sourcePath: dir,
|
|
4756
|
-
relativePath:
|
|
5353
|
+
relativePath: join23("skills", name),
|
|
4757
5354
|
kind: "dir",
|
|
4758
5355
|
hash: await hashPath(dir),
|
|
4759
5356
|
packageName,
|
|
@@ -4761,12 +5358,12 @@ async function artifactFromSkillPath(item, packageName) {
|
|
|
4761
5358
|
};
|
|
4762
5359
|
}
|
|
4763
5360
|
if (stats.isFile() && extname2(item.path).toLowerCase() === ".md") {
|
|
4764
|
-
const name = sanitizeSkillName(item.name ??
|
|
5361
|
+
const name = sanitizeSkillName(item.name ?? basename13(item.path, ".md"));
|
|
4765
5362
|
return {
|
|
4766
5363
|
type: "skills",
|
|
4767
5364
|
name,
|
|
4768
5365
|
sourcePath: item.path,
|
|
4769
|
-
relativePath:
|
|
5366
|
+
relativePath: join23("skills", `${name}.md`),
|
|
4770
5367
|
kind: "file",
|
|
4771
5368
|
hash: await hashPath(item.path),
|
|
4772
5369
|
packageName,
|
|
@@ -4784,7 +5381,7 @@ async function walk(dir, paths) {
|
|
|
4784
5381
|
}
|
|
4785
5382
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
4786
5383
|
if (!entry.isDirectory() || entry.name === ".git" || entry.name === "node_modules") continue;
|
|
4787
|
-
await walk(
|
|
5384
|
+
await walk(join23(dir, entry.name), paths);
|
|
4788
5385
|
}
|
|
4789
5386
|
}
|
|
4790
5387
|
function sanitizeSkillName(name) {
|
|
@@ -4801,12 +5398,12 @@ var SkillKitSourceDriver = class {
|
|
|
4801
5398
|
async resolve(source, options = {}) {
|
|
4802
5399
|
const spec = parseSkillKitSource(source);
|
|
4803
5400
|
if (await pathExists(spec)) {
|
|
4804
|
-
const resolvedPath =
|
|
5401
|
+
const resolvedPath = resolve9(spec);
|
|
4805
5402
|
return {
|
|
4806
5403
|
driver: this.name,
|
|
4807
5404
|
source,
|
|
4808
5405
|
resolvedPath,
|
|
4809
|
-
packageName: `skillkit/${
|
|
5406
|
+
packageName: `skillkit/${basename14(resolvedPath)}`,
|
|
4810
5407
|
mode: options.mode ?? "pinned",
|
|
4811
5408
|
sourceHash: await hashPath(resolvedPath)
|
|
4812
5409
|
};
|
|
@@ -4814,7 +5411,7 @@ var SkillKitSourceDriver = class {
|
|
|
4814
5411
|
return {
|
|
4815
5412
|
driver: this.name,
|
|
4816
5413
|
source,
|
|
4817
|
-
resolvedPath:
|
|
5414
|
+
resolvedPath: cachePathFor4(spec, options.cacheRoot),
|
|
4818
5415
|
packageName: `skillkit/${packageSlug(spec)}`,
|
|
4819
5416
|
mode: options.mode ?? "tracking",
|
|
4820
5417
|
requestedRef: options.ref,
|
|
@@ -4840,17 +5437,17 @@ var SkillKitSourceDriver = class {
|
|
|
4840
5437
|
if (!provider?.clone) {
|
|
4841
5438
|
throw new Error("SkillKit provider API unavailable or cannot resolve source. Expected @skillkit/core detectProvider().clone().");
|
|
4842
5439
|
}
|
|
4843
|
-
await
|
|
5440
|
+
await mkdir14(dirname18(resolved.resolvedPath), { recursive: true });
|
|
4844
5441
|
const result = await provider.clone(providerSpec, resolved.resolvedPath, {});
|
|
4845
5442
|
if (!result.success || !result.path) {
|
|
4846
5443
|
throw new Error(`SkillKit provider failed to fetch ${spec}: ${result.error ?? "unknown error"}`);
|
|
4847
5444
|
}
|
|
4848
|
-
if (
|
|
4849
|
-
await
|
|
5445
|
+
if (resolve9(result.path) !== resolve9(resolved.resolvedPath)) {
|
|
5446
|
+
await rm7(resolved.resolvedPath, { recursive: true, force: true });
|
|
4850
5447
|
await cp3(result.path, resolved.resolvedPath, { recursive: true, dereference: true });
|
|
4851
5448
|
}
|
|
4852
5449
|
if (result.tempRoot) {
|
|
4853
|
-
await
|
|
5450
|
+
await rm7(result.tempRoot, { recursive: true, force: true });
|
|
4854
5451
|
}
|
|
4855
5452
|
return {
|
|
4856
5453
|
...resolved,
|
|
@@ -4884,9 +5481,9 @@ var SkillKitSourceDriver = class {
|
|
|
4884
5481
|
throw new Error("SkillKit translateSkill API unavailable");
|
|
4885
5482
|
}
|
|
4886
5483
|
for (const skill of this.discover(resolved.resolvedPath)) {
|
|
4887
|
-
const skillMd =
|
|
5484
|
+
const skillMd = join24(skill.path, "SKILL.md");
|
|
4888
5485
|
if (await pathExists(skillMd)) {
|
|
4889
|
-
this.core.translateSkill(await
|
|
5486
|
+
this.core.translateSkill(await readFile17(skillMd, "utf8"), "openclaw", { sourceFilename: "SKILL.md" });
|
|
4890
5487
|
}
|
|
4891
5488
|
}
|
|
4892
5489
|
return resolved;
|
|
@@ -4914,9 +5511,9 @@ function normalizeProviderSource(spec) {
|
|
|
4914
5511
|
if (spec.startsWith("git:https://github.com/")) return spec.slice("git:".length);
|
|
4915
5512
|
return spec;
|
|
4916
5513
|
}
|
|
4917
|
-
function
|
|
4918
|
-
const root = cacheRoot ?
|
|
4919
|
-
return
|
|
5514
|
+
function cachePathFor4(spec, cacheRoot) {
|
|
5515
|
+
const root = cacheRoot ? resolve9(cacheRoot) : join24(homedir3(), ".agentwheel", "cache");
|
|
5516
|
+
return join24(root, "skillkit", packageSlug(spec));
|
|
4920
5517
|
}
|
|
4921
5518
|
function packageSlug(spec) {
|
|
4922
5519
|
return spec.replace(/^[a-z]+:\/\//i, "").replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "source";
|
|
@@ -4929,14 +5526,14 @@ function mapSeverity(severity) {
|
|
|
4929
5526
|
|
|
4930
5527
|
// src/source/vercel-skills.ts
|
|
4931
5528
|
import { stat as stat5 } from "fs/promises";
|
|
4932
|
-
import { basename as
|
|
5529
|
+
import { basename as basename15, join as join25, relative as relative5, resolve as resolve10 } from "path";
|
|
4933
5530
|
var VercelSkillsSourceDriver = class {
|
|
4934
5531
|
name = "vercel-skills";
|
|
4935
5532
|
git = new GitSourceDriver();
|
|
4936
5533
|
async resolve(source, options = {}) {
|
|
4937
5534
|
const parsed = parseVercelSource(source);
|
|
4938
5535
|
if (parsed.kind === "local") {
|
|
4939
|
-
const resolvedPath =
|
|
5536
|
+
const resolvedPath = resolve10(parsed.path);
|
|
4940
5537
|
if (!await pathExists(resolvedPath) || !(await stat5(resolvedPath)).isDirectory()) {
|
|
4941
5538
|
throw new Error(`Vercel skills local source not found: ${resolvedPath}`);
|
|
4942
5539
|
}
|
|
@@ -4944,7 +5541,7 @@ var VercelSkillsSourceDriver = class {
|
|
|
4944
5541
|
driver: this.name,
|
|
4945
5542
|
source,
|
|
4946
5543
|
resolvedPath,
|
|
4947
|
-
packageName: `vercel/${
|
|
5544
|
+
packageName: `vercel/${basename15(resolvedPath)}`,
|
|
4948
5545
|
mode: options.mode ?? "pinned",
|
|
4949
5546
|
sourceHash: await hashPath(resolvedPath)
|
|
4950
5547
|
};
|
|
@@ -4965,10 +5562,7 @@ var VercelSkillsSourceDriver = class {
|
|
|
4965
5562
|
driver: "git",
|
|
4966
5563
|
source: parsed.gitSource
|
|
4967
5564
|
});
|
|
4968
|
-
const resolvedPath =
|
|
4969
|
-
if (!await pathExists(resolvedPath)) {
|
|
4970
|
-
throw new Error(`Vercel skills subpath not found: ${parsed.subpath}`);
|
|
4971
|
-
}
|
|
5565
|
+
const resolvedPath = await resolveVercelSkillSubpath(fetched.resolvedPath, parsed.subpath);
|
|
4972
5566
|
return {
|
|
4973
5567
|
...resolved,
|
|
4974
5568
|
resolvedPath,
|
|
@@ -4993,6 +5587,15 @@ var VercelSkillsSourceDriver = class {
|
|
|
4993
5587
|
return resolved;
|
|
4994
5588
|
}
|
|
4995
5589
|
};
|
|
5590
|
+
async function resolveVercelSkillSubpath(root, subpath) {
|
|
5591
|
+
if (!subpath) return root;
|
|
5592
|
+
const candidates = [join25(root, subpath), join25(root, "skills", subpath)];
|
|
5593
|
+
for (const candidate of candidates) {
|
|
5594
|
+
if (await pathExists(candidate)) return candidate;
|
|
5595
|
+
}
|
|
5596
|
+
const tried = candidates.map((candidate) => relative5(root, candidate)).join(", ");
|
|
5597
|
+
throw new Error(`Vercel skills subpath not found: ${subpath} (tried ${tried})`);
|
|
5598
|
+
}
|
|
4996
5599
|
function parseVercelSource(source) {
|
|
4997
5600
|
if (!source.startsWith("vercel:")) {
|
|
4998
5601
|
throw new Error(`Invalid Vercel skills source: ${source}`);
|
|
@@ -5043,7 +5646,9 @@ var drivers = [
|
|
|
5043
5646
|
new LocalSourceDriver(),
|
|
5044
5647
|
new GitSourceDriver(),
|
|
5045
5648
|
new SkillKitSourceDriver(),
|
|
5046
|
-
new VercelSkillsSourceDriver()
|
|
5649
|
+
new VercelSkillsSourceDriver(),
|
|
5650
|
+
new McpRegistrySourceDriver(),
|
|
5651
|
+
new ClawHubSourceDriver()
|
|
5047
5652
|
];
|
|
5048
5653
|
function getSourceDriver(name = "local") {
|
|
5049
5654
|
const driver = drivers.find((candidate) => candidate.name === name);
|
|
@@ -5054,14 +5659,14 @@ function getSourceDriver(name = "local") {
|
|
|
5054
5659
|
}
|
|
5055
5660
|
|
|
5056
5661
|
// src/staging/staging.ts
|
|
5057
|
-
import { chmod, cp as cp5, mkdir as
|
|
5058
|
-
import { basename as
|
|
5662
|
+
import { chmod, cp as cp5, mkdir as mkdir16, mkdtemp as mkdtemp3, readdir as readdir5, stat as stat8 } from "fs/promises";
|
|
5663
|
+
import { basename as basename18, dirname as dirname21, join as join28, relative as relative7, resolve as resolve12, sep as sep2 } from "path";
|
|
5059
5664
|
import { tmpdir as tmpdir4 } from "os";
|
|
5060
5665
|
|
|
5061
5666
|
// src/compose/markdown.ts
|
|
5062
5667
|
import { createHash as createHash5 } from "crypto";
|
|
5063
|
-
import { readdir as readdir3, readFile as
|
|
5064
|
-
import { basename as
|
|
5668
|
+
import { readdir as readdir3, readFile as readFile18, stat as stat6, writeFile as writeFile16 } from "fs/promises";
|
|
5669
|
+
import { basename as basename16, dirname as dirname19, extname as extname3, join as join26, relative as relative6, resolve as resolve11, sep } from "path";
|
|
5065
5670
|
var includePattern = /<!--\s*openpack:include(\?)?\s+([^>]+?)\s*-->/g;
|
|
5066
5671
|
var escapedIncludePattern = /<!--\s*openpack\\:include(\?)?\s+([^>]+?)\s*-->/g;
|
|
5067
5672
|
var generatedPattern = /<!--\s*(?:BEGIN|END)\s+openpack:include\b/;
|
|
@@ -5076,7 +5681,7 @@ async function expandMarkdownIncludes(artifacts, packageRoot, options = {}) {
|
|
|
5076
5681
|
const composedFrom = [];
|
|
5077
5682
|
for (const file of files) {
|
|
5078
5683
|
const result = await expandFile(file, packageRoot, composeEntriesForFile(artifact, file), artifactPaths, options);
|
|
5079
|
-
if (result.changed) await
|
|
5684
|
+
if (result.changed) await writeFile16(file, result.content, "utf8");
|
|
5080
5685
|
composedFrom.push(...result.composedFrom);
|
|
5081
5686
|
}
|
|
5082
5687
|
const stagedPath = artifact.stagedPath ?? artifact.sourcePath;
|
|
@@ -5098,7 +5703,7 @@ async function validateMarkdownIncludes(artifacts, packageRoot, options = {}) {
|
|
|
5098
5703
|
}
|
|
5099
5704
|
}
|
|
5100
5705
|
async function expandFile(file, packageRoot, appendEntries, artifactPaths, options) {
|
|
5101
|
-
const raw = await
|
|
5706
|
+
const raw = await readFile18(file, "utf8");
|
|
5102
5707
|
const owner = ownerSelector(packageRoot, file, options.nodeId);
|
|
5103
5708
|
const expanded = await expandContent(raw, packageRoot, [owner], artifactPaths, options);
|
|
5104
5709
|
let content = expanded.content;
|
|
@@ -5191,7 +5796,7 @@ async function expandInclude(selector, packageRoot, artifactPaths, options) {
|
|
|
5191
5796
|
if (!stats.isFile()) {
|
|
5192
5797
|
throw new Error(`OpenPack include is not a file: ${displaySelector}`);
|
|
5193
5798
|
}
|
|
5194
|
-
const raw = sourceContent ?? await
|
|
5799
|
+
const raw = sourceContent ?? await readFile18(sourcePath, "utf8");
|
|
5195
5800
|
const { optional: _optional, markers: _markers, chain: _chain, ...childOptions } = options;
|
|
5196
5801
|
const expanded = await expandContent(raw, includePackageRoot, [...options.chain, displaySelector], includeArtifactPaths, {
|
|
5197
5802
|
...childOptions,
|
|
@@ -5249,8 +5854,8 @@ function extractOpenPackIncludeSelectors(content) {
|
|
|
5249
5854
|
return selectors;
|
|
5250
5855
|
}
|
|
5251
5856
|
function resolvePackageSelector(packageRoot, selector) {
|
|
5252
|
-
const root =
|
|
5253
|
-
const resolved =
|
|
5857
|
+
const root = resolve11(packageRoot);
|
|
5858
|
+
const resolved = resolve11(root, selector);
|
|
5254
5859
|
if (resolved !== root && !resolved.startsWith(`${root}${sep}`)) {
|
|
5255
5860
|
throw new Error(`OpenPack include escapes package root: ${selector}`);
|
|
5256
5861
|
}
|
|
@@ -5267,7 +5872,7 @@ async function listMarkdownFiles(root) {
|
|
|
5267
5872
|
const out = [];
|
|
5268
5873
|
async function walk2(dir) {
|
|
5269
5874
|
for (const entry of (await readdir3(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
5270
|
-
const full =
|
|
5875
|
+
const full = join26(dir, entry.name);
|
|
5271
5876
|
if (entry.isDirectory()) {
|
|
5272
5877
|
await walk2(full);
|
|
5273
5878
|
} else if (entry.isFile() && extname3(entry.name).toLowerCase() === ".md") {
|
|
@@ -5280,8 +5885,8 @@ async function listMarkdownFiles(root) {
|
|
|
5280
5885
|
}
|
|
5281
5886
|
function composeEntriesForFile(artifact, file) {
|
|
5282
5887
|
if (!artifact.compose?.length) return [];
|
|
5283
|
-
if (artifact.kind === "file") return [
|
|
5284
|
-
return
|
|
5888
|
+
if (artifact.kind === "file") return [resolve11(artifact.stagedPath ?? artifact.sourcePath), resolve11(file)].every(Boolean) && resolve11(artifact.stagedPath ?? artifact.sourcePath) === resolve11(file) ? artifact.compose : [];
|
|
5889
|
+
return basename16(file) === "SKILL.md" && dirname19(file) === resolve11(artifact.stagedPath ?? artifact.sourcePath) ? artifact.compose : [];
|
|
5285
5890
|
}
|
|
5286
5891
|
function orderedForExpansion(artifacts) {
|
|
5287
5892
|
return [...artifacts].sort((a, b) => Number(a.type === "fragments") - Number(b.type === "fragments"));
|
|
@@ -5298,7 +5903,7 @@ function applyReplacements(content, replacements) {
|
|
|
5298
5903
|
return out + content.slice(cursor);
|
|
5299
5904
|
}
|
|
5300
5905
|
function relativeSelector(root, file) {
|
|
5301
|
-
return
|
|
5906
|
+
return relative6(root, file).replaceAll("\\", "/");
|
|
5302
5907
|
}
|
|
5303
5908
|
function ownerSelector(root, file, nodeId) {
|
|
5304
5909
|
const selector = relativeSelector(root, file);
|
|
@@ -5327,8 +5932,8 @@ function artifactPathMap(artifacts) {
|
|
|
5327
5932
|
}
|
|
5328
5933
|
|
|
5329
5934
|
// src/staging/customize.ts
|
|
5330
|
-
import { cp as cp4, mkdir as
|
|
5331
|
-
import { dirname as
|
|
5935
|
+
import { cp as cp4, mkdir as mkdir15, readdir as readdir4, readFile as readFile19, writeFile as writeFile17 } from "fs/promises";
|
|
5936
|
+
import { dirname as dirname20, join as join27 } from "path";
|
|
5332
5937
|
async function applyCustomizations(artifacts, options) {
|
|
5333
5938
|
let next = [...artifacts];
|
|
5334
5939
|
next = await applyReplacements2(next, options, "override", installableArtifactTypes());
|
|
@@ -5344,16 +5949,16 @@ async function applyFragmentCustomizations(artifacts, options) {
|
|
|
5344
5949
|
return next.sort((a, b) => `${a.type}:${a.name}:${a.channel}`.localeCompare(`${b.type}:${b.name}:${b.channel}`));
|
|
5345
5950
|
}
|
|
5346
5951
|
async function applyInstructionOverlay(artifacts, options) {
|
|
5347
|
-
const overlayPath =
|
|
5952
|
+
const overlayPath = join27(options.workspaceRoot, ".agentwheel", "overlays", options.adapter.name, "instructions.local.md");
|
|
5348
5953
|
if (!await pathExists(overlayPath)) return artifacts;
|
|
5349
5954
|
const index = artifacts.findIndex((artifact2) => artifact2.type === "instructions");
|
|
5350
5955
|
if (index < 0) return artifacts;
|
|
5351
5956
|
const artifact = artifacts[index];
|
|
5352
|
-
const managed = await
|
|
5353
|
-
const local = await
|
|
5354
|
-
const composedPath =
|
|
5355
|
-
await
|
|
5356
|
-
await
|
|
5957
|
+
const managed = await readFile19(artifact.stagedPath ?? artifact.sourcePath, "utf8");
|
|
5958
|
+
const local = await readFile19(overlayPath, "utf8");
|
|
5959
|
+
const composedPath = join27(options.stageRoot, ".agentwheel-composed", "instructions", "AGENTS.md");
|
|
5960
|
+
await mkdir15(dirname20(composedPath), { recursive: true });
|
|
5961
|
+
await writeFile17(
|
|
5357
5962
|
composedPath,
|
|
5358
5963
|
[
|
|
5359
5964
|
"<!-- BEGIN agentwheel managed: upstream -->",
|
|
@@ -5379,19 +5984,19 @@ async function applyInstructionOverlay(artifacts, options) {
|
|
|
5379
5984
|
return [...artifacts.slice(0, index), updated, ...artifacts.slice(index + 1)];
|
|
5380
5985
|
}
|
|
5381
5986
|
async function applyAdditions(artifacts, options) {
|
|
5382
|
-
const additionsRoot =
|
|
5383
|
-
const rulesRoot =
|
|
5987
|
+
const additionsRoot = join27(options.workspaceRoot, ".agentwheel", "additions");
|
|
5988
|
+
const rulesRoot = join27(additionsRoot, "rules");
|
|
5384
5989
|
if (!await pathExists(rulesRoot)) return artifacts;
|
|
5385
5990
|
const additions = [];
|
|
5386
5991
|
for (const entry of await sortedDirEntries2(rulesRoot)) {
|
|
5387
|
-
const full =
|
|
5992
|
+
const full = join27(rulesRoot, entry.name);
|
|
5388
5993
|
if (!entry.isFile()) continue;
|
|
5389
5994
|
additions.push({
|
|
5390
5995
|
type: "rules",
|
|
5391
5996
|
name: entry.name,
|
|
5392
5997
|
sourcePath: full,
|
|
5393
5998
|
stagedPath: full,
|
|
5394
|
-
relativePath:
|
|
5999
|
+
relativePath: join27("additions", "rules", entry.name),
|
|
5395
6000
|
kind: "file",
|
|
5396
6001
|
hash: await hashPath(full),
|
|
5397
6002
|
packageName: options.packageName,
|
|
@@ -5415,17 +6020,17 @@ async function applyReplacements2(artifacts, options, channel, artifactTypes) {
|
|
|
5415
6020
|
);
|
|
5416
6021
|
}
|
|
5417
6022
|
for (const type of artifactTypes) {
|
|
5418
|
-
const typeRoot =
|
|
6023
|
+
const typeRoot = join27(root, type);
|
|
5419
6024
|
if (!await pathExists(typeRoot)) continue;
|
|
5420
6025
|
for (const entry of await sortedDirEntries2(typeRoot)) {
|
|
5421
6026
|
const artifactMapKey = `${type}:${entry.name}`;
|
|
5422
6027
|
if (seen.has(artifactMapKey)) continue;
|
|
5423
6028
|
seen.add(artifactMapKey);
|
|
5424
|
-
const full =
|
|
6029
|
+
const full = join27(typeRoot, entry.name);
|
|
5425
6030
|
const artifactKind = entry.isDirectory() ? "dir" : "file";
|
|
5426
6031
|
const existing = byKey.get(artifactMapKey);
|
|
5427
|
-
const stagedPath =
|
|
5428
|
-
await
|
|
6032
|
+
const stagedPath = join27(options.stageRoot, ".agentwheel-composed", channel, type, entry.name);
|
|
6033
|
+
await mkdir15(dirname20(stagedPath), { recursive: true });
|
|
5429
6034
|
await cp4(full, stagedPath, { recursive: artifactKind === "dir", dereference: true });
|
|
5430
6035
|
byKey.set(artifactMapKey, {
|
|
5431
6036
|
...existing,
|
|
@@ -5433,7 +6038,7 @@ async function applyReplacements2(artifacts, options, channel, artifactTypes) {
|
|
|
5433
6038
|
name: entry.name,
|
|
5434
6039
|
sourcePath: full,
|
|
5435
6040
|
stagedPath,
|
|
5436
|
-
relativePath: existing?.relativePath ??
|
|
6041
|
+
relativePath: existing?.relativePath ?? join27(type, entry.name),
|
|
5437
6042
|
kind: artifactKind,
|
|
5438
6043
|
hash: await hashPath(stagedPath),
|
|
5439
6044
|
packageName,
|
|
@@ -5448,13 +6053,13 @@ function replacementRoots(options, channel) {
|
|
|
5448
6053
|
const stateDir = channel === "override" ? "overrides" : "ejected";
|
|
5449
6054
|
const roots = [];
|
|
5450
6055
|
if (options.graphNodeId) {
|
|
5451
|
-
roots.push({ root:
|
|
6056
|
+
roots.push({ root: join27(options.workspaceRoot, ".agentwheel", stateDir, ...options.graphNodeId.split("/")), kind: "node" });
|
|
5452
6057
|
}
|
|
5453
6058
|
if (options.packageName && options.packageVersion) {
|
|
5454
|
-
roots.push({ root:
|
|
6059
|
+
roots.push({ root: join27(options.workspaceRoot, ".agentwheel", stateDir, ...`${options.packageName}@${options.packageVersion}`.split("/")), kind: "version" });
|
|
5455
6060
|
}
|
|
5456
6061
|
if (options.packageName) {
|
|
5457
|
-
roots.push({ root:
|
|
6062
|
+
roots.push({ root: join27(options.workspaceRoot, ".agentwheel", stateDir, ...options.packageName.split("/")), kind: "package" });
|
|
5458
6063
|
}
|
|
5459
6064
|
return roots;
|
|
5460
6065
|
}
|
|
@@ -5481,15 +6086,15 @@ async function stageResolvedSourceRaw(driver, resolved) {
|
|
|
5481
6086
|
return stageResolvedArtifactsRaw(resolved, artifacts);
|
|
5482
6087
|
}
|
|
5483
6088
|
async function stageResolvedArtifactsRaw(resolved, artifacts) {
|
|
5484
|
-
const root = await mkdtemp3(
|
|
6089
|
+
const root = await mkdtemp3(join28(tmpdir4(), "agentwheel-stage-"));
|
|
5485
6090
|
const stagedArtifacts = [];
|
|
5486
6091
|
for (const artifact of artifacts) {
|
|
5487
|
-
const stagedPath =
|
|
5488
|
-
await
|
|
6092
|
+
const stagedPath = join28(root, artifact.relativePath);
|
|
6093
|
+
await mkdir16(dirname21(stagedPath), { recursive: true });
|
|
5489
6094
|
await cp5(artifact.sourcePath, stagedPath, {
|
|
5490
6095
|
recursive: artifact.kind === "dir",
|
|
5491
6096
|
dereference: true,
|
|
5492
|
-
filter: (path) => !isIgnoredGeneratedEntry(
|
|
6097
|
+
filter: (path) => !isIgnoredGeneratedEntry(basename18(path))
|
|
5493
6098
|
});
|
|
5494
6099
|
await composeAssets(artifact, resolved.resolvedPath, stagedPath);
|
|
5495
6100
|
stagedArtifacts.push({
|
|
@@ -5518,7 +6123,8 @@ async function renderStagedBundle(bundle, options = {}) {
|
|
|
5518
6123
|
const runtimeSelectedSet = new Set(normalizeArtifactSelectors(options.select, options.skills) ?? []);
|
|
5519
6124
|
const runtimeArtifacts = options.adapter ? filterArtifactsByRuntime(selectedArtifacts, options.adapter.name, runtimeSelectedSet) : selectedArtifacts;
|
|
5520
6125
|
const codexRenderedArtifacts = await renderCodexSubagents(runtimeArtifacts, root, options.adapter);
|
|
5521
|
-
const
|
|
6126
|
+
const openClawRenderedArtifacts = await renderOpenClawSubagents(codexRenderedArtifacts, root, options.adapter);
|
|
6127
|
+
const renderedArtifacts = await renderCopilotArtifacts(openClawRenderedArtifacts, root, options.adapter);
|
|
5522
6128
|
const finalArtifacts = options.workspaceRoot && options.adapter ? await applyCustomizations(renderedArtifacts, {
|
|
5523
6129
|
workspaceRoot: options.workspaceRoot,
|
|
5524
6130
|
adapter: options.adapter,
|
|
@@ -5570,16 +6176,16 @@ async function composeAssets(artifact, packageRoot, stagedPath) {
|
|
|
5570
6176
|
}
|
|
5571
6177
|
for (const asset of artifact.assets) {
|
|
5572
6178
|
const source = resolvePackagePath(packageRoot, asset.from);
|
|
5573
|
-
const dest =
|
|
6179
|
+
const dest = join28(stagedPath, asset.into);
|
|
5574
6180
|
await copyAsset(asset, source, dest);
|
|
5575
6181
|
}
|
|
5576
6182
|
}
|
|
5577
6183
|
async function copyAsset(asset, source, dest) {
|
|
5578
6184
|
const sourceStats = await stat8(source);
|
|
5579
6185
|
if (sourceStats.isFile()) {
|
|
5580
|
-
if (matchesAny(
|
|
5581
|
-
await
|
|
5582
|
-
await copyAssetFile(source,
|
|
6186
|
+
if (matchesAny(basename18(source), asset.include)) {
|
|
6187
|
+
await mkdir16(dest, { recursive: true });
|
|
6188
|
+
await copyAssetFile(source, join28(dest, basename18(source)), asset);
|
|
5583
6189
|
}
|
|
5584
6190
|
return;
|
|
5585
6191
|
}
|
|
@@ -5587,25 +6193,25 @@ async function copyAsset(asset, source, dest) {
|
|
|
5587
6193
|
throw new Error(`Asset include source is not a file or directory: ${source}`);
|
|
5588
6194
|
}
|
|
5589
6195
|
if (!asset.include?.length) {
|
|
5590
|
-
await
|
|
6196
|
+
await mkdir16(dirname21(dest), { recursive: true });
|
|
5591
6197
|
await cp5(source, dest, { recursive: true, dereference: true });
|
|
5592
6198
|
if (asset.mode === "copy") await normalizeCopiedModes(dest);
|
|
5593
6199
|
return;
|
|
5594
6200
|
}
|
|
5595
6201
|
for (const file of await listFiles(source)) {
|
|
5596
|
-
const rel =
|
|
5597
|
-
if (!matchesAny(rel, asset.include) && !matchesAny(
|
|
5598
|
-
await copyAssetFile(file,
|
|
6202
|
+
const rel = relative7(source, file).replaceAll("\\", "/");
|
|
6203
|
+
if (!matchesAny(rel, asset.include) && !matchesAny(basename18(file), asset.include)) continue;
|
|
6204
|
+
await copyAssetFile(file, join28(dest, rel), asset);
|
|
5599
6205
|
}
|
|
5600
6206
|
}
|
|
5601
6207
|
async function copyAssetFile(source, dest, asset) {
|
|
5602
|
-
await
|
|
6208
|
+
await mkdir16(dirname21(dest), { recursive: true });
|
|
5603
6209
|
await cp5(source, dest, { dereference: true });
|
|
5604
6210
|
if (asset.mode === "copy") await chmod(dest, 420);
|
|
5605
6211
|
}
|
|
5606
6212
|
function resolvePackagePath(packageRoot, path) {
|
|
5607
|
-
const resolved =
|
|
5608
|
-
const root =
|
|
6213
|
+
const resolved = resolve12(packageRoot, path);
|
|
6214
|
+
const root = resolve12(packageRoot);
|
|
5609
6215
|
if (resolved !== root && !resolved.startsWith(`${root}${sep2}`)) {
|
|
5610
6216
|
throw new Error(`Asset include escapes package root: ${path}`);
|
|
5611
6217
|
}
|
|
@@ -5615,7 +6221,7 @@ async function listFiles(root) {
|
|
|
5615
6221
|
const out = [];
|
|
5616
6222
|
async function walk2(dir) {
|
|
5617
6223
|
for (const entry of (await readdir5(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
5618
|
-
const full =
|
|
6224
|
+
const full = join28(dir, entry.name);
|
|
5619
6225
|
if (entry.isDirectory()) {
|
|
5620
6226
|
await walk2(full);
|
|
5621
6227
|
} else if (entry.isFile()) {
|
|
@@ -5634,7 +6240,7 @@ async function normalizeCopiedModes(path) {
|
|
|
5634
6240
|
}
|
|
5635
6241
|
if (!stats.isDirectory()) return;
|
|
5636
6242
|
for (const entry of await readdir5(path, { withFileTypes: true })) {
|
|
5637
|
-
await normalizeCopiedModes(
|
|
6243
|
+
await normalizeCopiedModes(join28(path, entry.name));
|
|
5638
6244
|
}
|
|
5639
6245
|
}
|
|
5640
6246
|
function matchesAny(path, patterns) {
|
|
@@ -5647,14 +6253,14 @@ function matchesGlob(path, pattern) {
|
|
|
5647
6253
|
}
|
|
5648
6254
|
|
|
5649
6255
|
// src/model/workspace.ts
|
|
5650
|
-
import { readFile as
|
|
6256
|
+
import { readFile as readFile20 } from "fs/promises";
|
|
5651
6257
|
import { homedir as homedir4 } from "os";
|
|
5652
|
-
import { dirname as
|
|
6258
|
+
import { dirname as dirname22, join as join29, resolve as resolve13 } from "path";
|
|
5653
6259
|
import { z as z6 } from "zod";
|
|
5654
6260
|
var workspacePackageSchema = z6.object({
|
|
5655
6261
|
name: z6.string().min(1),
|
|
5656
6262
|
source: z6.string().min(1),
|
|
5657
|
-
driver: z6.enum(["local", "git", "skillkit", "vercel-skills"]).default("local"),
|
|
6263
|
+
driver: z6.enum(["local", "git", "skillkit", "vercel-skills", "mcp-registry", "clawhub"]).default("local"),
|
|
5658
6264
|
adapter: z6.string().min(1).default("openclaw"),
|
|
5659
6265
|
adapterConfig: z6.string().min(1).optional(),
|
|
5660
6266
|
adapterModule: z6.string().min(1).optional(),
|
|
@@ -5664,6 +6270,8 @@ var workspacePackageSchema = z6.object({
|
|
|
5664
6270
|
requestedRef: z6.string().min(1).optional(),
|
|
5665
6271
|
select: z6.array(z6.string().min(1)).optional(),
|
|
5666
6272
|
skills: z6.array(z6.string().min(1)).optional(),
|
|
6273
|
+
withSuggestions: z6.boolean().optional(),
|
|
6274
|
+
suggestions: z6.array(z6.string().min(1)).optional(),
|
|
5667
6275
|
aliases: z6.record(z6.string(), z6.string().min(1)).optional(),
|
|
5668
6276
|
overrides: z6.array(z6.string().min(1)).optional()
|
|
5669
6277
|
});
|
|
@@ -5718,12 +6326,12 @@ var workspaceConfigSchema = z6.object({
|
|
|
5718
6326
|
agents: z6.record(z6.string(), workspaceAgentSchema).default({})
|
|
5719
6327
|
});
|
|
5720
6328
|
function workspaceConfigPath(workspaceRoot) {
|
|
5721
|
-
return
|
|
6329
|
+
return join29(workspaceRoot, ".agentwheel", "config.json");
|
|
5722
6330
|
}
|
|
5723
6331
|
async function readWorkspaceConfig(workspaceRoot) {
|
|
5724
6332
|
const path = workspaceConfigPath(workspaceRoot);
|
|
5725
6333
|
if (!await pathExists(path)) return emptyWorkspaceConfig();
|
|
5726
|
-
return workspaceConfigSchema.parse(JSON.parse(await
|
|
6334
|
+
return workspaceConfigSchema.parse(JSON.parse(await readFile20(path, "utf8")));
|
|
5727
6335
|
}
|
|
5728
6336
|
async function writeWorkspaceConfig(workspaceRoot, config) {
|
|
5729
6337
|
await writeJsonAtomic(workspaceConfigPath(workspaceRoot), workspaceConfigSchema.parse(config));
|
|
@@ -5736,14 +6344,14 @@ function upsertPackage(config, entry) {
|
|
|
5736
6344
|
return { schemaVersion: 1, packages, bootstrapSkills: parsed.bootstrapSkills, registry: parsed.registry ?? {}, trust: parsed.trust ?? {}, profiles: parsed.profiles ?? {}, agents: parsed.agents ?? {} };
|
|
5737
6345
|
}
|
|
5738
6346
|
function globalWorkspaceConfigPath(globalRoot = homedir4()) {
|
|
5739
|
-
return
|
|
6347
|
+
return join29(globalRoot, ".agentwheel", "config.json");
|
|
5740
6348
|
}
|
|
5741
6349
|
async function findWorkspaceRoot(start = process.cwd()) {
|
|
5742
|
-
let current =
|
|
6350
|
+
let current = resolve13(start);
|
|
5743
6351
|
while (true) {
|
|
5744
6352
|
if (await pathExists(workspaceConfigPath(current))) return current;
|
|
5745
|
-
const parent =
|
|
5746
|
-
if (parent === current) return
|
|
6353
|
+
const parent = dirname22(current);
|
|
6354
|
+
if (parent === current) return resolve13(start);
|
|
5747
6355
|
current = parent;
|
|
5748
6356
|
}
|
|
5749
6357
|
}
|
|
@@ -5769,16 +6377,16 @@ function mergeWorkspaceConfig(global, project) {
|
|
|
5769
6377
|
});
|
|
5770
6378
|
}
|
|
5771
6379
|
function resolveConfigPath(path, baseRoot) {
|
|
5772
|
-
if (path.startsWith("~/")) return
|
|
6380
|
+
if (path.startsWith("~/")) return resolve13(homedir4(), path.slice(2));
|
|
5773
6381
|
if (path === "~") return homedir4();
|
|
5774
|
-
return path.startsWith("/") ?
|
|
6382
|
+
return path.startsWith("/") ? resolve13(path) : resolve13(baseRoot, path);
|
|
5775
6383
|
}
|
|
5776
6384
|
function emptyWorkspaceConfig() {
|
|
5777
6385
|
return { schemaVersion: 1, packages: [], registry: {}, trust: {}, profiles: {}, agents: {} };
|
|
5778
6386
|
}
|
|
5779
6387
|
async function readConfigPath(path) {
|
|
5780
6388
|
if (!await pathExists(path)) return emptyWorkspaceConfig();
|
|
5781
|
-
return workspaceConfigSchema.parse(JSON.parse(await
|
|
6389
|
+
return workspaceConfigSchema.parse(JSON.parse(await readFile20(path, "utf8")));
|
|
5782
6390
|
}
|
|
5783
6391
|
function mergeWorkspaceTrust(global, project) {
|
|
5784
6392
|
return {
|
|
@@ -5793,23 +6401,23 @@ function sortedUnique2(values) {
|
|
|
5793
6401
|
}
|
|
5794
6402
|
|
|
5795
6403
|
// src/lifecycle/customization.ts
|
|
5796
|
-
import { appendFile, cp as cp6, mkdir as
|
|
5797
|
-
import { dirname as
|
|
6404
|
+
import { appendFile, cp as cp6, mkdir as mkdir17, rm as rm9 } from "fs/promises";
|
|
6405
|
+
import { dirname as dirname24, join as join32 } from "path";
|
|
5798
6406
|
|
|
5799
6407
|
// src/resolve/graph.ts
|
|
5800
6408
|
import { createHash as createHash6 } from "crypto";
|
|
5801
|
-
import { mkdtemp as mkdtemp4, readdir as readdir6, readFile as
|
|
6409
|
+
import { mkdtemp as mkdtemp4, readdir as readdir6, readFile as readFile22, stat as stat10 } from "fs/promises";
|
|
5802
6410
|
import { tmpdir as tmpdir5 } from "os";
|
|
5803
|
-
import { basename as
|
|
6411
|
+
import { basename as basename19, extname as extname4, join as join31 } from "path";
|
|
5804
6412
|
|
|
5805
6413
|
// src/resolve/identity.ts
|
|
5806
6414
|
import { homedir as homedir6 } from "os";
|
|
5807
|
-
import { resolve as
|
|
6415
|
+
import { resolve as resolve15 } from "path";
|
|
5808
6416
|
|
|
5809
6417
|
// src/registry/client.ts
|
|
5810
|
-
import { readFile as
|
|
6418
|
+
import { readFile as readFile21, rm as rm8, stat as stat9 } from "fs/promises";
|
|
5811
6419
|
import { homedir as homedir5 } from "os";
|
|
5812
|
-
import { dirname as
|
|
6420
|
+
import { dirname as dirname23, join as join30, resolve as resolve14 } from "path";
|
|
5813
6421
|
import { fileURLToPath } from "url";
|
|
5814
6422
|
|
|
5815
6423
|
// src/model/registry.ts
|
|
@@ -5820,6 +6428,12 @@ var registryEntrySchema = z7.object({
|
|
|
5820
6428
|
type: z7.enum(["package", "skill", "plugin", "mcp", "adapter"]).default("package"),
|
|
5821
6429
|
description: z7.string().default(""),
|
|
5822
6430
|
tags: z7.array(z7.string().min(1)).default([]),
|
|
6431
|
+
select: z7.array(z7.string().min(1)).optional(),
|
|
6432
|
+
skills: z7.array(z7.string().min(1)).optional(),
|
|
6433
|
+
homepageUrl: z7.string().min(1).optional(),
|
|
6434
|
+
homepageLinkLabel: z7.string().min(1).optional(),
|
|
6435
|
+
sourceUrl: z7.string().min(1).optional(),
|
|
6436
|
+
sourceLinkLabel: z7.string().min(1).optional(),
|
|
5823
6437
|
openpack: z7.object({
|
|
5824
6438
|
schemaVersion: z7.number().int().positive().optional(),
|
|
5825
6439
|
specVersion: z7.string().min(1).optional()
|
|
@@ -5884,7 +6498,7 @@ var RegistryClient = class {
|
|
|
5884
6498
|
);
|
|
5885
6499
|
}
|
|
5886
6500
|
async clearCache() {
|
|
5887
|
-
await
|
|
6501
|
+
await rm8(this.cachePath, { force: true });
|
|
5888
6502
|
}
|
|
5889
6503
|
async getSources() {
|
|
5890
6504
|
if (this.options.sources?.length) return this.options.sources;
|
|
@@ -5907,7 +6521,7 @@ var RegistryClient = class {
|
|
|
5907
6521
|
}
|
|
5908
6522
|
async readCache() {
|
|
5909
6523
|
if (!await pathExists(this.cachePath)) return void 0;
|
|
5910
|
-
return registryCacheSchema.parse(JSON.parse(await
|
|
6524
|
+
return registryCacheSchema.parse(JSON.parse(await readFile21(this.cachePath, "utf8")));
|
|
5911
6525
|
}
|
|
5912
6526
|
isExpired(cache, ttlMs) {
|
|
5913
6527
|
return this.now().getTime() - new Date(cache.fetchedAt).getTime() > ttlMs;
|
|
@@ -5924,12 +6538,12 @@ var RegistryClient = class {
|
|
|
5924
6538
|
}
|
|
5925
6539
|
const filePath = source.startsWith("file:") ? fileURLToPath(source) : source;
|
|
5926
6540
|
if (await pathExists(filePath)) {
|
|
5927
|
-
const fullPath =
|
|
6541
|
+
const fullPath = resolve14(filePath);
|
|
5928
6542
|
const stats = await stat9(fullPath);
|
|
5929
|
-
return
|
|
6543
|
+
return readFile21(stats.isDirectory() ? join30(fullPath, "index.json") : fullPath, "utf8");
|
|
5930
6544
|
}
|
|
5931
|
-
const resolved = await this.git.fetch(await this.git.resolve(source, { cacheRoot:
|
|
5932
|
-
return
|
|
6545
|
+
const resolved = await this.git.fetch(await this.git.resolve(source, { cacheRoot: join30(dirname23(this.cachePath), "registry-repos") }));
|
|
6546
|
+
return readFile21(join30(resolved.resolvedPath, "index.json"), "utf8");
|
|
5933
6547
|
}
|
|
5934
6548
|
warnCompatibility(entries) {
|
|
5935
6549
|
for (const entry of entries) {
|
|
@@ -5943,17 +6557,20 @@ var RegistryClient = class {
|
|
|
5943
6557
|
}
|
|
5944
6558
|
};
|
|
5945
6559
|
async function resolvePackageSource(source, workspaceRoot, options = {}) {
|
|
5946
|
-
const { isExplicitSource } = await import("./identify-
|
|
6560
|
+
const { isExplicitSource } = await import("./identify-T4RE5RBD.js");
|
|
5947
6561
|
if (await isExplicitSource(source)) return { source };
|
|
5948
6562
|
const entry = await new RegistryClient({ workspaceRoot, offline: options.offline, warn: options.warn }).resolve(source);
|
|
5949
6563
|
if (!entry) {
|
|
5950
6564
|
if (options.offline) {
|
|
5951
6565
|
throw new Error(`Offline cannot refresh registry indexes (entry not found in cache: ${source}). Run without --offline first.`);
|
|
5952
6566
|
}
|
|
5953
|
-
throw new Error(`Registry entry not found: ${source}. Use an explicit path/git/skillkit/vercel source to bypass the registry.`);
|
|
6567
|
+
throw new Error(`Registry entry not found: ${source}. Use an explicit path/git/skillkit/vercel/mcp-registry/clawhub source to bypass the registry.`);
|
|
5954
6568
|
}
|
|
5955
6569
|
return { source: entry.source, registryEntry: entry };
|
|
5956
6570
|
}
|
|
6571
|
+
function selectorsFromRegistryEntry(entry) {
|
|
6572
|
+
return normalizeArtifactSelectors(entry?.select, entry?.skills);
|
|
6573
|
+
}
|
|
5957
6574
|
function mergeIndexes(indexes) {
|
|
5958
6575
|
const merged = /* @__PURE__ */ new Map();
|
|
5959
6576
|
for (const index of indexes) {
|
|
@@ -5964,7 +6581,7 @@ function mergeIndexes(indexes) {
|
|
|
5964
6581
|
return [...merged.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
5965
6582
|
}
|
|
5966
6583
|
function defaultRegistryCachePath() {
|
|
5967
|
-
return
|
|
6584
|
+
return join30(homedir5(), ".agentwheel", "registry-cache.json");
|
|
5968
6585
|
}
|
|
5969
6586
|
function sameSources(a, b) {
|
|
5970
6587
|
return a.length === b.length && a.every((source, index) => source === b[index]);
|
|
@@ -6021,7 +6638,23 @@ async function normalizeDependencySource(source, options) {
|
|
|
6021
6638
|
driver: "vercel-skills"
|
|
6022
6639
|
};
|
|
6023
6640
|
}
|
|
6024
|
-
|
|
6641
|
+
if (trimmed.startsWith("mcp-registry:")) {
|
|
6642
|
+
const spec = normalizeLiteralProviderSpec(trimmed, "mcp-registry:");
|
|
6643
|
+
return {
|
|
6644
|
+
source: spec,
|
|
6645
|
+
normalizedSource: spec,
|
|
6646
|
+
driver: "mcp-registry"
|
|
6647
|
+
};
|
|
6648
|
+
}
|
|
6649
|
+
if (trimmed.startsWith("clawhub:")) {
|
|
6650
|
+
const spec = normalizeLiteralProviderSpec(trimmed, "clawhub:");
|
|
6651
|
+
return {
|
|
6652
|
+
source: spec,
|
|
6653
|
+
normalizedSource: spec,
|
|
6654
|
+
driver: "clawhub"
|
|
6655
|
+
};
|
|
6656
|
+
}
|
|
6657
|
+
throw new Error(`Unsupported dependency source: ${source}. Use registry:<name>, ./, ../, local:, github:, git:, skillkit:, vercel:, mcp-registry:, or clawhub:.`);
|
|
6025
6658
|
}
|
|
6026
6659
|
function isBareRegistryName(source) {
|
|
6027
6660
|
return !source.includes(":") && !isLocalSource(source);
|
|
@@ -6034,16 +6667,16 @@ function localSourcePath(source) {
|
|
|
6034
6667
|
}
|
|
6035
6668
|
function resolveLocalPath(path, declaringPackageRoot) {
|
|
6036
6669
|
if (path === "~") return homedir6();
|
|
6037
|
-
if (path.startsWith("~/")) return
|
|
6038
|
-
if (path.startsWith("/")) return
|
|
6039
|
-
return
|
|
6670
|
+
if (path.startsWith("~/")) return resolve15(homedir6(), path.slice(2));
|
|
6671
|
+
if (path.startsWith("/")) return resolve15(path);
|
|
6672
|
+
return resolve15(declaringPackageRoot, path);
|
|
6040
6673
|
}
|
|
6041
6674
|
async function normalizeRegistrySource(name, options) {
|
|
6042
6675
|
const client = options.registryClient ?? new RegistryClient({ workspaceRoot: options.workspaceRoot });
|
|
6043
6676
|
const entry = await client.resolve(name);
|
|
6044
6677
|
if (!entry) {
|
|
6045
6678
|
throw new Error(
|
|
6046
|
-
`Registry entry not found: ${name}. Bare dependency names are registry-only inside package manifests; use ./, ../, local:, github:, git:, skillkit:, or
|
|
6679
|
+
`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
6680
|
);
|
|
6048
6681
|
}
|
|
6049
6682
|
const resolved = await normalizeDependencySource(entry.source, {
|
|
@@ -6102,6 +6735,11 @@ function normalizeProviderSpec(spec, declaringPackageRoot) {
|
|
|
6102
6735
|
}
|
|
6103
6736
|
return spec.trim();
|
|
6104
6737
|
}
|
|
6738
|
+
function normalizeLiteralProviderSpec(source, prefix) {
|
|
6739
|
+
const spec = source.slice(prefix.length).trim();
|
|
6740
|
+
if (!spec) throw new Error(`Provider dependency source must include a spec: ${prefix}<name>`);
|
|
6741
|
+
return `${prefix}${spec}`;
|
|
6742
|
+
}
|
|
6105
6743
|
|
|
6106
6744
|
// src/resolve/semver.ts
|
|
6107
6745
|
var semverPattern = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
@@ -6193,7 +6831,7 @@ function compareSemver(a, b) {
|
|
|
6193
6831
|
var cacheLocks = /* @__PURE__ */ new Map();
|
|
6194
6832
|
async function resolveDependencyGraph(roots, options) {
|
|
6195
6833
|
if (roots.length === 0) throw new Error("At least one graph root is required.");
|
|
6196
|
-
const graphRoot = await mkdtemp4(
|
|
6834
|
+
const graphRoot = await mkdtemp4(join31(tmpdir5(), "agentwheel-graph-"));
|
|
6197
6835
|
const fetchCache = /* @__PURE__ */ new Map();
|
|
6198
6836
|
const nodesByKey = /* @__PURE__ */ new Map();
|
|
6199
6837
|
const rootResults = [];
|
|
@@ -6213,7 +6851,9 @@ async function resolveDependencyGraph(roots, options) {
|
|
|
6213
6851
|
useLock: root.useLock ?? options.lockedResolution,
|
|
6214
6852
|
depth: 0,
|
|
6215
6853
|
optional: false,
|
|
6216
|
-
chain: [`workspace:${rootId}`]
|
|
6854
|
+
chain: [`workspace:${rootId}`],
|
|
6855
|
+
includeSuggestions: root.includeSuggestions ?? options.includeSuggestions,
|
|
6856
|
+
suggestionAliases: sortedUnique3([...root.suggestionAliases ?? [], ...options.suggestionAliases ?? []])
|
|
6217
6857
|
};
|
|
6218
6858
|
});
|
|
6219
6859
|
let iterations = 0;
|
|
@@ -6341,13 +6981,18 @@ Run without ${lockLabel === "Offline" ? "--offline" : "--frozen-lock"} first.`
|
|
|
6341
6981
|
selectionReasons: /* @__PURE__ */ new Map(),
|
|
6342
6982
|
processedNeeds: /* @__PURE__ */ new Set(),
|
|
6343
6983
|
processedPackageAliases: /* @__PURE__ */ new Set(),
|
|
6984
|
+
processedSuggestions: /* @__PURE__ */ new Set(),
|
|
6344
6985
|
depth: requirement.depth,
|
|
6345
|
-
fullPackageSelected: false
|
|
6986
|
+
fullPackageSelected: false,
|
|
6987
|
+
includeSuggestions: false,
|
|
6988
|
+
suggestionAliases: /* @__PURE__ */ new Set()
|
|
6346
6989
|
};
|
|
6347
6990
|
nodesByKey.set(nodeKey, state);
|
|
6348
6991
|
}
|
|
6349
6992
|
state.depth = Math.min(state.depth, requirement.depth);
|
|
6350
6993
|
state.fullPackageSelected = state.fullPackageSelected || requirement.select === void 0;
|
|
6994
|
+
state.includeSuggestions = state.includeSuggestions || requirement.includeSuggestions === true;
|
|
6995
|
+
for (const alias of requirement.suggestionAliases ?? []) state.suggestionAliases.add(alias);
|
|
6351
6996
|
state.requiredBy.add(requirement.requiredBy);
|
|
6352
6997
|
for (const selector of selected) addSelectedSelector(state, selector, requirement.selectionReason);
|
|
6353
6998
|
refreshNode(state);
|
|
@@ -6394,10 +7039,13 @@ Dependency chain: ${requirement.chain.join(" -> ")}`);
|
|
|
6394
7039
|
async function collectDependencyNeeds(state, fetched, options, chain) {
|
|
6395
7040
|
if (fetched.manifest?.schemaVersion !== 2) return [];
|
|
6396
7041
|
const dependencies = fetched.manifest.requires ?? {};
|
|
7042
|
+
const suggestions = fetched.manifest.suggests ?? {};
|
|
6397
7043
|
const dependencyEntries = Object.entries(dependencies).sort(([a], [b]) => a.localeCompare(b));
|
|
7044
|
+
const suggestionEntries = Object.entries(suggestions).sort(([a], [b]) => a.localeCompare(b));
|
|
7045
|
+
const suggestionOptions = suggestionOptionsForState(state, options);
|
|
6398
7046
|
const requirements = [];
|
|
6399
7047
|
if (options.noDeps) {
|
|
6400
|
-
warnNoDepsOnce(state, dependencyEntries.map(([alias]) => alias), options.warn);
|
|
7048
|
+
warnNoDepsOnce(state, [...dependencyEntries.map(([alias]) => alias), ...suggestionEntries.map(([alias]) => alias)], options.warn);
|
|
6401
7049
|
} else {
|
|
6402
7050
|
for (const [alias, dependency] of dependencyEntries) {
|
|
6403
7051
|
if (state.processedPackageAliases.has(alias)) continue;
|
|
@@ -6406,6 +7054,14 @@ async function collectDependencyNeeds(state, fetched, options, chain) {
|
|
|
6406
7054
|
if (!dependencyTargetsRuntime(dependency.runtimes, options.runtime, state.node.id, alias, options.warn)) continue;
|
|
6407
7055
|
requirements.push(dependencyRequirement(state, fetched, alias, dependency, dependency.select, chain, options.lockedResolution === true));
|
|
6408
7056
|
}
|
|
7057
|
+
for (const [alias, suggestion] of suggestionEntries) {
|
|
7058
|
+
if (state.processedSuggestions.has(alias)) continue;
|
|
7059
|
+
if (!shouldIncludeSuggestionAlias(alias, suggestionOptions, state.fullPackageSelected)) continue;
|
|
7060
|
+
if (!suggestion.select?.length && !(state.fullPackageSelected && suggestion.select === void 0) && !explicitSuggestionAlias(alias, suggestionOptions)) continue;
|
|
7061
|
+
state.processedSuggestions.add(alias);
|
|
7062
|
+
if (!dependencyTargetsRuntime(suggestion.runtimes, options.runtime, state.node.id, alias, options.warn)) continue;
|
|
7063
|
+
requirements.push(suggestionRequirement(state, fetched, alias, suggestion, suggestion.select, chain, suggestionOptions));
|
|
7064
|
+
}
|
|
6409
7065
|
}
|
|
6410
7066
|
const artifactsBySelector = new Map(fetched.artifacts.map((artifact) => [artifactSelectorKey(artifact), artifact]));
|
|
6411
7067
|
const artifactsByRelativePath = new Map(fetched.artifacts.map((artifact) => [artifact.relativePath.replaceAll("\\", "/"), artifact]));
|
|
@@ -6449,6 +7105,28 @@ async function collectDependencyNeeds(state, fetched, options, chain) {
|
|
|
6449
7105
|
}
|
|
6450
7106
|
addSelectedSelector(state, parsed.selector, `required by ${parentSelector}`);
|
|
6451
7107
|
}
|
|
7108
|
+
for (const suggestion of artifact.suggests ?? []) {
|
|
7109
|
+
const parsed = parseArtifactSuggestion(suggestion);
|
|
7110
|
+
if (!shouldIncludeSuggestionAlias(parsed.alias, suggestionOptions, true)) continue;
|
|
7111
|
+
if (!requirementTargetsRuntime(parsed.runtimes, options.runtime, `${state.node.id}:${parentSelector} -> ${parsed.raw}`, options.warn)) continue;
|
|
7112
|
+
if (options.noDeps) {
|
|
7113
|
+
warnNoDepsOnce(state, [parsed.alias], options.warn);
|
|
7114
|
+
continue;
|
|
7115
|
+
}
|
|
7116
|
+
const packageSuggestion = suggestionForAlias(suggestions, state.node.id, parsed.alias);
|
|
7117
|
+
if (!dependencyTargetsRuntime(packageSuggestion.runtimes, options.runtime, state.node.id, parsed.alias, options.warn)) continue;
|
|
7118
|
+
requirements.push(suggestionRequirement(
|
|
7119
|
+
state,
|
|
7120
|
+
fetched,
|
|
7121
|
+
parsed.alias,
|
|
7122
|
+
packageSuggestion,
|
|
7123
|
+
combinedSelectors(packageSuggestion.select, parsed.select),
|
|
7124
|
+
chain,
|
|
7125
|
+
suggestionOptions,
|
|
7126
|
+
parsed.optional || shouldTreatSuggestionAsOptional(parsed.alias, packageSuggestion, suggestionOptions),
|
|
7127
|
+
`suggested by ${parentSelector}`
|
|
7128
|
+
));
|
|
7129
|
+
}
|
|
6452
7130
|
for (const include of await collectIncludeNeeds(artifact, artifactsByRelativePath)) {
|
|
6453
7131
|
if (!include.alias) continue;
|
|
6454
7132
|
if (options.noDeps) {
|
|
@@ -6490,9 +7168,24 @@ function dependencyRequirement(state, fetched, alias, dependency, select, chain,
|
|
|
6490
7168
|
chain: [...chain, `${state.node.id}:${alias}`],
|
|
6491
7169
|
version: dependency.version,
|
|
6492
7170
|
integrity: dependency.integrity,
|
|
6493
|
-
selectionReason
|
|
7171
|
+
selectionReason,
|
|
7172
|
+
includeSuggestions: state.includeSuggestions,
|
|
7173
|
+
suggestionAliases: sortedUnique3([...state.suggestionAliases])
|
|
6494
7174
|
};
|
|
6495
7175
|
}
|
|
7176
|
+
function suggestionRequirement(state, fetched, alias, suggestion, select, chain, options, optional = shouldTreatSuggestionAsOptional(alias, suggestion, options), selectionReason) {
|
|
7177
|
+
return dependencyRequirement(
|
|
7178
|
+
state,
|
|
7179
|
+
fetched,
|
|
7180
|
+
alias,
|
|
7181
|
+
suggestion,
|
|
7182
|
+
select,
|
|
7183
|
+
chain,
|
|
7184
|
+
options.lockedResolution === true,
|
|
7185
|
+
optional,
|
|
7186
|
+
selectionReason
|
|
7187
|
+
);
|
|
7188
|
+
}
|
|
6496
7189
|
function dependencyForAlias(dependencies, nodeId, alias) {
|
|
6497
7190
|
const dependency = dependencies[alias];
|
|
6498
7191
|
if (!dependency) {
|
|
@@ -6500,6 +7193,13 @@ function dependencyForAlias(dependencies, nodeId, alias) {
|
|
|
6500
7193
|
}
|
|
6501
7194
|
return dependency;
|
|
6502
7195
|
}
|
|
7196
|
+
function suggestionForAlias(suggestions, nodeId, alias) {
|
|
7197
|
+
const suggestion = suggestions[alias];
|
|
7198
|
+
if (!suggestion) {
|
|
7199
|
+
throw new Error(`Suggestion alias not found in ${nodeId}: ${alias}`);
|
|
7200
|
+
}
|
|
7201
|
+
return suggestion;
|
|
7202
|
+
}
|
|
6503
7203
|
function warnNoDepsOnce(state, aliases, warn) {
|
|
6504
7204
|
const unique = sortedUnique3(aliases.filter(Boolean));
|
|
6505
7205
|
if (unique.length === 0 || state.processedPackageAliases.has("__noDepsWarned")) return;
|
|
@@ -6517,6 +7217,45 @@ function parseArtifactRequirement(requirement) {
|
|
|
6517
7217
|
runtimes: typeof requirement === "string" ? void 0 : requirement.runtimes
|
|
6518
7218
|
};
|
|
6519
7219
|
}
|
|
7220
|
+
function parseArtifactSuggestion(suggestion) {
|
|
7221
|
+
if (typeof suggestion === "string") {
|
|
7222
|
+
return {
|
|
7223
|
+
raw: suggestion,
|
|
7224
|
+
alias: suggestion,
|
|
7225
|
+
optional: false
|
|
7226
|
+
};
|
|
7227
|
+
}
|
|
7228
|
+
return {
|
|
7229
|
+
raw: suggestion.alias,
|
|
7230
|
+
alias: suggestion.alias,
|
|
7231
|
+
select: suggestion.select,
|
|
7232
|
+
optional: suggestion.optional === true,
|
|
7233
|
+
runtimes: suggestion.runtimes
|
|
7234
|
+
};
|
|
7235
|
+
}
|
|
7236
|
+
function shouldIncludeSuggestionAlias(alias, options, includeWhenAllSuggestions) {
|
|
7237
|
+
const aliases = new Set(options.suggestionAliases ?? []);
|
|
7238
|
+
if (aliases.has(alias)) return true;
|
|
7239
|
+
return options.includeSuggestions === true && includeWhenAllSuggestions;
|
|
7240
|
+
}
|
|
7241
|
+
function suggestionOptionsForState(state, options) {
|
|
7242
|
+
return {
|
|
7243
|
+
...options,
|
|
7244
|
+
includeSuggestions: state.includeSuggestions || options.includeSuggestions === true,
|
|
7245
|
+
suggestionAliases: sortedUnique3([...options.suggestionAliases ?? [], ...state.suggestionAliases])
|
|
7246
|
+
};
|
|
7247
|
+
}
|
|
7248
|
+
function explicitSuggestionAlias(alias, options) {
|
|
7249
|
+
return (options.suggestionAliases ?? []).includes(alias);
|
|
7250
|
+
}
|
|
7251
|
+
function shouldTreatSuggestionAsOptional(alias, suggestion, options) {
|
|
7252
|
+
if (suggestion.optional === true) return true;
|
|
7253
|
+
return options.includeSuggestions === true && !(options.suggestionAliases ?? []).includes(alias);
|
|
7254
|
+
}
|
|
7255
|
+
function combinedSelectors(base, extra) {
|
|
7256
|
+
const values = [...base ?? [], ...extra ?? []];
|
|
7257
|
+
return values.length > 0 ? sortedUnique3(values) : void 0;
|
|
7258
|
+
}
|
|
6520
7259
|
function parseDependencySelector(value) {
|
|
6521
7260
|
const cleaned = value.trim();
|
|
6522
7261
|
const slash = cleaned.indexOf("/");
|
|
@@ -6555,7 +7294,7 @@ async function collectIncludeNeeds(artifact, artifactsByRelativePath) {
|
|
|
6555
7294
|
const file = stack.shift();
|
|
6556
7295
|
if (scanned.has(file)) continue;
|
|
6557
7296
|
scanned.add(file);
|
|
6558
|
-
const content = await
|
|
7297
|
+
const content = await readFile22(file, "utf8");
|
|
6559
7298
|
for (const include of extractOpenPackIncludeSelectors(content)) {
|
|
6560
7299
|
await collectIncludeSelector(include.raw, include.optional, artifactsByRelativePath, scanned, stack, needs);
|
|
6561
7300
|
}
|
|
@@ -6598,7 +7337,7 @@ async function listMarkdownFiles2(root) {
|
|
|
6598
7337
|
const out = [];
|
|
6599
7338
|
async function walk2(dir) {
|
|
6600
7339
|
for (const entry of (await readdir6(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
6601
|
-
const full =
|
|
7340
|
+
const full = join31(dir, entry.name);
|
|
6602
7341
|
if (entry.isDirectory()) {
|
|
6603
7342
|
await walk2(full);
|
|
6604
7343
|
} else if (entry.isFile() && extname4(entry.name).toLowerCase() === ".md") {
|
|
@@ -6633,7 +7372,7 @@ async function fetchPackage(normalized, mode, options, fetchCache, refOverride)
|
|
|
6633
7372
|
const promise = (async () => {
|
|
6634
7373
|
const driver = getSourceDriver(normalized.driver);
|
|
6635
7374
|
const resolved = await driver.resolve(normalized.source, {
|
|
6636
|
-
cacheRoot: options.cacheRoot ??
|
|
7375
|
+
cacheRoot: options.cacheRoot ?? join31(options.workspaceRoot, ".agentwheel", "cache"),
|
|
6637
7376
|
mode,
|
|
6638
7377
|
ref: refOverride ?? normalized.requestedRef,
|
|
6639
7378
|
frozenLock: hardLockedCheckout
|
|
@@ -6643,7 +7382,7 @@ async function fetchPackage(normalized, mode, options, fetchCache, refOverride)
|
|
|
6643
7382
|
const exported = await driver.export(translated);
|
|
6644
7383
|
const manifest = await readPackageManifest(exported.resolvedPath);
|
|
6645
7384
|
const artifacts = await driver.list(exported);
|
|
6646
|
-
const name = manifest?.name ?? exported.packageName ??
|
|
7385
|
+
const name = manifest?.name ?? exported.packageName ?? basename19(exported.resolvedPath);
|
|
6647
7386
|
const version = manifest?.version ?? exported.packageVersion ?? "0.0.0";
|
|
6648
7387
|
const sourceHash = exported.sourceHash ?? await hashPath(exported.resolvedPath);
|
|
6649
7388
|
return {
|
|
@@ -6727,7 +7466,7 @@ function shouldCheckLockedRootSource(requirement) {
|
|
|
6727
7466
|
}
|
|
6728
7467
|
function isExplicitNonRegistrySource(source) {
|
|
6729
7468
|
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:");
|
|
7469
|
+
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
7470
|
}
|
|
6732
7471
|
function lockedRootSourceDrifted(declared, locked) {
|
|
6733
7472
|
return declared.normalizedSource !== locked.normalizedSource || declared.requestedRef !== locked.requestedRef;
|
|
@@ -6750,8 +7489,8 @@ function verifyIntegrity(integrity, sourceHash, label) {
|
|
|
6750
7489
|
async function withCachePathLock(path, fn) {
|
|
6751
7490
|
const previous = cacheLocks.get(path) ?? Promise.resolve();
|
|
6752
7491
|
let release = () => void 0;
|
|
6753
|
-
const current = previous.then(() => new Promise((
|
|
6754
|
-
release =
|
|
7492
|
+
const current = previous.then(() => new Promise((resolve21) => {
|
|
7493
|
+
release = resolve21;
|
|
6755
7494
|
}));
|
|
6756
7495
|
cacheLocks.set(path, current);
|
|
6757
7496
|
await previous;
|
|
@@ -6840,8 +7579,8 @@ async function mapLimit(items, limit, fn) {
|
|
|
6840
7579
|
|
|
6841
7580
|
// src/lifecycle/customization.ts
|
|
6842
7581
|
async function remember(workspaceRoot, runtime, text) {
|
|
6843
|
-
const overlayPath =
|
|
6844
|
-
await
|
|
7582
|
+
const overlayPath = join32(workspaceRoot, ".agentwheel", "overlays", runtime, "instructions.local.md");
|
|
7583
|
+
await mkdir17(dirname24(overlayPath), { recursive: true });
|
|
6845
7584
|
await appendFile(overlayPath, `${text.trim()}
|
|
6846
7585
|
`, "utf8");
|
|
6847
7586
|
return { overlayPath };
|
|
@@ -6864,9 +7603,9 @@ async function ejectArtifact(workspaceRoot, item) {
|
|
|
6864
7603
|
throw new Error(`Artifact not found: ${item}`);
|
|
6865
7604
|
}
|
|
6866
7605
|
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
|
|
7606
|
+
const ejectedPath = join32(workspaceRoot, ".agentwheel", "ejected", ...ejectedIdentity.split("/"), parsed.type, parsed.name);
|
|
7607
|
+
await mkdir17(dirname24(ejectedPath), { recursive: true });
|
|
7608
|
+
await rm9(ejectedPath, { recursive: true, force: true });
|
|
6870
7609
|
await cp6(artifact.stagedPath ?? artifact.sourcePath, ejectedPath, { recursive: artifact.kind === "dir", dereference: true });
|
|
6871
7610
|
return {
|
|
6872
7611
|
...parsed,
|
|
@@ -6877,7 +7616,7 @@ async function ejectArtifact(workspaceRoot, item) {
|
|
|
6877
7616
|
ejectedPath
|
|
6878
7617
|
};
|
|
6879
7618
|
} finally {
|
|
6880
|
-
await Promise.all(candidates.map((candidate) =>
|
|
7619
|
+
await Promise.all(candidates.map((candidate) => rm9(candidate.bundle.root, { recursive: true, force: true })));
|
|
6881
7620
|
}
|
|
6882
7621
|
}
|
|
6883
7622
|
function parseEjectItem(item) {
|
|
@@ -6907,7 +7646,7 @@ async function stageEjectCandidate(workspaceRoot, pkg) {
|
|
|
6907
7646
|
const adapter = pkg.adapterConfig ? await loadAdapterConfig(pkg.adapterConfig) : getAdapter(pkg.adapter);
|
|
6908
7647
|
const bundle = await stageSource(driver, normalized.source, {
|
|
6909
7648
|
adapter,
|
|
6910
|
-
cacheRoot:
|
|
7649
|
+
cacheRoot: join32(workspaceRoot, ".agentwheel", "cache"),
|
|
6911
7650
|
mode: pkg.mode,
|
|
6912
7651
|
ref: normalized.requestedRef ?? pkg.requestedRef
|
|
6913
7652
|
});
|
|
@@ -6954,12 +7693,12 @@ function ejectCommands(candidates, parsed) {
|
|
|
6954
7693
|
}
|
|
6955
7694
|
|
|
6956
7695
|
// src/lifecycle/profile.ts
|
|
6957
|
-
import { rm as
|
|
7696
|
+
import { rm as rm10 } from "fs/promises";
|
|
6958
7697
|
|
|
6959
7698
|
// src/lifecycle/source-plan.ts
|
|
6960
7699
|
import { createHash as createHash8 } from "crypto";
|
|
6961
|
-
import { mkdir as
|
|
6962
|
-
import { dirname as
|
|
7700
|
+
import { mkdir as mkdir19 } from "fs/promises";
|
|
7701
|
+
import { dirname as dirname26, join as join35, resolve as resolve16 } from "path";
|
|
6963
7702
|
|
|
6964
7703
|
// src/resolve/graph-diff.ts
|
|
6965
7704
|
function diffGraphLocks(previous, next) {
|
|
@@ -7081,11 +7820,11 @@ function short(hash) {
|
|
|
7081
7820
|
|
|
7082
7821
|
// src/resolve/render.ts
|
|
7083
7822
|
import { createHash as createHash7 } from "crypto";
|
|
7084
|
-
import { readFile as
|
|
7823
|
+
import { readFile as readFile23, mkdtemp as mkdtemp5 } from "fs/promises";
|
|
7085
7824
|
import { tmpdir as tmpdir6 } from "os";
|
|
7086
|
-
import { join as
|
|
7825
|
+
import { join as join33 } from "path";
|
|
7087
7826
|
async function renderGraphForTarget(graph, targetContext = {}) {
|
|
7088
|
-
const root = await mkdtemp5(
|
|
7827
|
+
const root = await mkdtemp5(join33(tmpdir6(), "agentwheel-render-"));
|
|
7089
7828
|
const artifacts = [];
|
|
7090
7829
|
const stagedNodes = /* @__PURE__ */ new Map();
|
|
7091
7830
|
const includeEdges = /* @__PURE__ */ new Map();
|
|
@@ -7158,7 +7897,8 @@ async function renderGraphForTarget(graph, targetContext = {}) {
|
|
|
7158
7897
|
const runtimeSelectedSet = new Set(normalizeArtifactSelectors(rawNode.node.selected) ?? []);
|
|
7159
7898
|
const runtimeArtifacts = targetContext.adapter ? filterArtifactsByRuntime2(selectedArtifacts, targetContext.adapter.name, runtimeSelectedSet) : selectedArtifacts;
|
|
7160
7899
|
const codexRenderedArtifacts = await renderCodexSubagents(runtimeArtifacts, staged.root, targetContext.adapter);
|
|
7161
|
-
const
|
|
7900
|
+
const openClawRenderedArtifacts = await renderOpenClawSubagents(codexRenderedArtifacts, staged.root, targetContext.adapter);
|
|
7901
|
+
const runtimeRenderedArtifacts = await renderCopilotArtifacts(openClawRenderedArtifacts, staged.root, targetContext.adapter);
|
|
7162
7902
|
const renderedArtifacts = targetContext.workspaceRoot && targetContext.adapter ? await applyCustomizations(runtimeRenderedArtifacts, {
|
|
7163
7903
|
workspaceRoot: targetContext.workspaceRoot,
|
|
7164
7904
|
adapter: targetContext.adapter,
|
|
@@ -7205,7 +7945,7 @@ async function artifactContentMap(artifacts) {
|
|
|
7205
7945
|
const out = /* @__PURE__ */ new Map();
|
|
7206
7946
|
for (const artifact of artifacts) {
|
|
7207
7947
|
if (artifact.kind !== "file") continue;
|
|
7208
|
-
out.set(artifact.relativePath.replaceAll("\\", "/"), await
|
|
7948
|
+
out.set(artifact.relativePath.replaceAll("\\", "/"), await readFile23(artifact.stagedPath ?? artifact.sourcePath, "utf8"));
|
|
7209
7949
|
}
|
|
7210
7950
|
return out;
|
|
7211
7951
|
}
|
|
@@ -7468,9 +8208,9 @@ function lockArtifactFor(artifact) {
|
|
|
7468
8208
|
}
|
|
7469
8209
|
|
|
7470
8210
|
// src/lifecycle/trust.ts
|
|
7471
|
-
import { mkdir as
|
|
8211
|
+
import { mkdir as mkdir18, readFile as readFile24 } from "fs/promises";
|
|
7472
8212
|
import { homedir as homedir7 } from "os";
|
|
7473
|
-
import { dirname as
|
|
8213
|
+
import { dirname as dirname25, join as join34 } from "path";
|
|
7474
8214
|
import { z as z8 } from "zod";
|
|
7475
8215
|
var trustStoreSchema = z8.object({
|
|
7476
8216
|
version: z8.literal(1),
|
|
@@ -7544,14 +8284,14 @@ function sortedUnique4(values) {
|
|
|
7544
8284
|
}
|
|
7545
8285
|
async function readTrustStore(path) {
|
|
7546
8286
|
if (!await pathExists(path)) return { version: 1, acceptedSources: [] };
|
|
7547
|
-
return trustStoreSchema.parse(JSON.parse(await
|
|
8287
|
+
return trustStoreSchema.parse(JSON.parse(await readFile24(path, "utf8")));
|
|
7548
8288
|
}
|
|
7549
8289
|
async function writeTrustStore(path, store) {
|
|
7550
|
-
await
|
|
8290
|
+
await mkdir18(dirname25(path), { recursive: true });
|
|
7551
8291
|
await writeJsonAtomic(path, trustStoreSchema.parse(store));
|
|
7552
8292
|
}
|
|
7553
8293
|
function defaultTrustStorePath() {
|
|
7554
|
-
return process.env.AGENTWHEEL_TRUST_STORE ??
|
|
8294
|
+
return process.env.AGENTWHEEL_TRUST_STORE ?? join34(homedir7(), ".agentwheel", "trust.json");
|
|
7555
8295
|
}
|
|
7556
8296
|
|
|
7557
8297
|
// src/lifecycle/source-plan.ts
|
|
@@ -7589,9 +8329,11 @@ async function createGraphSourcePlan(options) {
|
|
|
7589
8329
|
const registryClient = new RegistryClient({ workspaceRoot, offline: lockMode, offlineLabel: lockLabel, warn });
|
|
7590
8330
|
const graph = await resolveDependencyGraph(options.roots, {
|
|
7591
8331
|
workspaceRoot,
|
|
7592
|
-
cacheRoot:
|
|
8332
|
+
cacheRoot: join35(workspaceRoot, ".agentwheel", "cache"),
|
|
7593
8333
|
registryClient,
|
|
7594
8334
|
noDeps: options.noDeps,
|
|
8335
|
+
includeSuggestions: options.includeSuggestions,
|
|
8336
|
+
suggestionAliases: options.suggestionAliases,
|
|
7595
8337
|
lockedResolution: options.lockedResolution,
|
|
7596
8338
|
frozenLock: lockMode,
|
|
7597
8339
|
offline: options.offline,
|
|
@@ -7687,7 +8429,7 @@ async function readExistingGraphLock(path) {
|
|
|
7687
8429
|
return readGraphLock(path);
|
|
7688
8430
|
}
|
|
7689
8431
|
function pathForGraphLock(workspaceRoot, targetKey, adapter, targetFingerprint) {
|
|
7690
|
-
return
|
|
8432
|
+
return join35(workspaceRoot, ".agentwheel", "locks", sanitizePathSegment(targetKey), sanitizePathSegment(adapter), `${targetFingerprint}.graph-lock.json`);
|
|
7691
8433
|
}
|
|
7692
8434
|
function sanitizePathSegment(value) {
|
|
7693
8435
|
return value.replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "default";
|
|
@@ -7696,7 +8438,7 @@ function digestGraphLock(lock) {
|
|
|
7696
8438
|
return createHash8("sha256").update(canonicalGraphLockJson(lock)).digest("hex");
|
|
7697
8439
|
}
|
|
7698
8440
|
function workspaceOwnerId(workspaceRoot) {
|
|
7699
|
-
return `workspace-root:${
|
|
8441
|
+
return `workspace-root:${resolve16(workspaceRoot)}`;
|
|
7700
8442
|
}
|
|
7701
8443
|
function assertFrozenGraph(previousLock, graph, frozen, label) {
|
|
7702
8444
|
if (!frozen) return;
|
|
@@ -7751,7 +8493,7 @@ ${sources.map((source) => `- ${source}`).join("\n")}`);
|
|
|
7751
8493
|
}
|
|
7752
8494
|
|
|
7753
8495
|
// src/runtime/target.ts
|
|
7754
|
-
import { basename as
|
|
8496
|
+
import { basename as basename20, dirname as dirname27, join as join36, resolve as resolve17 } from "path";
|
|
7755
8497
|
var runtimeMarkers = [
|
|
7756
8498
|
{ adapter: "openclaw", dirs: [".openclaw", ".clawdbot", ".moltbot"] },
|
|
7757
8499
|
{ adapter: "claude", dirs: [".claude"] },
|
|
@@ -7760,9 +8502,9 @@ var runtimeMarkers = [
|
|
|
7760
8502
|
{ adapter: "copilot", dirs: [".github"] }
|
|
7761
8503
|
];
|
|
7762
8504
|
async function resolveRuntimeTarget(request = {}) {
|
|
7763
|
-
const cwd =
|
|
8505
|
+
const cwd = resolve17(request.cwd ?? process.cwd());
|
|
7764
8506
|
if (request.targetRoot) {
|
|
7765
|
-
const targetRoot =
|
|
8507
|
+
const targetRoot = resolve17(request.targetRoot);
|
|
7766
8508
|
return {
|
|
7767
8509
|
adapter: request.adapter ?? "openclaw",
|
|
7768
8510
|
installationType: request.installationType,
|
|
@@ -7793,7 +8535,7 @@ async function resolveRuntimeTarget(request = {}) {
|
|
|
7793
8535
|
async function resolveAllRuntimeTargets(request = {}) {
|
|
7794
8536
|
if (request.targetRoot) return [await resolveRuntimeTarget(request)];
|
|
7795
8537
|
if (request.agent) return [await resolveRuntimeTarget(request)];
|
|
7796
|
-
const cwd =
|
|
8538
|
+
const cwd = resolve17(request.cwd ?? process.cwd());
|
|
7797
8539
|
const workspaceRoot = await findWorkspaceRoot(cwd);
|
|
7798
8540
|
const config = await readMergedWorkspaceConfig(workspaceRoot, { globalRoot: request.globalRoot });
|
|
7799
8541
|
const targets = Object.entries(config.agents).map(([name]) => targetFromAgent(name, config, workspaceRoot, request.installationType));
|
|
@@ -7803,8 +8545,8 @@ async function resolveAllRuntimeTargets(request = {}) {
|
|
|
7803
8545
|
return targets;
|
|
7804
8546
|
}
|
|
7805
8547
|
async function resolveProfileRuntimeTargets(request) {
|
|
7806
|
-
const cwd =
|
|
7807
|
-
const workspaceRoot = request.targetRoot ?
|
|
8548
|
+
const cwd = resolve17(request.cwd ?? process.cwd());
|
|
8549
|
+
const workspaceRoot = request.targetRoot ? resolve17(request.targetRoot) : await findWorkspaceRoot(cwd);
|
|
7808
8550
|
const config = await readMergedWorkspaceConfig(workspaceRoot, { globalRoot: request.globalRoot });
|
|
7809
8551
|
const profile = config.profiles[request.profile];
|
|
7810
8552
|
if (!profile) {
|
|
@@ -7857,14 +8599,14 @@ async function detectRuntimeTarget(cwd = process.cwd(), adapterFilter) {
|
|
|
7857
8599
|
return unique[0];
|
|
7858
8600
|
}
|
|
7859
8601
|
async function detectRuntimeTargets(cwd = process.cwd(), adapterFilter) {
|
|
7860
|
-
const root =
|
|
8602
|
+
const root = resolve17(cwd);
|
|
7861
8603
|
const matches = [];
|
|
7862
8604
|
for (const marker of runtimeMarkers) {
|
|
7863
8605
|
if (adapterFilter && marker.adapter !== adapterFilter) continue;
|
|
7864
8606
|
for (const dir of marker.dirs) {
|
|
7865
|
-
if (
|
|
7866
|
-
matches.push({ adapter: marker.adapter, targetRoot:
|
|
7867
|
-
} else if (await pathExists(
|
|
8607
|
+
if (basename20(root) === dir) {
|
|
8608
|
+
matches.push({ adapter: marker.adapter, targetRoot: dirname27(root) });
|
|
8609
|
+
} else if (await pathExists(join36(root, dir))) {
|
|
7868
8610
|
matches.push({ adapter: marker.adapter, targetRoot: root });
|
|
7869
8611
|
}
|
|
7870
8612
|
}
|
|
@@ -7901,9 +8643,9 @@ function dedupeTargets(matches) {
|
|
|
7901
8643
|
return [...byKey.values()];
|
|
7902
8644
|
}
|
|
7903
8645
|
function runtimeScanRoot(request) {
|
|
7904
|
-
const root =
|
|
8646
|
+
const root = resolve17(request.targetRoot ?? request.cwd ?? process.cwd());
|
|
7905
8647
|
if (request.targetRoot) return root;
|
|
7906
|
-
return runtimeMarkers.some((marker) => marker.dirs.includes(
|
|
8648
|
+
return runtimeMarkers.some((marker) => marker.dirs.includes(basename20(root))) ? dirname27(root) : root;
|
|
7907
8649
|
}
|
|
7908
8650
|
|
|
7909
8651
|
// src/lifecycle/profile.ts
|
|
@@ -7940,7 +8682,9 @@ async function syncProfile(options) {
|
|
|
7940
8682
|
ref: pkg.requestedRef,
|
|
7941
8683
|
select: selected ?? normalizeArtifactSelectors(pkg.select, pkg.skills),
|
|
7942
8684
|
aliases: pkg.aliases,
|
|
7943
|
-
overrides: pkg.overrides
|
|
8685
|
+
overrides: pkg.overrides,
|
|
8686
|
+
includeSuggestions: options.includeSuggestions === true || pkg.withSuggestions === true,
|
|
8687
|
+
suggestionAliases: combinedSuggestionAliases(pkg.suggestions, options.suggestionAliases)
|
|
7944
8688
|
})),
|
|
7945
8689
|
targetRoot: target.targetRoot,
|
|
7946
8690
|
workspaceRoot: options.workspaceRoot,
|
|
@@ -7960,6 +8704,8 @@ async function syncProfile(options) {
|
|
|
7960
8704
|
},
|
|
7961
8705
|
installationType,
|
|
7962
8706
|
noDeps: options.noDeps,
|
|
8707
|
+
includeSuggestions: options.includeSuggestions,
|
|
8708
|
+
suggestionAliases: options.suggestionAliases,
|
|
7963
8709
|
lockedResolution: options.lockedResolution,
|
|
7964
8710
|
frozenLock: options.frozenLock,
|
|
7965
8711
|
offline: options.offline,
|
|
@@ -7989,7 +8735,7 @@ async function syncProfile(options) {
|
|
|
7989
8735
|
});
|
|
7990
8736
|
}
|
|
7991
8737
|
} finally {
|
|
7992
|
-
await
|
|
8738
|
+
await rm10(graphPlan.bundle.root, { recursive: true, force: true });
|
|
7993
8739
|
}
|
|
7994
8740
|
}
|
|
7995
8741
|
return results;
|
|
@@ -8000,6 +8746,7 @@ async function packageFromSource(source, options) {
|
|
|
8000
8746
|
warn: options.warn
|
|
8001
8747
|
});
|
|
8002
8748
|
const driver = options.driver ?? inferSourceDriverName(resolved.source);
|
|
8749
|
+
const selectedArtifacts = normalizeArtifactSelectors(options.select, options.skills) ?? selectorsFromRegistryEntry(resolved.registryEntry);
|
|
8003
8750
|
return {
|
|
8004
8751
|
name: resolved.registryEntry?.name ?? source,
|
|
8005
8752
|
source: resolved.source,
|
|
@@ -8007,15 +8754,184 @@ async function packageFromSource(source, options) {
|
|
|
8007
8754
|
adapter: "openclaw",
|
|
8008
8755
|
installationType: options.installationType,
|
|
8009
8756
|
mode: options.mode ?? "pinned",
|
|
8010
|
-
select:
|
|
8011
|
-
|
|
8757
|
+
select: selectedArtifacts,
|
|
8758
|
+
withSuggestions: options.includeSuggestions === true ? true : void 0,
|
|
8759
|
+
suggestions: options.suggestionAliases
|
|
8012
8760
|
};
|
|
8013
8761
|
}
|
|
8762
|
+
function combinedSuggestionAliases(packageAliases, optionAliases) {
|
|
8763
|
+
const aliases = [...packageAliases ?? [], ...optionAliases ?? []].map((item) => item.trim()).filter(Boolean);
|
|
8764
|
+
return aliases.length > 0 ? [...new Set(aliases)].sort((a, b) => a.localeCompare(b)) : void 0;
|
|
8765
|
+
}
|
|
8766
|
+
|
|
8767
|
+
// src/registry/publish.ts
|
|
8768
|
+
var DEFAULT_REGISTRY_SUBMISSION_URL = "https://github.com/NestDevLab/agentwheel-registry/issues/new";
|
|
8769
|
+
var registryEntryTypes = ["package", "skill", "plugin", "mcp", "adapter"];
|
|
8770
|
+
var explicitSourcePrefixes = ["github:", "git:", "skillkit:", "vercel:", "mcp-registry:", "clawhub:"];
|
|
8771
|
+
function createRegistryPublishDraft(sourceInput, options = {}) {
|
|
8772
|
+
const source = normalizeCatalogueSource(sourceInput);
|
|
8773
|
+
const entry = {
|
|
8774
|
+
name: normalizeRegistryName(options.name ?? inferRegistryName(source)),
|
|
8775
|
+
source,
|
|
8776
|
+
type: options.type ? parseRegistryEntryType(options.type) : inferRegistryEntryType(source),
|
|
8777
|
+
description: options.description?.trim() ?? "",
|
|
8778
|
+
tags: normalizeTags(options.tags ?? [])
|
|
8779
|
+
};
|
|
8780
|
+
const selectors = normalizeArtifactSelectors(options.select, options.skills);
|
|
8781
|
+
if (selectors?.length) entry.select = selectors;
|
|
8782
|
+
if (options.skills?.length) entry.skills = normalizeSkillNames(options.skills);
|
|
8783
|
+
const installCommand = installCommandForEntry(entry);
|
|
8784
|
+
return {
|
|
8785
|
+
entry,
|
|
8786
|
+
installCommand,
|
|
8787
|
+
issueUrl: registrySubmissionUrl(entry, installCommand, options.submissionUrl ?? DEFAULT_REGISTRY_SUBMISSION_URL)
|
|
8788
|
+
};
|
|
8789
|
+
}
|
|
8790
|
+
function normalizeCatalogueSource(sourceInput) {
|
|
8791
|
+
const source = sourceInput.trim();
|
|
8792
|
+
if (!source) throw new Error("Catalogue source is required.");
|
|
8793
|
+
const unprefixedGitUrl = source.startsWith("git+http://") || source.startsWith("git+https://") ? source.slice("git+".length) : source;
|
|
8794
|
+
const githubSource = normalizeGitHubUrl(unprefixedGitUrl);
|
|
8795
|
+
if (githubSource) return githubSource;
|
|
8796
|
+
if (isHttpUrl(unprefixedGitUrl)) return `git:${unprefixedGitUrl}`;
|
|
8797
|
+
if (explicitSourcePrefixes.some((prefix) => source.startsWith(prefix))) return source;
|
|
8798
|
+
if (source.startsWith(".") || source.startsWith("/") || source.startsWith("local:")) {
|
|
8799
|
+
throw new Error("Catalogue submissions must use a public source, not a local path.");
|
|
8800
|
+
}
|
|
8801
|
+
const shorthand = normalizeOwnerRepoShorthand(source);
|
|
8802
|
+
if (shorthand) return shorthand;
|
|
8803
|
+
throw new Error(`Unsupported catalogue source: ${sourceInput}. Use a GitHub URL, github:owner/repo, git:https://..., skillkit:, vercel:, mcp-registry:, or clawhub:.`);
|
|
8804
|
+
}
|
|
8805
|
+
function inferRegistryEntryType(source) {
|
|
8806
|
+
if (source.startsWith("mcp-registry:")) return "mcp";
|
|
8807
|
+
if (source.startsWith("clawhub:")) return "plugin";
|
|
8808
|
+
if (source.startsWith("skillkit:") || source.startsWith("vercel:")) return "skill";
|
|
8809
|
+
return "package";
|
|
8810
|
+
}
|
|
8811
|
+
function parseRegistryEntryType(value) {
|
|
8812
|
+
const normalized = value.trim().toLowerCase();
|
|
8813
|
+
if (registryEntryTypes.includes(normalized)) return normalized;
|
|
8814
|
+
throw new Error(`Unsupported registry entry type: ${value}. Use ${registryEntryTypes.join(", ")}.`);
|
|
8815
|
+
}
|
|
8816
|
+
function installCommandForEntry(entry) {
|
|
8817
|
+
const base = ["agentwheel", "install", entry.source, ...selectorArgsForEntry(entry)];
|
|
8818
|
+
if (entry.type === "mcp") return [...base, "--adapter", "claude", "--local", "--dry-run"].map(shellQuoteArg).join(" ");
|
|
8819
|
+
if (entry.source.startsWith("clawhub:") || entry.type === "plugin") {
|
|
8820
|
+
return [...base, "--adapter", "openclaw", "--local", "--dry-run"].map(shellQuoteArg).join(" ");
|
|
8821
|
+
}
|
|
8822
|
+
return [...base, "--adapter", "codex", "--local", "--dry-run"].map(shellQuoteArg).join(" ");
|
|
8823
|
+
}
|
|
8824
|
+
function registrySubmissionUrl(entry, installCommand, baseUrl) {
|
|
8825
|
+
const url = new URL(baseUrl);
|
|
8826
|
+
url.searchParams.set("title", `Catalogue submission: ${entry.name}`);
|
|
8827
|
+
url.searchParams.set("body", registrySubmissionBody(entry, installCommand));
|
|
8828
|
+
return url.toString();
|
|
8829
|
+
}
|
|
8830
|
+
function registrySubmissionBody(entry, installCommand) {
|
|
8831
|
+
const descriptionNote = entry.description ? [] : ["", "Note: add a concise description before submitting."];
|
|
8832
|
+
return [
|
|
8833
|
+
"## Agentwheel catalogue submission",
|
|
8834
|
+
"",
|
|
8835
|
+
"Please review this generated registry entry:",
|
|
8836
|
+
"",
|
|
8837
|
+
"```json",
|
|
8838
|
+
JSON.stringify(entry, null, 2),
|
|
8839
|
+
"```",
|
|
8840
|
+
"",
|
|
8841
|
+
"## Verification",
|
|
8842
|
+
"",
|
|
8843
|
+
`Source: \`${entry.source}\``,
|
|
8844
|
+
`Suggested check: \`${installCommand}\``,
|
|
8845
|
+
"",
|
|
8846
|
+
"## Checklist",
|
|
8847
|
+
"",
|
|
8848
|
+
"- [ ] The source is public and installable.",
|
|
8849
|
+
"- [ ] The description is concise and factual.",
|
|
8850
|
+
"- [ ] Tags help discovery.",
|
|
8851
|
+
...descriptionNote
|
|
8852
|
+
].join("\n");
|
|
8853
|
+
}
|
|
8854
|
+
function normalizeGitHubUrl(value) {
|
|
8855
|
+
let url;
|
|
8856
|
+
try {
|
|
8857
|
+
url = new URL(value);
|
|
8858
|
+
} catch {
|
|
8859
|
+
return void 0;
|
|
8860
|
+
}
|
|
8861
|
+
if (url.hostname.toLowerCase() !== "github.com") return void 0;
|
|
8862
|
+
const segments = url.pathname.split("/").filter(Boolean).map((segment) => decodeURIComponent(segment));
|
|
8863
|
+
const [owner, repoSegment] = segments;
|
|
8864
|
+
if (!owner || !repoSegment) return void 0;
|
|
8865
|
+
const repo = repoSegment.replace(/\.git$/i, "");
|
|
8866
|
+
let ref = url.hash ? decodeURIComponent(url.hash.slice(1)) : "";
|
|
8867
|
+
if (segments[2] === "tree" && segments.length > 3) {
|
|
8868
|
+
ref = segments.slice(3).join("/");
|
|
8869
|
+
}
|
|
8870
|
+
return `github:${owner}/${repo}${ref ? `#${ref}` : ""}`;
|
|
8871
|
+
}
|
|
8872
|
+
function normalizeOwnerRepoShorthand(value) {
|
|
8873
|
+
const match = /^([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)(#[^\s]+)?$/.exec(value);
|
|
8874
|
+
if (!match) return void 0;
|
|
8875
|
+
return `github:${match[1]}/${match[2]}${match[3] ?? ""}`;
|
|
8876
|
+
}
|
|
8877
|
+
function inferRegistryName(source) {
|
|
8878
|
+
const withoutRef = source.split("#", 1)[0];
|
|
8879
|
+
if (withoutRef.startsWith("github:")) return lastPathSegment(withoutRef.slice("github:".length));
|
|
8880
|
+
if (withoutRef.startsWith("git:")) return nameFromGitUrl(withoutRef.slice("git:".length));
|
|
8881
|
+
if (withoutRef.startsWith("skillkit:")) return lastPathSegment(withoutRef.slice("skillkit:".length));
|
|
8882
|
+
if (withoutRef.startsWith("vercel:")) return lastPathSegment(withoutRef.slice("vercel:".length));
|
|
8883
|
+
if (withoutRef.startsWith("mcp-registry:")) return lastPathSegment(withoutRef.slice("mcp-registry:".length));
|
|
8884
|
+
if (withoutRef.startsWith("clawhub:")) return lastPathSegment(withoutRef.slice("clawhub:".length));
|
|
8885
|
+
return withoutRef;
|
|
8886
|
+
}
|
|
8887
|
+
function nameFromGitUrl(value) {
|
|
8888
|
+
try {
|
|
8889
|
+
return lastPathSegment(new URL(value).pathname);
|
|
8890
|
+
} catch {
|
|
8891
|
+
return lastPathSegment(value);
|
|
8892
|
+
}
|
|
8893
|
+
}
|
|
8894
|
+
function lastPathSegment(value) {
|
|
8895
|
+
const trimmed = value.replace(/\.git$/i, "").replace(/\/+$/g, "");
|
|
8896
|
+
const segments = trimmed.split("/").filter(Boolean);
|
|
8897
|
+
return segments.at(-1) ?? trimmed;
|
|
8898
|
+
}
|
|
8899
|
+
function normalizeRegistryName(value) {
|
|
8900
|
+
const name = slugify(value);
|
|
8901
|
+
if (!name) throw new Error("Registry entry name could not be inferred. Pass --name <short-name>.");
|
|
8902
|
+
return name;
|
|
8903
|
+
}
|
|
8904
|
+
function normalizeTags(tags) {
|
|
8905
|
+
const normalized = tags.flatMap((tag) => tag.split(",")).map((tag) => slugify(tag)).filter(Boolean);
|
|
8906
|
+
return [...new Set(normalized)];
|
|
8907
|
+
}
|
|
8908
|
+
function normalizeSkillNames(skills) {
|
|
8909
|
+
return [...new Set(skills.flatMap((skill) => skill.split(",")).map((skill) => skill.trim()).filter(Boolean))];
|
|
8910
|
+
}
|
|
8911
|
+
function selectorArgsForEntry(entry) {
|
|
8912
|
+
if (entry.skills?.length) return entry.skills.flatMap((skill) => ["--skill", skill]);
|
|
8913
|
+
return (entry.select ?? []).flatMap((selector) => ["--select", selector]);
|
|
8914
|
+
}
|
|
8915
|
+
function slugify(value) {
|
|
8916
|
+
return value.trim().toLowerCase().replace(/\.git$/i, "").replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
8917
|
+
}
|
|
8918
|
+
function isHttpUrl(value) {
|
|
8919
|
+
try {
|
|
8920
|
+
const url = new URL(value);
|
|
8921
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
8922
|
+
} catch {
|
|
8923
|
+
return false;
|
|
8924
|
+
}
|
|
8925
|
+
}
|
|
8926
|
+
function shellQuoteArg(value) {
|
|
8927
|
+
if (/^[A-Za-z0-9_./:@=-]+$/.test(value)) return value;
|
|
8928
|
+
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
8929
|
+
}
|
|
8014
8930
|
|
|
8015
8931
|
// src/cli/update-check.ts
|
|
8016
|
-
import { mkdir as
|
|
8932
|
+
import { mkdir as mkdir20, readFile as readFile25, writeFile as writeFile18 } from "fs/promises";
|
|
8017
8933
|
import { homedir as homedir8 } from "os";
|
|
8018
|
-
import { dirname as
|
|
8934
|
+
import { dirname as dirname28, join as join37 } from "path";
|
|
8019
8935
|
var DEFAULT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
8020
8936
|
var DEFAULT_TIMEOUT_MS = 300;
|
|
8021
8937
|
var REGISTRY_URL = "https://registry.npmjs.org/agentwheel";
|
|
@@ -8023,7 +8939,7 @@ async function maybeCheckForUpdate(options) {
|
|
|
8023
8939
|
if (isDisabled(options)) return;
|
|
8024
8940
|
const now = options.now?.() ?? /* @__PURE__ */ new Date();
|
|
8025
8941
|
const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
|
|
8026
|
-
const cachePath = options.cachePath ??
|
|
8942
|
+
const cachePath = options.cachePath ?? join37(homedir8(), ".agentwheel", "update-check.json");
|
|
8027
8943
|
try {
|
|
8028
8944
|
const cached = await readCache(cachePath);
|
|
8029
8945
|
if (cached && now.getTime() - Date.parse(cached.checkedAt) < ttlMs) {
|
|
@@ -8060,7 +8976,7 @@ async function fetchLatestVersion(fetchImpl, timeoutMs) {
|
|
|
8060
8976
|
}
|
|
8061
8977
|
async function readCache(path) {
|
|
8062
8978
|
try {
|
|
8063
|
-
const parsed = JSON.parse(await
|
|
8979
|
+
const parsed = JSON.parse(await readFile25(path, "utf8"));
|
|
8064
8980
|
if (typeof parsed.checkedAt !== "string" || typeof parsed.latest !== "string") return void 0;
|
|
8065
8981
|
return { checkedAt: parsed.checkedAt, latest: parsed.latest };
|
|
8066
8982
|
} catch {
|
|
@@ -8068,8 +8984,8 @@ async function readCache(path) {
|
|
|
8068
8984
|
}
|
|
8069
8985
|
}
|
|
8070
8986
|
async function writeCache(path, cache) {
|
|
8071
|
-
await
|
|
8072
|
-
await
|
|
8987
|
+
await mkdir20(dirname28(path), { recursive: true });
|
|
8988
|
+
await writeFile18(path, `${JSON.stringify(cache, null, 2)}
|
|
8073
8989
|
`, "utf8");
|
|
8074
8990
|
}
|
|
8075
8991
|
function warnIfNewer(latest, current, stderr = process.stderr) {
|
|
@@ -8092,9 +9008,9 @@ function normalizeVersion(version) {
|
|
|
8092
9008
|
|
|
8093
9009
|
// src/model/package-validate.ts
|
|
8094
9010
|
import { stat as stat11 } from "fs/promises";
|
|
8095
|
-
import { resolve as
|
|
9011
|
+
import { resolve as resolve18 } from "path";
|
|
8096
9012
|
async function validatePackage(root) {
|
|
8097
|
-
const packageRoot =
|
|
9013
|
+
const packageRoot = resolve18(root);
|
|
8098
9014
|
const findings = [];
|
|
8099
9015
|
const manifestPath = await findPackageManifestPath(packageRoot);
|
|
8100
9016
|
if (!manifestPath) {
|
|
@@ -8141,6 +9057,14 @@ function validateDeclaredSelectors(manifest, findings, manifestPath) {
|
|
|
8141
9057
|
validateSelector(selector, `requires.${alias}.select`, findings, manifestPath, { localOnly: true });
|
|
8142
9058
|
}
|
|
8143
9059
|
}
|
|
9060
|
+
for (const [alias, suggestion] of Object.entries(manifest.suggests ?? {})) {
|
|
9061
|
+
if (!alias.trim()) {
|
|
9062
|
+
findings.push({ level: "error", message: "Suggestion alias must be non-empty", path: manifestPath });
|
|
9063
|
+
}
|
|
9064
|
+
for (const selector of suggestion.select ?? []) {
|
|
9065
|
+
validateSelector(selector, `suggests.${alias}.select`, findings, manifestPath, { localOnly: true });
|
|
9066
|
+
}
|
|
9067
|
+
}
|
|
8144
9068
|
}
|
|
8145
9069
|
for (const [provideIndex, provide] of manifest.provides.entries()) {
|
|
8146
9070
|
if (!("items" in provide) || !provide.items) continue;
|
|
@@ -8149,6 +9073,15 @@ function validateDeclaredSelectors(manifest, findings, manifestPath) {
|
|
|
8149
9073
|
const selector = typeof requirement === "string" ? requirement : requirement.selector;
|
|
8150
9074
|
validateSelector(selector, `provides[${provideIndex}].items.${itemName}.requires`, findings, manifestPath, { aliases: manifest.schemaVersion === 2 ? Object.keys(manifest.requires ?? {}) : [] });
|
|
8151
9075
|
}
|
|
9076
|
+
for (const suggestion of item.suggests ?? []) {
|
|
9077
|
+
const alias = typeof suggestion === "string" ? suggestion : suggestion.alias;
|
|
9078
|
+
if (manifest.schemaVersion === 2 && !Object.keys(manifest.suggests ?? {}).includes(alias)) {
|
|
9079
|
+
findings.push({ level: "error", message: `provides[${provideIndex}].items.${itemName}.suggests: suggestion alias not declared: ${alias}`, path: manifestPath });
|
|
9080
|
+
}
|
|
9081
|
+
for (const selector of typeof suggestion === "string" ? [] : suggestion.select ?? []) {
|
|
9082
|
+
validateSelector(selector, `provides[${provideIndex}].items.${itemName}.suggests.${alias}.select`, findings, manifestPath, { localOnly: true });
|
|
9083
|
+
}
|
|
9084
|
+
}
|
|
8152
9085
|
for (const entry of item.compose ?? []) {
|
|
8153
9086
|
validateSelector(entry.include, `provides[${provideIndex}].items.${itemName}.compose.include`, findings, manifestPath, {
|
|
8154
9087
|
aliases: manifest.schemaVersion === 2 ? Object.keys(manifest.requires ?? {}) : [],
|
|
@@ -8162,7 +9095,7 @@ async function validateManifestComposeInclude(packageRoot, selector, optional, f
|
|
|
8162
9095
|
try {
|
|
8163
9096
|
validateSelector(selector, "compose.include", findings, manifestPath, { fragmentsOnly: true, aliases });
|
|
8164
9097
|
if (isCrossPackageSelector(selector)) return;
|
|
8165
|
-
const full =
|
|
9098
|
+
const full = resolve18(packageRoot, selector);
|
|
8166
9099
|
if (full !== packageRoot && !full.startsWith(`${packageRoot}/`)) {
|
|
8167
9100
|
findings.push({ level: "error", message: `Compose include escapes package root: ${selector}`, path: manifestPath });
|
|
8168
9101
|
return;
|
|
@@ -8215,13 +9148,13 @@ function isCrossPackageSelector(value) {
|
|
|
8215
9148
|
}
|
|
8216
9149
|
|
|
8217
9150
|
// src/model/package-migrate.ts
|
|
8218
|
-
import { readFile as
|
|
8219
|
-
import { join as
|
|
9151
|
+
import { readFile as readFile26, rename as rename4, writeFile as writeFile19 } from "fs/promises";
|
|
9152
|
+
import { join as join39, resolve as resolve19 } from "path";
|
|
8220
9153
|
import { applyEdits, modify, parse as parse4 } from "jsonc-parser";
|
|
8221
9154
|
async function migratePackageManifest(root) {
|
|
8222
|
-
const packageRoot =
|
|
9155
|
+
const packageRoot = resolve19(root);
|
|
8223
9156
|
for (const name of openPackManifestNames) {
|
|
8224
|
-
const path =
|
|
9157
|
+
const path = join39(packageRoot, name);
|
|
8225
9158
|
if (await pathExists(path)) {
|
|
8226
9159
|
return { changed: false, to: path, message: `Package already uses ${name}.` };
|
|
8227
9160
|
}
|
|
@@ -8230,18 +9163,18 @@ async function migratePackageManifest(root) {
|
|
|
8230
9163
|
if (!legacyName) {
|
|
8231
9164
|
throw new Error(`No legacy package manifest found at ${packageRoot}`);
|
|
8232
9165
|
}
|
|
8233
|
-
const from =
|
|
9166
|
+
const from = join39(packageRoot, legacyName);
|
|
8234
9167
|
const toName = legacyName.endsWith(".jsonc") ? "openpack.jsonc" : "openpack.json";
|
|
8235
|
-
const to =
|
|
8236
|
-
const content = await
|
|
9168
|
+
const to = join39(packageRoot, toName);
|
|
9169
|
+
const content = await readFile26(from, "utf8");
|
|
8237
9170
|
const updated = updateSchemaVersion(content);
|
|
8238
9171
|
await rename4(from, to);
|
|
8239
|
-
await
|
|
9172
|
+
await writeFile19(to, updated, "utf8");
|
|
8240
9173
|
return { changed: true, from, to, message: `Migrated ${legacyName} to ${toName}.` };
|
|
8241
9174
|
}
|
|
8242
9175
|
async function firstExistingLegacyManifest(root) {
|
|
8243
9176
|
for (const name of legacyPackageManifestNames) {
|
|
8244
|
-
if (await pathExists(
|
|
9177
|
+
if (await pathExists(join39(root, name))) return name;
|
|
8245
9178
|
}
|
|
8246
9179
|
return void 0;
|
|
8247
9180
|
}
|
|
@@ -8259,20 +9192,20 @@ function updateSchemaVersion(content) {
|
|
|
8259
9192
|
|
|
8260
9193
|
// src/cli/version.ts
|
|
8261
9194
|
import { readFileSync } from "fs";
|
|
8262
|
-
import { dirname as
|
|
9195
|
+
import { dirname as dirname29, join as join40 } from "path";
|
|
8263
9196
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8264
9197
|
var FALLBACK_VERSION = "0.0.0";
|
|
8265
9198
|
function resolveCliVersion() {
|
|
8266
|
-
let dir =
|
|
9199
|
+
let dir = dirname29(fileURLToPath2(import.meta.url));
|
|
8267
9200
|
while (true) {
|
|
8268
9201
|
try {
|
|
8269
|
-
const pkg = JSON.parse(readFileSync(
|
|
9202
|
+
const pkg = JSON.parse(readFileSync(join40(dir, "package.json"), "utf8"));
|
|
8270
9203
|
if (pkg.name === "agentwheel" && typeof pkg.version === "string") {
|
|
8271
9204
|
return pkg.version;
|
|
8272
9205
|
}
|
|
8273
9206
|
} catch {
|
|
8274
9207
|
}
|
|
8275
|
-
const parent =
|
|
9208
|
+
const parent = dirname29(dir);
|
|
8276
9209
|
if (parent === dir) return FALLBACK_VERSION;
|
|
8277
9210
|
dir = parent;
|
|
8278
9211
|
}
|
|
@@ -8308,7 +9241,7 @@ program.command("init").description("initialize an agentwheel workspace or packa
|
|
|
8308
9241
|
if (bootstrapPackage) console.log("Auto-added the agentwheel bootstrap skill for openclaw.");
|
|
8309
9242
|
console.log(nextInstallNudge());
|
|
8310
9243
|
});
|
|
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,
|
|
9244
|
+
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
9245
|
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
8313
9246
|
const targetRoot = normalizeTargetRoot(normalizedOptions.targetRoot ?? process.cwd());
|
|
8314
9247
|
const entry = await packageEntryFromSource(source, targetRoot, normalizedOptions);
|
|
@@ -8317,10 +9250,10 @@ program.command("add").description("add a package to .agentwheel/config.json wit
|
|
|
8317
9250
|
});
|
|
8318
9251
|
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
9252
|
const targetRoot = normalizeTargetRoot(options.targetRoot);
|
|
8320
|
-
const selectedArtifacts = selectedArtifactsFromOptions(options);
|
|
8321
9253
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
9254
|
+
const selectedArtifacts = selectedArtifactsFromOptionsOrRegistry(options, resolvedInput.registryEntry);
|
|
8322
9255
|
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:
|
|
9256
|
+
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join41(targetRoot, ".agentwheel", "cache") }))));
|
|
8324
9257
|
const artifacts = filterArtifactsBySelection(await driver.list(resolved), selectedArtifacts);
|
|
8325
9258
|
for (const artifact of artifacts) {
|
|
8326
9259
|
console.log(`${artifact.type} ${artifact.name} ${artifact.relativePath}`);
|
|
@@ -8330,7 +9263,7 @@ program.command("scan").description("scan a package source for validation findin
|
|
|
8330
9263
|
const targetRoot = normalizeTargetRoot(options.targetRoot);
|
|
8331
9264
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
8332
9265
|
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:
|
|
9266
|
+
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join41(targetRoot, ".agentwheel", "cache") }))));
|
|
8334
9267
|
const result = await driver.scan(resolved);
|
|
8335
9268
|
if (result.findings.length === 0) {
|
|
8336
9269
|
console.log("Scan ok: no findings");
|
|
@@ -8341,17 +9274,17 @@ program.command("scan").description("scan a package source for validation findin
|
|
|
8341
9274
|
}
|
|
8342
9275
|
if (!result.ok) process.exitCode = 1;
|
|
8343
9276
|
});
|
|
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) => {
|
|
9277
|
+
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
9278
|
await runInstallCommand(source, { ...options, dryRun: true }, { apply: false });
|
|
8346
9279
|
});
|
|
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) => {
|
|
9280
|
+
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
9281
|
await runInstallCommand(source, options, { apply: !options.dryRun });
|
|
8349
9282
|
});
|
|
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) => {
|
|
9283
|
+
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
9284
|
console.error("warning: 'agentwheel sync' is deprecated and will be removed in 0.10. Use 'agentwheel install'.");
|
|
8352
9285
|
await runInstallCommand(source, options, { apply: !options.dryRun });
|
|
8353
9286
|
});
|
|
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) => {
|
|
9287
|
+
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
9288
|
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
8356
9289
|
const targets = await resolveCliTargets(normalizedOptions, { preferAllProfile: true });
|
|
8357
9290
|
for (const target of targets) {
|
|
@@ -8359,7 +9292,7 @@ program.command("update").description("re-resolve tracking packages, then apply
|
|
|
8359
9292
|
}
|
|
8360
9293
|
});
|
|
8361
9294
|
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) => {
|
|
9295
|
+
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
9296
|
const normalizedOptions = normalizeRuntimeScopeOptions(options, { defaultUser: shouldDefaultUserInstall(source, options) });
|
|
8364
9297
|
const targets = await resolveCliTargets(normalizedOptions);
|
|
8365
9298
|
for (const target of targets) {
|
|
@@ -8372,7 +9305,7 @@ program.command("deps").description("inspect the OpenPack dependency graph").add
|
|
|
8372
9305
|
for (const decision of result.bundle.graphLock.canonical.overrides) {
|
|
8373
9306
|
console.log(`OVERRIDE ${decision.graphNodeId}:${decision.type}/${decision.name} replaces ${decision.overriddenGraphNodeId}:${decision.type}/${decision.name} via ${decision.rootId} (${decision.selector})`);
|
|
8374
9307
|
}
|
|
8375
|
-
await
|
|
9308
|
+
await rm11(result.bundle.root, { recursive: true, force: true });
|
|
8376
9309
|
}
|
|
8377
9310
|
continue;
|
|
8378
9311
|
}
|
|
@@ -8409,6 +9342,29 @@ program.command("registry").description("manage optional registry indexes").addC
|
|
|
8409
9342
|
const client = new RegistryClient({ workspaceRoot: normalizeTargetRoot(options.targetRoot), warn: (message) => console.warn(message) });
|
|
8410
9343
|
printRegistryEntries(await client.search(query));
|
|
8411
9344
|
})
|
|
9345
|
+
).addCommand(
|
|
9346
|
+
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) => {
|
|
9347
|
+
const draft = createRegistryPublishDraft(source, {
|
|
9348
|
+
name: options.name,
|
|
9349
|
+
type: options.type,
|
|
9350
|
+
description: options.description,
|
|
9351
|
+
tags: options.tag,
|
|
9352
|
+
select: options.select,
|
|
9353
|
+
skills: options.skill
|
|
9354
|
+
});
|
|
9355
|
+
if (options.json) {
|
|
9356
|
+
console.log(JSON.stringify(draft.entry, null, 2));
|
|
9357
|
+
return;
|
|
9358
|
+
}
|
|
9359
|
+
console.log("Draft registry entry:");
|
|
9360
|
+
console.log(JSON.stringify(draft.entry, null, 2));
|
|
9361
|
+
console.log("");
|
|
9362
|
+
console.log(`Verify: ${draft.installCommand}`);
|
|
9363
|
+
if (!draft.entry.description) console.log('Tip: add --description "..." or fill the description before submitting.');
|
|
9364
|
+
console.log("");
|
|
9365
|
+
console.log("Submit:");
|
|
9366
|
+
console.log(draft.issueUrl);
|
|
9367
|
+
})
|
|
8412
9368
|
);
|
|
8413
9369
|
program.command("trust").description("manage persisted source trust decisions").addCommand(
|
|
8414
9370
|
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 +9449,7 @@ program.command("status").description("show configured packages and runtime inst
|
|
|
8493
9449
|
await printStatus(target, normalizedOptions);
|
|
8494
9450
|
}
|
|
8495
9451
|
});
|
|
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) => {
|
|
9452
|
+
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
9453
|
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
8498
9454
|
const target = await resolveRuntimeTarget({
|
|
8499
9455
|
targetRoot: normalizedOptions.targetRoot,
|
|
@@ -8527,6 +9483,8 @@ async function runInstallCommand(nameOrSource, options, behavior) {
|
|
|
8527
9483
|
forceConflict: normalizedOptions.forceConflict,
|
|
8528
9484
|
replaceConflict: normalizedOptions.replaceConflict,
|
|
8529
9485
|
noDeps: noDepsFromOptions(normalizedOptions),
|
|
9486
|
+
includeSuggestions: normalizedOptions.withSuggestions,
|
|
9487
|
+
suggestionAliases: suggestionAliasesFromOptions(normalizedOptions),
|
|
8530
9488
|
lockedResolution: true,
|
|
8531
9489
|
frozenLock: normalizedOptions.frozenLock,
|
|
8532
9490
|
offline: normalizedOptions.offline,
|
|
@@ -8576,7 +9534,7 @@ async function runInstallCommand(nameOrSource, options, behavior) {
|
|
|
8576
9534
|
});
|
|
8577
9535
|
console.log(`Applied ${result.plan.adapter} at ${result.plan.targetRoot}.`);
|
|
8578
9536
|
}
|
|
8579
|
-
await
|
|
9537
|
+
await rm11(result.bundle.root, { recursive: true, force: true });
|
|
8580
9538
|
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
8581
9539
|
}
|
|
8582
9540
|
if (behavior.apply && extraPackage && !targetOptions.onlySource) {
|
|
@@ -8585,10 +9543,10 @@ async function runInstallCommand(nameOrSource, options, behavior) {
|
|
|
8585
9543
|
}
|
|
8586
9544
|
}
|
|
8587
9545
|
async function packageEntryFromSource(source, targetRoot, options) {
|
|
8588
|
-
const selectedArtifacts = selectedArtifactsFromOptions(options);
|
|
8589
9546
|
const lockMode = options.frozenLock === true || options.offline === true;
|
|
8590
9547
|
const resolvedInput = await resolvePackageSource(source, targetRoot, { offline: lockMode });
|
|
8591
9548
|
const resolvedSource = resolvedInput.source;
|
|
9549
|
+
const selectedArtifacts = selectedArtifactsFromOptionsOrRegistry(options, resolvedInput.registryEntry);
|
|
8592
9550
|
const driverName = options.driver ?? inferSourceDriverName(resolvedSource);
|
|
8593
9551
|
const driver = getSourceDriver(driverName);
|
|
8594
9552
|
const adapter = await resolveAdapter({
|
|
@@ -8602,7 +9560,7 @@ async function packageEntryFromSource(source, targetRoot, options) {
|
|
|
8602
9560
|
const bundle = await stageSource(driver, resolvedSource, {
|
|
8603
9561
|
workspaceRoot: targetRoot,
|
|
8604
9562
|
adapter,
|
|
8605
|
-
cacheRoot:
|
|
9563
|
+
cacheRoot: join41(targetRoot, ".agentwheel", "cache"),
|
|
8606
9564
|
mode: options.mode,
|
|
8607
9565
|
frozenLock: lockMode,
|
|
8608
9566
|
select: selectedArtifacts
|
|
@@ -8621,10 +9579,12 @@ async function packageEntryFromSource(source, targetRoot, options) {
|
|
|
8621
9579
|
mode: options.mode ?? "pinned",
|
|
8622
9580
|
requestedRef: bundle.source.requestedRef,
|
|
8623
9581
|
select: selectedArtifacts,
|
|
9582
|
+
withSuggestions: options.withSuggestions === true ? true : void 0,
|
|
9583
|
+
suggestions: suggestionAliasesFromOptions(options),
|
|
8624
9584
|
overrides: overrideArtifactsFromOptions(options)
|
|
8625
9585
|
};
|
|
8626
9586
|
} finally {
|
|
8627
|
-
await
|
|
9587
|
+
await rm11(bundle.root, { recursive: true, force: true });
|
|
8628
9588
|
}
|
|
8629
9589
|
}
|
|
8630
9590
|
function findConfiguredPackage(packages, value) {
|
|
@@ -8765,7 +9725,7 @@ async function runConfiguredGraphPackages(target, options, behavior) {
|
|
|
8765
9725
|
});
|
|
8766
9726
|
console.log(`Applied ${result.plan.adapter} at ${result.plan.targetRoot}.`);
|
|
8767
9727
|
}
|
|
8768
|
-
await
|
|
9728
|
+
await rm11(result.bundle.root, { recursive: true, force: true });
|
|
8769
9729
|
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
8770
9730
|
}
|
|
8771
9731
|
}
|
|
@@ -8830,6 +9790,8 @@ async function buildGraphPlansForTarget(target, source, options, behavior) {
|
|
|
8830
9790
|
select: selectedArtifacts && packageIsScoped ? selectedArtifacts : normalizeArtifactSelectors(pkg.select, pkg.skills),
|
|
8831
9791
|
aliases: pkg.aliases,
|
|
8832
9792
|
overrides: pkg.overrides,
|
|
9793
|
+
includeSuggestions: targetOptions.withSuggestions === true || pkg.withSuggestions === true,
|
|
9794
|
+
suggestionAliases: packageSuggestionAliases(pkg, targetOptions),
|
|
8833
9795
|
useLock: behavior.mode === "install" ? true : !updateThisPackage
|
|
8834
9796
|
};
|
|
8835
9797
|
}),
|
|
@@ -8854,6 +9816,8 @@ async function buildGraphPlansForTarget(target, source, options, behavior) {
|
|
|
8854
9816
|
targetFingerprintParts: targetFingerprintParts(group.target, adapter, group.adapterOptions, group.installationType),
|
|
8855
9817
|
installationType: group.installationType,
|
|
8856
9818
|
noDeps: noDepsFromOptions(targetOptions),
|
|
9819
|
+
includeSuggestions: targetOptions.withSuggestions,
|
|
9820
|
+
suggestionAliases: suggestionAliasesFromOptions(targetOptions),
|
|
8857
9821
|
lockedResolution: behavior.mode === "install",
|
|
8858
9822
|
frozenLock: targetOptions.frozenLock,
|
|
8859
9823
|
offline: targetOptions.offline,
|
|
@@ -8954,7 +9918,7 @@ function keepManifestEntryOperation(entry, targetRoot, rootId, operation, option
|
|
|
8954
9918
|
artifactType: entry.artifactType,
|
|
8955
9919
|
artifactName: entry.artifactName,
|
|
8956
9920
|
kind: entry.kind,
|
|
8957
|
-
destPath: operation?.destPath ??
|
|
9921
|
+
destPath: operation?.destPath ?? join41(targetRoot, entry.path),
|
|
8958
9922
|
relativeDestPath: entry.path,
|
|
8959
9923
|
desiredHash: entry.sourceHash,
|
|
8960
9924
|
currentHash: operation?.currentHash ?? entry.hash,
|
|
@@ -9019,7 +9983,9 @@ async function uninstallConfiguredPackage(target, packageName, options) {
|
|
|
9019
9983
|
ref: pkg2.requestedRef,
|
|
9020
9984
|
select: normalizeArtifactSelectors(pkg2.select, pkg2.skills),
|
|
9021
9985
|
aliases: pkg2.aliases,
|
|
9022
|
-
overrides: pkg2.overrides
|
|
9986
|
+
overrides: pkg2.overrides,
|
|
9987
|
+
includeSuggestions: options.withSuggestions === true || pkg2.withSuggestions === true,
|
|
9988
|
+
suggestionAliases: packageSuggestionAliases(pkg2, options)
|
|
9023
9989
|
})),
|
|
9024
9990
|
targetRoot: remainingGroup.target.targetRoot,
|
|
9025
9991
|
workspaceRoot: remainingGroup.target.workspaceRoot,
|
|
@@ -9028,6 +9994,8 @@ async function uninstallConfiguredPackage(target, packageName, options) {
|
|
|
9028
9994
|
targetKey: targetKeyForTarget(remainingGroup.target, remainingAdapter.name),
|
|
9029
9995
|
targetFingerprintParts: targetFingerprintParts(remainingGroup.target, remainingAdapter, remainingGroup.adapterOptions, remainingGroup.installationType),
|
|
9030
9996
|
installationType: remainingGroup.installationType,
|
|
9997
|
+
includeSuggestions: options.withSuggestions,
|
|
9998
|
+
suggestionAliases: suggestionAliasesFromOptions(options),
|
|
9031
9999
|
lockedResolution: true,
|
|
9032
10000
|
frozenLock: options.frozenLock,
|
|
9033
10001
|
offline: options.offline,
|
|
@@ -9065,7 +10033,7 @@ async function uninstallConfiguredPackage(target, packageName, options) {
|
|
|
9065
10033
|
if (!options.dryRun) {
|
|
9066
10034
|
console.log(formatUninstallResult(result));
|
|
9067
10035
|
}
|
|
9068
|
-
if (renderedRoot) await
|
|
10036
|
+
if (renderedRoot) await rm11(renderedRoot, { recursive: true, force: true });
|
|
9069
10037
|
if (plan.hasBlockingChanges) process.exitCode = 1;
|
|
9070
10038
|
}
|
|
9071
10039
|
}
|
|
@@ -9193,7 +10161,7 @@ async function printPendingInstallWork(target, options) {
|
|
|
9193
10161
|
const message = error instanceof Error ? error.message : String(error);
|
|
9194
10162
|
console.log(`Pending install work: unavailable (${message})`);
|
|
9195
10163
|
} finally {
|
|
9196
|
-
await Promise.all(results.map((result) =>
|
|
10164
|
+
await Promise.all(results.map((result) => rm11(result.bundle.root, { recursive: true, force: true })));
|
|
9197
10165
|
}
|
|
9198
10166
|
}
|
|
9199
10167
|
async function printDoctor(target, options) {
|
|
@@ -9204,33 +10172,113 @@ async function printDoctor(target, options) {
|
|
|
9204
10172
|
if (!targetMapping?.enabled) {
|
|
9205
10173
|
throw new Error(`Adapter ${adapter.name} does not support skills for installation type '${installationType}'.`);
|
|
9206
10174
|
}
|
|
9207
|
-
const
|
|
9208
|
-
const
|
|
9209
|
-
const
|
|
9210
|
-
|
|
9211
|
-
|
|
9212
|
-
|
|
10175
|
+
const transport = transportForTarget(target);
|
|
10176
|
+
const state = installStateForTarget(target, adapter, adapterOptions, installationType);
|
|
10177
|
+
const manifest = await readInstallManifest(state.installRoot, adapter.name, transport, state);
|
|
10178
|
+
const requestedSkills = doctorSkillRequests(target, options);
|
|
10179
|
+
const skills = [];
|
|
10180
|
+
for (const request of requestedSkills) {
|
|
10181
|
+
const skillPath = join41(state.installRoot, targetMapping.dest, request.name);
|
|
10182
|
+
const exists = await pathExists(skillPath);
|
|
10183
|
+
const manifestEntry = manifest?.entries.find((entry) => {
|
|
10184
|
+
if (entry.artifactType !== "skills") return false;
|
|
10185
|
+
const legacyInstallName = "installName" in entry && typeof entry.installName === "string" ? entry.installName : void 0;
|
|
10186
|
+
return entry.artifactName === request.name || legacyInstallName === request.name || entry.path === join41(targetMapping.dest, request.name);
|
|
10187
|
+
});
|
|
10188
|
+
const status = manifestEntry ? "managed" : exists ? "present-unmanaged" : "missing";
|
|
10189
|
+
skills.push({
|
|
10190
|
+
name: request.name,
|
|
10191
|
+
source: request.source,
|
|
10192
|
+
label: request.label,
|
|
10193
|
+
status,
|
|
10194
|
+
path: skillPath,
|
|
10195
|
+
managed: Boolean(manifestEntry),
|
|
10196
|
+
present: exists,
|
|
10197
|
+
suggestedCommands: status === "missing" ? {
|
|
10198
|
+
dryRun: skillInstallCommand(adapter.name, installationType, options, request, { dryRun: true }),
|
|
10199
|
+
apply: skillInstallCommand(adapter.name, installationType, options, request)
|
|
10200
|
+
} : void 0
|
|
10201
|
+
});
|
|
10202
|
+
}
|
|
10203
|
+
const report = {
|
|
10204
|
+
adapter: adapter.name,
|
|
10205
|
+
installationType,
|
|
10206
|
+
targetRoot: target.targetRoot,
|
|
10207
|
+
installRoot: state.installRoot,
|
|
10208
|
+
manifest: manifest ? { entries: manifest.entries.length, revision: manifest.revision } : null,
|
|
10209
|
+
skills
|
|
10210
|
+
};
|
|
10211
|
+
if (options.json) {
|
|
10212
|
+
console.log(JSON.stringify(report, null, 2));
|
|
9213
10213
|
return;
|
|
9214
10214
|
}
|
|
9215
|
-
console.log(`
|
|
9216
|
-
|
|
9217
|
-
|
|
9218
|
-
|
|
10215
|
+
console.log(`Doctor for ${adapter.name}/${installationType} at ${state.installRoot}`);
|
|
10216
|
+
for (const skill of skills) {
|
|
10217
|
+
const statusLabel = skill.status === "managed" ? "installed" : skill.status === "present-unmanaged" ? "installed (unmanaged)" : "missing";
|
|
10218
|
+
console.log(`${skill.label}: ${statusLabel} at ${skill.path}`);
|
|
10219
|
+
}
|
|
10220
|
+
const missing = skills.filter((skill) => skill.status === "missing");
|
|
10221
|
+
if (missing.length > 0) {
|
|
10222
|
+
console.log("Suggested commands:");
|
|
10223
|
+
for (const skill of missing) {
|
|
10224
|
+
console.log(` ${skill.suggestedCommands?.dryRun}`);
|
|
10225
|
+
console.log(` ${skill.suggestedCommands?.apply}`);
|
|
10226
|
+
}
|
|
10227
|
+
}
|
|
10228
|
+
}
|
|
10229
|
+
function doctorSkillRequests(target, options) {
|
|
10230
|
+
const explicitSkills = normalizeDoctorSkillNames(options.skill ?? []);
|
|
10231
|
+
if (explicitSkills.length > 0) {
|
|
10232
|
+
return explicitSkills.map((name) => ({
|
|
10233
|
+
name,
|
|
10234
|
+
source: options.source ?? defaultSourceForSkill(name),
|
|
10235
|
+
label: doctorSkillLabel(name)
|
|
10236
|
+
}));
|
|
10237
|
+
}
|
|
10238
|
+
const requests = [{
|
|
10239
|
+
name: COMPANION_SKILL_NAME,
|
|
10240
|
+
source: COMPANION_SKILL_SOURCE,
|
|
10241
|
+
label: "Agentwheel companion skill"
|
|
10242
|
+
}];
|
|
10243
|
+
if (isSyncwheelWorkspace(target.targetRoot)) {
|
|
10244
|
+
requests.push({
|
|
10245
|
+
name: "syncwheel",
|
|
10246
|
+
source: "github:NestDevLab/syncwheel",
|
|
10247
|
+
label: "Syncwheel skill"
|
|
10248
|
+
});
|
|
10249
|
+
}
|
|
10250
|
+
return requests;
|
|
9219
10251
|
}
|
|
9220
|
-
function
|
|
10252
|
+
function normalizeDoctorSkillNames(skills) {
|
|
10253
|
+
return [...new Set(skills.flatMap(splitSelectorList).map((item) => item.trim()).filter(Boolean))];
|
|
10254
|
+
}
|
|
10255
|
+
function defaultSourceForSkill(name) {
|
|
10256
|
+
if (name === COMPANION_SKILL_NAME) return COMPANION_SKILL_SOURCE;
|
|
10257
|
+
if (name === "syncwheel") return "github:NestDevLab/syncwheel";
|
|
10258
|
+
return `github:NestDevLab/${name}`;
|
|
10259
|
+
}
|
|
10260
|
+
function doctorSkillLabel(name) {
|
|
10261
|
+
if (name === COMPANION_SKILL_NAME) return "Agentwheel companion skill";
|
|
10262
|
+
if (name === "syncwheel") return "Syncwheel skill";
|
|
10263
|
+
return `${name} skill`;
|
|
10264
|
+
}
|
|
10265
|
+
function isSyncwheelWorkspace(targetRoot) {
|
|
10266
|
+
return existsSync(join41(targetRoot, ".syncwheel", "manifest.json"));
|
|
10267
|
+
}
|
|
10268
|
+
function skillInstallCommand(adapter, installationType, options, skill, behavior = {}) {
|
|
9221
10269
|
const args = [
|
|
9222
10270
|
"agentwheel",
|
|
9223
10271
|
"install",
|
|
9224
|
-
|
|
10272
|
+
skill.source,
|
|
9225
10273
|
"--adapter",
|
|
9226
10274
|
adapter,
|
|
9227
10275
|
...installationTypeCommandArgs(installationType),
|
|
9228
10276
|
...targetRootCommandArgs(options),
|
|
9229
10277
|
"--skill",
|
|
9230
|
-
|
|
10278
|
+
skill.name
|
|
9231
10279
|
];
|
|
9232
10280
|
if (behavior.dryRun) args.push("--dry-run");
|
|
9233
|
-
return args.map(
|
|
10281
|
+
return args.map(shellQuoteArg2).join(" ");
|
|
9234
10282
|
}
|
|
9235
10283
|
function installationTypeCommandArgs(installationType) {
|
|
9236
10284
|
if (installationType === "user") return ["--user"];
|
|
@@ -9240,7 +10288,7 @@ function installationTypeCommandArgs(installationType) {
|
|
|
9240
10288
|
function targetRootCommandArgs(options) {
|
|
9241
10289
|
return options.targetRoot && options.installationType !== "user" ? ["--target-root", options.targetRoot] : [];
|
|
9242
10290
|
}
|
|
9243
|
-
function
|
|
10291
|
+
function shellQuoteArg2(value) {
|
|
9244
10292
|
if (/^[A-Za-z0-9_./:@=-]+$/.test(value)) return value;
|
|
9245
10293
|
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
9246
10294
|
}
|
|
@@ -9250,12 +10298,18 @@ function collectSelectOption(value, previous) {
|
|
|
9250
10298
|
function collectSkillOption(value, previous) {
|
|
9251
10299
|
return [...previous, ...splitSelectorList(value)];
|
|
9252
10300
|
}
|
|
10301
|
+
function collectSuggestionOption(value, previous) {
|
|
10302
|
+
return [...previous, ...splitSelectorList(value)];
|
|
10303
|
+
}
|
|
9253
10304
|
function collectTrustOption(value, previous) {
|
|
9254
10305
|
return [...previous, value];
|
|
9255
10306
|
}
|
|
9256
10307
|
function collectOverrideOption(value, previous) {
|
|
9257
10308
|
return [...previous, ...splitSelectorList(value)];
|
|
9258
10309
|
}
|
|
10310
|
+
function collectTagOption(value, previous) {
|
|
10311
|
+
return [...previous, ...splitSelectorList(value)];
|
|
10312
|
+
}
|
|
9259
10313
|
function normalizeRuntimeScopeOptions(options, behavior = {}) {
|
|
9260
10314
|
if (options.user && options.local) {
|
|
9261
10315
|
throw new Error("Choose either --user or --local.");
|
|
@@ -9292,11 +10346,11 @@ function looksLikeSourceSpecifier(value) {
|
|
|
9292
10346
|
}
|
|
9293
10347
|
function normalizeCliPath(value) {
|
|
9294
10348
|
if (value === "~") return homedir9();
|
|
9295
|
-
if (value.startsWith("~/")) return
|
|
9296
|
-
return
|
|
10349
|
+
if (value.startsWith("~/")) return resolve20(homedir9(), value.slice(2));
|
|
10350
|
+
return resolve20(value);
|
|
9297
10351
|
}
|
|
9298
10352
|
function isHomePath(path) {
|
|
9299
|
-
return
|
|
10353
|
+
return resolve20(path) === resolve20(homedir9());
|
|
9300
10354
|
}
|
|
9301
10355
|
function adapterListFromOption(adapter) {
|
|
9302
10356
|
if (!adapter) return [];
|
|
@@ -9310,6 +10364,18 @@ function adapterListFromOption(adapter) {
|
|
|
9310
10364
|
function selectedArtifactsFromOptions(options) {
|
|
9311
10365
|
return normalizeArtifactSelectors(options.select, options.skills ?? options.skill);
|
|
9312
10366
|
}
|
|
10367
|
+
function selectedArtifactsFromOptionsOrRegistry(options, registryEntry) {
|
|
10368
|
+
return selectedArtifactsFromOptions(options) ?? selectorsFromRegistryEntry(registryEntry);
|
|
10369
|
+
}
|
|
10370
|
+
function suggestionAliasesFromOptions(options) {
|
|
10371
|
+
const values = options.suggestions ?? options.suggestion;
|
|
10372
|
+
if (!values?.length) return void 0;
|
|
10373
|
+
return [...new Set(values.flatMap(splitSelectorList).map((item) => item.trim()).filter(Boolean))].sort((a, b) => a.localeCompare(b));
|
|
10374
|
+
}
|
|
10375
|
+
function packageSuggestionAliases(pkg, options) {
|
|
10376
|
+
const aliases = [...pkg.suggestions ?? [], ...suggestionAliasesFromOptions(options) ?? []].map((item) => item.trim()).filter(Boolean);
|
|
10377
|
+
return aliases.length > 0 ? [...new Set(aliases)].sort((a, b) => a.localeCompare(b)) : void 0;
|
|
10378
|
+
}
|
|
9313
10379
|
function overrideArtifactsFromOptions(options) {
|
|
9314
10380
|
const values = options.overrides ?? options.override;
|
|
9315
10381
|
return values && values.length > 0 ? values : void 0;
|
|
@@ -9339,10 +10405,10 @@ function filterUninstallPlanBySelection(plan, selected) {
|
|
|
9339
10405
|
};
|
|
9340
10406
|
}
|
|
9341
10407
|
async function initPackage(root) {
|
|
9342
|
-
await
|
|
9343
|
-
await
|
|
9344
|
-
await
|
|
9345
|
-
const manifestPath =
|
|
10408
|
+
await mkdir21(join41(root, "instructions"), { recursive: true });
|
|
10409
|
+
await mkdir21(join41(root, "rules"), { recursive: true });
|
|
10410
|
+
await mkdir21(join41(root, "skills"), { recursive: true });
|
|
10411
|
+
const manifestPath = join41(root, "openpack.json");
|
|
9346
10412
|
const manifest = {
|
|
9347
10413
|
schemaVersion: 2,
|
|
9348
10414
|
name: "example/agentwheel-package",
|
|
@@ -9353,12 +10419,12 @@ async function initPackage(root) {
|
|
|
9353
10419
|
{ type: "skills", path: "skills" }
|
|
9354
10420
|
]
|
|
9355
10421
|
};
|
|
9356
|
-
await
|
|
10422
|
+
await writeFile20(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
9357
10423
|
`, "utf8");
|
|
9358
|
-
await
|
|
10424
|
+
await writeFile20(join41(root, "instructions", "AGENTS.md"), "# Agent Instructions\n", "utf8");
|
|
9359
10425
|
}
|
|
9360
10426
|
async function defaultBootstrapPackage(_root) {
|
|
9361
|
-
const packageRoot = await findAgentwheelPackageRoot(
|
|
10427
|
+
const packageRoot = await findAgentwheelPackageRoot(dirname30(fileURLToPath3(import.meta.url)));
|
|
9362
10428
|
if (!packageRoot) return void 0;
|
|
9363
10429
|
return {
|
|
9364
10430
|
name: "agentwheel",
|
|
@@ -9402,10 +10468,10 @@ function withFleetExample(config) {
|
|
|
9402
10468
|
};
|
|
9403
10469
|
}
|
|
9404
10470
|
async function findAgentwheelPackageRoot(start) {
|
|
9405
|
-
let current =
|
|
10471
|
+
let current = resolve20(start);
|
|
9406
10472
|
while (true) {
|
|
9407
10473
|
if (await findPackageManifestPath(current, { warnLegacy: false })) return current;
|
|
9408
|
-
const parent =
|
|
10474
|
+
const parent = dirname30(current);
|
|
9409
10475
|
if (parent === current) return void 0;
|
|
9410
10476
|
current = parent;
|
|
9411
10477
|
}
|